This is an automated email from the git hooks/post-receive script. New commit to branch feature/pollen-riot-js in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit c67bcb21491815883004fbf61f9bfa29439263bc Author: Tony CHEMIT <dev@tchemit.fr> Date: Thu Jan 26 18:45:02 2017 +0100 gestion des choix en mode edition --- pollen-persistence/src/main/xmi/pollen.zargo | Bin 21037 -> 21259 bytes .../org/chorem/pollen/services/bean/PollBean.java | 37 ++- .../pollen/services/service/ChoiceService.java | 5 + .../pollen/services/service/PollService.java | 4 +- .../i18n/pollen-services_en_GB.properties | 1 + .../i18n/pollen-services_fr_FR.properties | 1 + pollen-ui-riot-js/src/main/web/i18n.json | 22 +- pollen-ui-riot-js/src/main/web/js/ChoiceService.js | 8 +- pollen-ui-riot-js/src/main/web/js/ChoiceText.js | 3 +- pollen-ui-riot-js/src/main/web/js/PollForm.js | 55 ++++- pollen-ui-riot-js/src/main/web/js/PollService.js | 24 +- pollen-ui-riot-js/src/main/web/tag/Pollen.tag | 52 ++-- .../src/main/web/tag/poll/CreatePoll.tag | 9 +- pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag | 33 ++- .../src/main/web/tag/poll/PollChoiceText.tag | 179 +++++++++++--- .../src/main/web/tag/poll/PollChoiceTextGroup.tag | 33 --- .../src/main/web/tag/poll/PollChoices.tag | 68 ++++++ .../src/main/web/tag/poll/PollChoicesText.tag | 262 ++++++++++++++++----- .../src/main/web/tag/poll/PollCreated.tag | 2 +- .../src/main/web/tag/poll/PollVotes.tag | 4 - pollen-ui-riot-js/src/main/web/tag/poll/Polls.tag | 47 +++- 21 files changed, 656 insertions(+), 193 deletions(-) diff --git a/pollen-persistence/src/main/xmi/pollen.zargo b/pollen-persistence/src/main/xmi/pollen.zargo index e3d4cad..60e8e66 100644 Binary files a/pollen-persistence/src/main/xmi/pollen.zargo and b/pollen-persistence/src/main/xmi/pollen.zargo differ diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/bean/PollBean.java b/pollen-services/src/main/java/org/chorem/pollen/services/bean/PollBean.java index fe430a8..4d73aaf 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/bean/PollBean.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/bean/PollBean.java @@ -22,6 +22,7 @@ package org.chorem.pollen.services.bean; */ import com.google.common.base.Joiner; +import org.chorem.pollen.persistence.entity.ChoiceType; import org.chorem.pollen.persistence.entity.CommentVisibility; import org.chorem.pollen.persistence.entity.Poll; import org.chorem.pollen.persistence.entity.PollImpl; @@ -52,8 +53,6 @@ public class PollBean extends PollenBean<Poll> { CLOSED } - ; - public PollBean() { super(Poll.class); } @@ -80,6 +79,8 @@ public class PollBean extends PollenBean<Poll> { protected String description; + protected Date createDate; + protected Date beginChoiceDate; protected Date endChoiceDate; @@ -122,8 +123,12 @@ public class PollBean extends PollenBean<Poll> { protected long voteCount; + protected long choiceCount; + protected PollStatus status; + protected ChoiceType choiceType; + @Override public void fromEntity(Poll entity) { @@ -148,6 +153,7 @@ public class PollBean extends PollenBean<Poll> { setTitle(entity.getTitle()); setDescription(entity.getDescription()); + setCreateDate(entity.getTopiaCreateDate()); setBeginChoiceDate(entity.getBeginChoiceDate()); setEndChoiceDate(entity.getEndChoiceDate()); setBeginDate(entity.getBeginDate()); @@ -163,6 +169,7 @@ public class PollBean extends PollenBean<Poll> { setResultVisibility(entity.getResultVisibility()); setClosed(entity.isClosed()); setParticipants(new LinkedHashSet<>(entity.getParticipants() == null ? Collections.emptyList() : Arrays.asList(entity.getParticipants().split("\\s")))); + setChoiceType(entity.getChoiceType()); if (entity.isClosed()) { setStatus(PollStatus.CLOSED); @@ -209,7 +216,7 @@ public class PollBean extends PollenBean<Poll> { entity.setResultVisibility(getResultVisibility()); entity.setClosed(isClosed()); entity.setParticipants(Joiner.on(' ').join(getParticipants())); - + entity.setChoiceType(getChoiceType()); return entity; } @@ -429,4 +436,28 @@ public class PollBean extends PollenBean<Poll> { public void setStatus(PollStatus status) { this.status = status; } + + public Date getCreateDate() { + return createDate; + } + + public void setCreateDate(Date createDate) { + this.createDate = createDate; + } + + public ChoiceType getChoiceType() { + return choiceType; + } + + public void setChoiceType(ChoiceType choiceType) { + this.choiceType = choiceType; + } + + public long getChoiceCount() { + return choiceCount; + } + + public void setChoiceCount(long choiceCount) { + this.choiceCount = choiceCount; + } } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/ChoiceService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/ChoiceService.java index 6acf79b..7871b96 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/ChoiceService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/ChoiceService.java @@ -360,4 +360,9 @@ public class ChoiceService extends PollenServiceSupport { } + public long getChoiceCount(String pollId) { + checkNotNull(pollId); + Poll poll = getPollService().getPoll0(pollId); + return getChoiceDao().forPollEquals(poll).count(); + } } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollService.java index e8f58cd..225a3c7 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollService.java @@ -83,6 +83,8 @@ public class PollService extends PollenServiceSupport { input.setCommentCount(commentCount); long voteVount = getVoteService().getVoteCount(input.getEntityId()); input.setVoteCount(voteVount); + long choiceCount = getChoiceService().getChoiceCount(input.getEntityId()); + input.setChoiceCount(choiceCount); return input; }; @@ -311,7 +313,6 @@ public class PollService extends PollenServiceSupport { } - public File exportPoll(String pollId) { checkNotNull(pollId); @@ -428,6 +429,7 @@ public class PollService extends PollenServiceSupport { ErrorMap errors = new ErrorMap(); + checkNotNull(errors, Poll.PROPERTY_CHOICE_TYPE, poll.getChoiceType(), l(getLocale(), "pollen.error.poll.choiceType.mandatory")); checkNotNull(errors, Poll.PROPERTY_POLL_TYPE, poll.getPollType(), l(getLocale(), "pollen.error.poll.pollType.mandatory")); checkNotNull(errors, Poll.PROPERTY_COMMENT_VISIBILITY, poll.getCommentVisibility(), l(getLocale(), "pollen.error.poll.commentVisibility.mandatory")); checkNotNull(errors, Poll.PROPERTY_VOTE_VISIBILITY, poll.getVoteVisibility(), l(getLocale(), "pollen.error.poll.voteVisibility.mandatory")); diff --git a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties index 05c2853..406d376 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_en_GB.properties @@ -50,6 +50,7 @@ pollen.error.favoriteListMember.name.already.used=member name already used pollen.error.favoriteListMember.name.empty=member name can not be empty pollen.error.poll.beginChoiceDate.afterEndDate=the begin date of adding choice is after the end of poll pollen.error.poll.choice.mandatory=At least a choice is mandatory +pollen.error.poll.choiceType.mandatory= pollen.error.poll.commentVisibility.mandatory=comment visiblity is mandatory pollen.error.poll.creator.email.invalid=email is not valid pollen.error.poll.endChoiceDate.beforeBeginChoiceDate=The end date of adding choice is before the begin date diff --git a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties index ebedd6e..3d49724 100644 --- a/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties +++ b/pollen-services/src/main/resources/i18n/pollen-services_fr_FR.properties @@ -50,6 +50,7 @@ pollen.error.favoriteListMember.name.already.used=Le nom du membre est déjà ut pollen.error.favoriteListMember.name.empty=Le nom du membre est obligatoire pollen.error.poll.beginChoiceDate.afterEndDate=La date de début d'ajout de choix est après la date de fin du sondage pollen.error.poll.choice.mandatory=Au moins un choix est nécessaire +pollen.error.poll.choiceType.mandatory= pollen.error.poll.commentVisibility.mandatory=la visibilité des commentaires est obligatoire pollen.error.poll.creator.email.invalid=Courriel est invalide pollen.error.poll.endChoiceDate.beforeBeginChoiceDate=La date de fin d'ajout de choix est avant la date de début diff --git a/pollen-ui-riot-js/src/main/web/i18n.json b/pollen-ui-riot-js/src/main/web/i18n.json index 5837dc6..48e1b07 100644 --- a/pollen-ui-riot-js/src/main/web/i18n.json +++ b/pollen-ui-riot-js/src/main/web/i18n.json @@ -2,13 +2,11 @@ "fr": { "pagination_page": "Page", "pagination_resultsPerPage": "Résultats par page", - "polls_CLOSED": "Fermé", - "polls_ADDING_CHOICES": "Ajout de choix en cours", - "polls_VOTING": "Vote en cours", "poll_closePoll": "Clôturer le sondage", "poll_reopenPoll": "Réouvrir le sondage", "poll_deletePoll": "Supprimer le sondage", "poll_votes": "Votes", + "poll_choices": "Choix", "poll_results": "Résultats", "poll_results_title": "Résultats", "poll_results_noResult": "Les résultats ne sont pas encore disponibles.", @@ -35,7 +33,12 @@ "polls_polls": "Tous les sondages", "polls_name": "Nom", "polls_createDate": "Date de création", + "polls_choiceDates": "Ajout de choix", + "polls_voteDate": "Vote", "polls_status": "Statut", + "polls_CLOSED": "Fermé", + "polls_ADDING_CHOICES": "Ajout de choix en cours", + "polls_VOTING": "Vote en cours", "signup_title": "Créer un compte", "signup_email": "Email", "signup_email_placeholder": "Entrer l'email", @@ -152,7 +155,9 @@ "poll_choices_description": "Description", "poll_choices_previous": "Précédent", "poll_choices_next": "Suivant", + "poll_choices_save": "Enregister", "poll_choices_moreChoices": "Ajouter des choix", + "poll_choices_moreChoice": "Ajouter un choix", "poll_header_general": "Description", "poll_header_choices": "Choix", "poll_header_options": "Options", @@ -172,9 +177,7 @@ "en": { "pagination_page": "Page", "pagination_resultsPerPage": "Results per page", - "polls_CLOSED": "Closed", - "polls_ADDING_CHOICES": "Adding choices", - "polls_VOTING": "Voting", + "poll_choices": "Choices", "poll_votes": "Votes", "poll_closePoll": "Close poll", "poll_reopenPoll": "Reopen poll", @@ -204,9 +207,14 @@ "polls_title": "My polls", "polls_name": "Name", "polls_createDate": "Created date", + "polls_choiceDates": "Add choices", + "polls_voteDates": "Vote", "polls_status": "Status", "polls_createdPolls": "My polls", "polls_polls": "All polls", + "polls_CLOSED": "Closed", + "polls_ADDING_CHOICES": "Adding choices", + "polls_VOTING": "Voting", "signup_title": "Create an account", "signup_email": "Email", "signup_email_placeholder": "Enter your email", @@ -323,7 +331,9 @@ "poll_choices_description": "Description", "poll_choices_previous": "Previous", "poll_choices_next": "Next", + "poll_choices_save": "Save", "poll_choices_moreChoices": "Add more choices", + "poll_choices_moreChoice": "Add one choice", "poll_header_general": "General", "poll_header_choices": "Choices", "poll_header_options": "Options", diff --git a/pollen-ui-riot-js/src/main/web/js/ChoiceService.js b/pollen-ui-riot-js/src/main/web/js/ChoiceService.js index 210bad4..381c4d9 100644 --- a/pollen-ui-riot-js/src/main/web/js/ChoiceService.js +++ b/pollen-ui-riot-js/src/main/web/js/ChoiceService.js @@ -31,8 +31,12 @@ class ChoiceService extends FetchService { return this.getWithParams("/v1/polls/" + pollId + "/choices", args); } - addChoice(pollId, form) { - return this.form("/v1/polls/" + pollId + "/choices", {choice: form}); + addChoice(pollId, form, permission) { + let url = "/v1/polls/" + pollId + "/choices"; + if (permission) { + url += "?permission=" + permission; + } + return this.form(url, {choice: form}); } updateChoice(pollId, form, permission) { diff --git a/pollen-ui-riot-js/src/main/web/js/ChoiceText.js b/pollen-ui-riot-js/src/main/web/js/ChoiceText.js index a0b9683..c7c7645 100644 --- a/pollen-ui-riot-js/src/main/web/js/ChoiceText.js +++ b/pollen-ui-riot-js/src/main/web/js/ChoiceText.js @@ -20,10 +20,11 @@ */ class ChoiceText { - constructor(name, description) { + constructor(name, description, id) { this.choiceValue = name; this.description = description; this.choiceType = 'TEXT'; + this.id = id; } } 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 99d503b..0836a66 100644 --- a/pollen-ui-riot-js/src/main/web/js/PollForm.js +++ b/pollen-ui-riot-js/src/main/web/js/PollForm.js @@ -28,7 +28,8 @@ class PollForm { this.service = require('./PollService'); this.step = 0; this.isInit = false; - this.type = null; + this.choiceType = null; + this.mode = null; this.showOptions = false; this.model = null; this.choices = []; @@ -48,11 +49,12 @@ class PollForm { this.model.description = "Premier sondage!"; this.model.name = "Dick Laurent"; this.model.email = "user@pollen.org"; + this.mode = "create"; if (user) { this.model.name = user.name; this.model.email = user.email; } - this.model.voteCountingType= "1"; + this.model.voteCountingType = "1"; this.model.participant = []; this.choices = [ new ChoiceText("Mozart", "Requiem is so powerfull"), @@ -62,6 +64,19 @@ class PollForm { } create() { + switch (this.choiceType) { + case 'text': + this.model.choiceType = 'TEXT'; + break; + case 'date': + this.model.choiceType = 'DATE'; + break; + case 'image': + this.model.choiceType = 'RESOURCE'; + break; + } + console.info("form before create"); + console.info(this.form); return this.service.create(this.model, this.choices).then((result) => { console.info("Poll created"); console.info(result); @@ -99,23 +114,55 @@ class PollForm { if (e.name.indexOf("description") == 0) { map[e.name] = e.value; } + if (e.name.indexOf("id") == 0) { + map[e.name] = e.value; + } } }); for (let i = 0; i < count; i++) { let text = map['choice' + i]; let description = map['description' + i]; - choices.push(new ChoiceText(text, description)); + let id = map['id' + i]; + if (this.type!='add' || !id) { + choices.push(new ChoiceText(text, description, id)); + } } console.info("FromTextChoices"); console.info(choices); this.choices = choices; } + getTextChoice(form, index) { + let choices = []; + + let map = {}; + let count = 0; + Array.prototype.forEach.call(form.elements, (e) => { + if (e.name && e.value) { + if (e.name.indexOf("choice") == 0) { + map[e.name] = e.value; + count++; + } + if (e.name.indexOf("description") == 0) { + map[e.name] = e.value; + } + if (e.name.indexOf("id") == 0) { + map[e.name] = e.value; + } + } + }); + + let text = map['choice' + index]; + let description = map['description' + index]; + let id = map['id' + index]; + return new ChoiceText(text, description, id); + } + setStep(step) { console.info("setStep:: " + step); this.step = step; - route("poll/new/" + this.type + "/" + step, null, true); + route("poll/new/" + this.choiceType + "/" + step, null, true); } } diff --git a/pollen-ui-riot-js/src/main/web/js/PollService.js b/pollen-ui-riot-js/src/main/web/js/PollService.js index f2760b2..9bf692c 100644 --- a/pollen-ui-riot-js/src/main/web/js/PollService.js +++ b/pollen-ui-riot-js/src/main/web/js/PollService.js @@ -39,16 +39,28 @@ class PollService extends FetchService { return this.getWithParams("/v1/polls", {paginationParameter: pagination}); } - getPoll(pollId) { - return this.get("/v1/polls/" + pollId); + getPoll(pollId, permission) { + let url = "/v1/polls/" + pollId; + if (permission) { + url += "?permission=" + permission; + } + return this.get(url); } - closePoll(pollId) { - return this.put("/v1/polls/" + pollId + '/close'); + closePoll(pollId, permission) { + let url = "/v1/polls/" + pollId + '/close'; + if (permission) { + url += "?permission=" + permission; + } + return this.put(url); } - reopenPoll(pollId) { - return this.put("/v1/polls/" + pollId + '/reopen'); + reopenPoll(pollId, permission) { + let url = "/v1/polls/" + pollId + '/reopen'; + if (permission) { + url += "?permission=" + permission; + } + return this.put(url); } } diff --git a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag index 337ccd2..4153e0d 100644 --- a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag +++ b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag @@ -64,6 +64,29 @@ require("./poll/Polls.tag"); riot.mount(this.refs.content, "signcheck", {id: id, token: token}); }); + route("/poll/created", () => { + if (!session.isConnected()) { + route("/signin?url=/poll/created"); + } else { + riot.mount(this.refs.content, "polls", { method: "createdPolls", session: session}); + } + }); + + route("/poll", () => { + if (!session.isConnected()) { + route("/signin?url=/poll"); + } else { + + riot.mount(this.refs.content, "polls", {method: "polls", session: session}); + } + }); + + route("/poll/*/choice", (pollId) => { + riot.mount(this.refs.content, "poll", {pollId: pollId, tabName: 'choices'}); + }); + route("/poll/*/choice/*", (pollId, permission) => { + riot.mount(this.refs.content, "poll", {pollId: pollId, tabName: 'choices', permission: permission}); + }); route("/poll/*/vote", (pollId) => { riot.mount(this.refs.content, "poll", {pollId: pollId, tabName: 'votes'}); }); @@ -83,6 +106,14 @@ require("./poll/Polls.tag"); riot.mount(this.refs.content, "poll", {pollId: pollId, tabName: 'results', permission: permission}); }); + route("/poll/*", (pollId) => { + riot.mount(this.refs.content, "poll", {pollId: pollId}); + }); + + route("/poll/*/*", (pollId, permission) => { + riot.mount(this.refs.content, "poll", {pollId: pollId, permission: permission}); + }); + route("/user/profile", () => { riot.mount(this.refs.content, "userprofile"); }); @@ -90,25 +121,8 @@ require("./poll/Polls.tag"); riot.mount(this.refs.content, "userfavoritelists"); }); - route("/poll/created", () => { - if (!session.isConnected()) { - route("/signin?url=/poll/created"); - } else { - riot.mount(this.refs.content, "polls", { method: "createdPolls", session: session}); - } - }); - - route("/poll", () => { - if (!session.isConnected()) { - route("/signin?url=/poll"); - } else { - - riot.mount(this.refs.content, "polls", {method: "polls", session: session}); - } - }); - - route("/poll/new/*/*", (type, step) => { - riot.mount(this.refs.content, "createpoll", {type: type, step: step}); + route("/poll/new/*/*", (choiceType, step) => { + riot.mount(this.refs.content, "createpoll", {choiceType: choiceType, step: step}); }); route(() => { riot.mount(this.refs.content, "home"); diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/CreatePoll.tag b/pollen-ui-riot-js/src/main/web/tag/poll/CreatePoll.tag index 66b5643..512a272 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/CreatePoll.tag +++ b/pollen-ui-riot-js/src/main/web/tag/poll/CreatePoll.tag @@ -19,9 +19,7 @@ * #L% */ require("./PollDescription.tag"); -require("./PollChoicesText.tag"); -require("./PollChoicesImage.tag"); -require("./PollChoicesDate.tag"); +require("./PollChoices.tag"); require("./PollSettings.tag"); require("./PollVoters.tag"); require("./PollCreated.tag"); @@ -126,13 +124,12 @@ require("./PollCreated.tag"); <script> console.info("Create poll"); - this.type = opts.type; this.form = require("../../js/PollForm"); this.session = require("../../js/Session"); - this.form.type = this.type; + this.form.choiceType = opts.choiceType; this.steps = { 0: 'polldescription', - 1: 'pollchoices' + this.type, + 1: 'pollchoices' + this.form.choiceType, 2: 'pollsettings', 3: 'pollvoters', 4: 'pollcreated' diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag b/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag index ee5ff0c..43939d2 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Poll.tag @@ -1,6 +1,8 @@ require('./PollVotes.tag'); require('./PollComments.tag'); require('./PollResults.tag'); +require('./PollChoices.tag'); + <Poll> <div class="container"> @@ -10,6 +12,11 @@ require('./PollResults.tag'); </div> <div class="tab-header"> + <div class="{selectedTab=='choices'?'tab-selected':'tab-not-selected'}"> + <a href="#poll/{pollId}/choice{permission?'/' + permission : ''}"> + <i class="fa fa-thumbs-o-up"></i> {__.choices} ({choicesCount}) + </a> + </div> <div class="{selectedTab=='votes'?'tab-selected':'tab-not-selected'}"> <a href="#poll/{pollId}/vote{permission?'/' + permission : ''}"> <i class="fa fa-thumbs-o-up fa-flip-horizontal"></i> {__.votes} ({votesCount}) @@ -30,8 +37,10 @@ require('./PollResults.tag'); <div class="actions-dropdown"> <i class="fa fa-bars fa-2x mainColor"/> <div class="actions-dropdown-content"> - <a if="{!poll.isClosed}" onclick="{closePoll}"><i class="link fa fa-close fa-15x"/> {__.closePoll}</a> - <a if="{poll.isClosed}" onclick="{reopenPoll}"><i class="link fa fa-play fa-15x"/> {__.reopenPoll}</a> + <a if="{!poll.isClosed}" onclick="{closePoll}"><i class="link fa fa-close fa-15x"/> + {__.closePoll}</a> + <a if="{poll.isClosed}" onclick="{reopenPoll}"><i class="link fa fa-play fa-15x"/> + {__.reopenPoll}</a> <a onclick="{deletePoll}"><i class="link fa fa-trash fa-15x"/> {__.deletePoll}</a> </div> </div> @@ -53,13 +62,23 @@ require('./PollResults.tag'); let pollService = require('../../js/PollService'); this.on('mount', () => { - pollService.getPoll(this.pollId).then(result => { + pollService.getPoll(this.pollId, this.permission).then(result => { this.poll = result; this.commentsCount = this.poll.commentCount; this.votesCount = this.poll.voteCount; - this.poll.$canVote = this.poll.canVote; + this.choicesCount = this.poll.choiceCount; + this.poll.$canVote = this.poll.canVote; console.info("Poll::"); console.info(this.poll); + if (!this.selectedTab) { + if (this.poll.status == 'VOTING') { + this.selectedTab = 'votes'; + } else if (this.poll.status == 'ADDING_CHOICES') { + this.selectedTab = 'choices'; + } else if (this.poll.status == 'CLOSED') { + this.selectedTab = 'results'; + } + } this.refresh(); }); }); @@ -78,6 +97,7 @@ require('./PollResults.tag'); this[countName] = count; let args = {}; args[countName] = count; + console.info('update count on ' + countName + " = " + count); this.update(args); }); } @@ -86,14 +106,14 @@ require('./PollResults.tag'); }; this.closePoll = () => { - pollService.closePoll(this.pollId).then(() => { + pollService.closePoll(this.pollId, this.permission).then(() => { this.poll.isClosed = true; this.poll.canVote = false; this.refresh(); }); }; this.reopenPoll = () => { - pollService.reopenPoll(this.pollId).then(() => { + pollService.reopenPoll(this.pollId, this.permission).then(() => { this.poll.isClosed = false; this.poll.canVote = this.poll.$canVote; this.refresh(); @@ -106,6 +126,7 @@ require('./PollResults.tag'); .fa-15x { font-size: 1.5em; } + .block { cursor: not-allowed; } diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/PollChoiceText.tag b/pollen-ui-riot-js/src/main/web/tag/poll/PollChoiceText.tag index 18e08af..d168105 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/PollChoiceText.tag +++ b/pollen-ui-riot-js/src/main/web/tag/poll/PollChoiceText.tag @@ -1,60 +1,173 @@ /*- - * #%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% - */ +* #%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% +*/ <PollChoiceText> - <div class="container"> - <label class="wide" for="choice{number}">{__.label} {number + 1}</label> - <label if="{number == 0 }">{__.description}</label> - </div> - <div class="container"> - <input class="wide" type="text" ref="choice" name="choice{number}" value="{choice.choiceValue}"> - <input class="wider" type="text" ref="description" name="description{number}" value="{choice.description}"> + <div class="choice-body"> + + <div class="choice-actions"> + <div show="{mode =='edit' && !editing}" id="{choice.id}"> + <a onclick="{deleteChoice}"><i class="fa fa-trash danger fa-15x"/></a> + <a onclick="{onEditChoice}"><i class="fa fa-pencil-square-o fa-15x"/></a> + </div> + <div show="{editing}"> + <a onclick="{cancelEditChoice}" class="danger"> + <i class="fa fa-remove fa-15x"/> + </a> + <button class="icon" type="submit" onclick="{prepareSave}"> + <i class="fa fa-check fa-15x"/> + </button> + </div> + </div> + <div ref="edit_choice" id="edit_choice_{number}" class="choice-container choice-view"> + <div class="choice-container-child"> + <label class="choice-wide" for="choice{number}">{__.label} {number + 1}</label> + <label class="choice-wider" if="{number == 0}">{__.description}</label> + <label class="choice-wider" if="{number > 0}"/> + </div> + <div class="choice-container-child"> + <input type="hidden" name="id{number}" value="{choice.id}"> + <div class="choice-inputs"> + <input type="text" ref="choice" name="choice{number}" value="{choice.choiceValue}" + disabled="{edit && !editing && choice.choiceValue?'disabled':''}" + class="choice-wide {edit && !editing && choice.choiceValue?'choice-disabled':''}"> + <input type="text" ref="description" name="description{number}" value="{choice.description}" + disabled="{edit && !editing && choice.choiceValue?'disabled':''}" + class="choice-wider {edit && !editing && choice.choiceValue?'choice-disabled':''}"> + </div> + </div> + </div> </div> <script> this.installBundle(opts.session, "poll_choices"); - this.number = opts.number; - this.choice = opts.choices[this.number] || {name: '', description: ''}; + this.number = parseInt(opts.number); + this.mode = opts.mode; + this.edit = opts.mode == 'add' || opts.mode == 'edit'; + this.choice = opts.choice; this.on('mount', () => { - if (this.number == 0) { - this.refs.choice.required = "require"; + if (this.number == 0 || this.edit) { + this.refs.choice.required = "required"; } }); + this.prepareSave = () => { + this.$choice = { + choiceValue: this.refs.choice.value, + description: this.refs.description.value + }; + }; + this.onEditChoice = () => { + this.editing = true; + let ref = this.refs['edit_choice']; + ref.classList.add("choice-edit"); + ref.classList.remove("choice-view"); + this.trigger('editChoice', this.number); + Object.assign(this.$choice = {}, this.choice); + console.info(this.$choice); + }; + + this.cancelEditChoice = () => { + if (this.editing) { + let ref = this.refs['edit_choice']; + ref.classList.remove("choice-edit"); + ref.classList.add("choice-view"); + Object.assign(this.choice, this.$choice); + console.info(this.choice); + this.refs.choice.value = this.choice.choiceValue; + this.refs.description.value = this.choice.description; + + } + this.editing = false; + this.trigger('cancelEditChoice', this.number); + }; + + this.deleteChoice = () => { + if (confirm('Delete choice?')) { + this.trigger('deleteChoice', this.number); + } + }; + </script> <style> - .wide { + + .icon { + border-style: none; + background-color: white; + cursor: pointer; + color: #13a2ff; + } + + .danger { + color: red; + } + + .choice-wide { width: 440px; } - .wider { + .choice-wider { width: 640px; } - .container { + .choice-actions { + padding-right: 5px; + } + + .choice-body { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: center; + } + + .choice-container { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + } + + .choice-container-child { + display: flex; + } + + .choice-inputs { display: flex; flex-direction: row; justify-content: flex-start; align-items: center; } + + .choice-disabled { + cursor: not-allowed; + } + + .choice-edit { + border: 2px solid #13a2ff; + } + + .choice-view { + border: 2px solid white; + } + </style> </PollChoiceText> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/PollChoiceTextGroup.tag b/pollen-ui-riot-js/src/main/web/tag/poll/PollChoiceTextGroup.tag deleted file mode 100644 index 5e13f77..0000000 --- a/pollen-ui-riot-js/src/main/web/tag/poll/PollChoiceTextGroup.tag +++ /dev/null @@ -1,33 +0,0 @@ -/*- - * #%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% - */ -require('./PollChoiceText.tag'); -<PollChoiceTextGroup> - <PollChoiceText number="{start}" choices="{choices}" session="{session}"/> - <PollChoiceText number="{start + 1}" choices="{choices}" session="{session}"/> - <PollChoiceText number="{start + 2}" choices="{choices}" session="{session}"/> - <PollChoiceText number="{start + 3}" choices="{choices}" session="{session}"/> - <PollChoiceText number="{start + 4}" choices="{choices}" session="{session}"/> - <script> - this.choices = opts.choices; - this.start = parseInt(opts.start); - this.session = opts.session; - </script> -</PollChoiceTextGroup> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/PollChoices.tag b/pollen-ui-riot-js/src/main/web/tag/poll/PollChoices.tag new file mode 100644 index 0000000..51b0eae --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/poll/PollChoices.tag @@ -0,0 +1,68 @@ +require("./PollChoicesText.tag"); +require("./PollChoicesImage.tag"); +require("./PollChoicesDate.tag"); +<PollChoices> + <div ref="choicecontent"/> + <script> + if (opts.form) { + this.form = form; + } else { + this.form = require('../../js/PollForm'); + this.form.model = opts.poll; + this.form.choiceType = opts.poll.choiceType || 'text'; + this.form.mode = 'view'; + if (opts.poll.choiceAddAllowed && opts.poll.status == 'ADDING_CHOICES') { + + // can add choices + + if (opts.poll.permission) { + this.form.mode = 'edit'; + } else { + this.form.mode = 'add'; + } + } else { + + if (opts.poll.permission && opts.poll.voteCount == 0) { + this.form.mode = 'edit'; + } + } + + console.info("form mode: "+this.form.mode); + this.form.choices = []; + } + if (opts.choiceType) { + this.choiceType = opts.choiceType; + } else { + this.choiceType = this.form.choiceType || 'text'; + } + console.info(this.choiceType); + console.info(this.form); + + this.on('mount', () => { + + if (opts.poll) { + + let choiceService = require("../../js/ChoiceService"); + choiceService.getChoices(opts.poll.id).then(choices => { + this.form.choices = choices; + console.info("Choices::"); + console.info(this.form.choices); + + riot.mount(this.refs.choicecontent, 'pollchoices' + this.form.choiceType, { + form: this.form, + session: opts.session, + parentTag: this + }); + + }); + } else { + riot.mount(this.refs.choicecontent, 'pollchoices' + this.form.choiceType, { + form: this.form, + session: opts.session, + parentTag: this + }); + } + }); + + </script> +</PollChoices> \ No newline at end of file diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/PollChoicesText.tag b/pollen-ui-riot-js/src/main/web/tag/poll/PollChoicesText.tag index 5b0e017..2474ff8 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/PollChoicesText.tag +++ b/pollen-ui-riot-js/src/main/web/tag/poll/PollChoicesText.tag @@ -1,34 +1,39 @@ /*- - * #%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% - */ -require('./PollChoiceTextGroup.tag'); +* #%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% +*/ +require('./PollChoiceText.tag'); <PollChoicesText> - <form ref="choices" onsubmit="{nextStep}"> - <virtual each={item in counts}> - <PollChoiceTextGroup choices="{form.choices}" start="{item - 5}" session="{session}"/> - </virtual> + <div if="{form.type == 'add'}"> + Période d'ajout de choix du {form.model.beginChoiceDate} au {form.model.endChoiceDate} + </div> + + <form ref="formDom" onsubmit="{action}"> + <div ref="choicesContainer"/> <br/> - <div class="actions"> + <div if="{(form.mode == 'add' || form.mode == 'edit') && !editing}" class="choices-actions"> + <a class="button mainColorBackground" onclick="{addOneChoice}"> <i class="fa fa-plus"/>{__.moreChoice}</a> + </div> + <div if="{form.mode == 'create'}" class="choices-actions"> <a class="button" onclick="{addMoreChoices}"> <i class="fa fa-plus"/>{__.moreChoices}</a> </div> - <div class="actions"> + <div if="{form.mode == 'create'}" class="choices-actions"> <a class="button" onclick="{previousStep}">{__.previous}</a> <input type="submit" class="button mainColorBackground" value="{__.next}"> </div> @@ -36,53 +41,191 @@ require('./PollChoiceTextGroup.tag'); <script> this.installBundle(opts.session, "poll_choices"); + let choiceService = require('../../js/ChoiceService'); this.form = opts.form; this.session = opts.session; + this.parentTag = opts.parentTag; + this.choiceTags = {}; this.on('mount', () => { - this.choices = this.refs.choices; - let choices = this.form.choices; - console.info("init step1 with " + choices.length + " choices"); - console.info(choices); - if (choices.length == 0) { - this.counts = [5]; - this.count = 5; - } else { - this.counts = []; - this.count = 0; - let i; - for (i = 0; i < choices.length; i++) { - if (i > 0 && i % 5 == 0) { - this.addChoices(i); - } + this.formDom = this.refs.formDom; + this.choicesCount = this.form.choices.length - 1; + if (this.form.mode == 'create') { + + // must fill to obtain a 5-multiple + + if (this.choicesCount == -1) { + this.choicesCount = 5; } - if (i <= 5 || i % 5 > 0) { - this.addChoices(this.count + 5); + + while (this.choicesCount % 5 != 0) { + this.choicesCount++; } + this.choicesCount--; + + } + + console.info("init with " + this.form.choices.length + " choices"); + console.info(this.form.choices); + console.info("choicesCount:: " + this.choicesCount); + + for (let i = 0; i <= this.choicesCount; i++) { + this.addChoice(i); } - console.info("Final count: " + this.count); this.update(); }); + this.addOneChoice = (e) => { + this.addChoice(); + let choiceTag = this.choiceTags[this.choicesCount]; + choiceTag.onEditChoice(); + choiceTag.update(); + }; + + this.addChoice = (choiceNumber) => { + if (!choiceNumber && choiceNumber != 0) { + this.choicesCount++; + choiceNumber = this.choicesCount; + } + + let choiceDom = document.createElement("div"); + choiceDom.id = 'choice_' + choiceNumber; + this.refs.choicesContainer.appendChild(choiceDom); + + let tag = riot.mount(choiceDom, 'pollchoicetext', { + number: choiceNumber, + choice: this.form.choices[choiceNumber] || {choiceValue: '', description: ''}, + mode: this.form.mode, + session: this.session + })[0]; + this.choiceTags[choiceNumber] = tag; + + tag.on('editChoice', choiceNumber => { + if (this.editChoiceNumber || this.editChoiceNumber == 0) { + console.info("remove old edit choice id " + this.editChoiceNumber); + let choiceTag = this.choiceTags[this.editChoiceNumber]; + choiceTag.cancelEditChoice(); + choiceTag.update(); + } + this.editChoiceNumber = choiceNumber; + this.editing = true; + this.update({editing:this.editing}); + }); + + tag.on('cancelEditChoice', choiceNumber => { + + if (!this.choiceTags[this.editChoiceNumber].choice.id) { + + // remove choice + delete this.choiceTags[this.editChoiceNumber]; + this.choicesCount--; + let dom = document.getElementById('choice_' + choiceNumber); + dom.parentNode.removeChild(dom); + } + this.editChoiceNumber = null; + this.editing = false; + this.update({editing:this.editing}); + }); + + tag.on('deleteChoice', choiceNumber => { + + let ref = this.choiceTags[choiceNumber]; + let choiceId = ref.choice.id; + choiceService.deleteChoice(this.form.model.id, choiceId, this.form.model.permission).then(result => { + + console.info('after delete choice ' + choiceNumber + " / " + this.choicesCount); + + delete this.choiceTags[choiceNumber]; + + this.form.choices.splice(choiceNumber, 1); + + let dom = document.getElementById('choice_' + choiceNumber); + dom.parentNode.removeChild(dom); + + while (choiceNumber < this.choicesCount) { + console.info("update choices for number: " + choiceNumber); + console.info(this.choiceTags); + let c = this.choiceTags[choiceNumber] = this.choiceTags[choiceNumber + 1]; + c.number--; + c.update({number: c.number}); + delete this.choiceTags[choiceNumber + 1]; + choiceNumber++; + } + + this.parentTag.trigger('count', this.choicesCount); + this.choicesCount--; + }); + + }); + }; + + this.addMoreChoices = () => { + for (let i = 0; i < 5; i++) { + this.addChoice(); + } + }; + this.previousStep = (e) => { - this.form.fromTextChoices(this.choices); + this.form.fromTextChoices(this.formDom); this.form.previousStep(); }; - this.nextStep = (e) => { + + this.action = (e) => { e.preventDefault(); e.stopPropagation(); - this.form.fromTextChoices(this.choices); - this.form.nextStep(); - }; - this.addMoreChoices = () => { - this.addChoices(this.count + 5); - this.update({counts: this.counts}); - }; + if (this.form.mode == 'create') { + + this.form.fromTextChoices(this.formDom); + this.form.nextStep(); + + } else { + let choice = this.form.getTextChoice(this.formDom, this.editChoiceNumber); + + if (choice.id) { + + // update + choiceService.updateChoice(this.form.model.id, choice, this.form.model.permission).then(result => { + + console.info('after update choice'); + console.info(result); - this.addChoices = (i) => { - this.count = i; - this.counts.push(i); + let ref = this.choiceTags[this.editChoiceNumber]; + ref.choice.description = choice.description; + ref.choice.choiceValue = choice.choiceValue; + + ref.cancelEditChoice(); + ref.update(); + + }); + + } else { + + // add + choiceService.addChoice(this.form.model.id, choice, this.form.model.permission).then(result => { + + console.info('after add choice'); + console.info(result); + choice.id = result.id; + + let ref = this.choiceTags[this.editChoiceNumber]; + ref.choice.id = result.id; + ref.choice.description = choice.description; + ref.choice.choiceValue = choice.choiceValue; + + this.form.choices.push(choice) + + ref.cancelEditChoice(); + ref.update(); + + this.choicesCount++; + this.parentTag.trigger('count', this.choicesCount); + }); + + } + + } }; + </script> <style> @@ -94,19 +237,16 @@ require('./PollChoiceTextGroup.tag'); margin-right: 10px; } - .actions { + .choices-actions { display: flex; flex-direction: row; justify-content: flex-start; align-items: center; } - .actions > a { + .choices-actions > a, .choices-actions > input { margin: 5px; } - .actions > input { - margin: 5px; - } </style> </PollChoicesText> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/PollCreated.tag b/pollen-ui-riot-js/src/main/web/tag/poll/PollCreated.tag index 7cdfaf4..04da539 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/PollCreated.tag +++ b/pollen-ui-riot-js/src/main/web/tag/poll/PollCreated.tag @@ -29,7 +29,7 @@ Le sondage «{form.model.title}» vient d'être créé. Un courriel vous a été adressé ainsi qu'aux éventuels participants. <br/> - <a href="#poll/{form.model.id}/vote">Accéder au sondage</a>. + <a href="#poll/{form.model.id}">Accéder au sondage</a>. </div> </div> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/PollVotes.tag b/pollen-ui-riot-js/src/main/web/tag/poll/PollVotes.tag index 5def112..ca71240 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/PollVotes.tag +++ b/pollen-ui-riot-js/src/main/web/tag/poll/PollVotes.tag @@ -133,10 +133,6 @@ }); }; - this.submit = e => { - console.info("submit") - this.refs.form.onsubmit(); - }; this.addOrEditVote = e => { e.preventDefault(); e.stopPropagation(); diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Polls.tag b/pollen-ui-riot-js/src/main/web/tag/poll/Polls.tag index fb243e0..5a3fd6b 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Polls.tag +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Polls.tag @@ -18,7 +18,17 @@ require('../Pagination.tag'); {__.createDate} <i ref="sortCreateDate" class="disabled fa fa-sort"></i> </div> </th> - <th onclick="{toggleSort}"> + <th> + <div> + {__.choiceDates} + </div> + </th> + <th> + <div> + {__.voteDates} + </div> + </th> + <th> <div>{__.status}</div> </th> </tr> @@ -26,15 +36,17 @@ require('../Pagination.tag'); <tbody> <tr each="{poll in polls}"> <td> - <a href="#poll/{poll.id}/vote">{poll.title}</a> + <a href="#poll/{poll.id}">{poll.title}</a> </td> - <td>{poll.beginDateStr}</td> + <td>{poll.createDateStr}</td> + <td>{poll.choiceDates}</td> + <td>{poll.voteDates}</td> <td>{poll.statusStr}</td> </tr> </tbody> <tfoot> <tr> - <th colspan="3"> + <th colspan="7"> <div> <Pagination ref=pagination" sortName='{sortName}' sortValue='false' callback="{callback}"/> </div> @@ -82,7 +94,15 @@ require('../Pagination.tag'); this.refreshPolls = () => { this.polls.forEach(p => { - p.beginDateStr = this.moment(p.beginDate).format('LL'); + p.createDateStr = this.moment(p.createDate).format('llll'); + p.choiceDates = p.beginChoiceDate ? this.moment(p.beginChoiceDate).format('llll') : ''; + if (p.endChoiceDate) { + p.choiceDates += ' -> ' + this.moment(p.endChoiceDate).format('llll'); + } + p.voteDates = p.beginDate ? this.moment(p.beginDate).format('llll') : ''; + if (p.endDate) { + p.voteDates += ' -> ' + this.moment(p.endDate).format('llll'); + } p.statusStr = this.__[p.status]; }); }; @@ -124,6 +144,7 @@ require('../Pagination.tag'); pagination { width: 100%; } + .disabled { color: gray; } @@ -132,6 +153,10 @@ require('../Pagination.tag'); width: 400px; } + .body-container { + max-width: 90%; + } + .container { display: flex; justify-content: space-around; @@ -141,7 +166,7 @@ require('../Pagination.tag'); border: solid 2px #c8ccca; border-radius: 10px; padding: 15px 0; - width: 90%; + width: 100%; } table { @@ -163,7 +188,15 @@ require('../Pagination.tag'); } td:nth-child(2) { - width: 150px; + width: 210px; + } + + td:nth-child(3) { + width: 430px; + } + + td:nth-child(4) { + width: 430px; } .legend { -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.