branch develop updated (5f169b95 -> 0af58178)
This is an automated email from the git hooks/post-receive script. New change to branch develop in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git from 5f169b95 Maj des dépendances java new 0af58178 refs #199 vote par ordre The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit 0af581788866b2f91b47327822d130f48c8cb42e Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Thu May 17 16:34:54 2018 +0200 refs #199 vote par ordre Summary of changes: pollen-ui-riot-js/src/main/web/css/custom.css | 2 + pollen-ui-riot-js/src/main/web/css/main.css | 12 + 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/Poll.js | 4 + pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html | 14 + .../src/main/web/tag/components/Draggable.tag.html | 132 ++++++ .../web/tag/components/MultiLineLabel.tag.html | 6 +- .../src/main/web/tag/poll/ChoiceView.tag.html | 61 +-- .../src/main/web/tag/poll/EditVoteOrder.tag.html | 505 +++++++++++++++++++++ .../src/main/web/tag/poll/Report.tag.html | 112 +---- .../src/main/web/tag/poll/Votes.tag.html | 13 +- .../src/main/web/tag/poll/VotesTable.tag.html | 2 +- .../src/main/web/tag/popup/AddReportModal.tag.html | 102 +++++ .../main/web/tag/popup/ChoiceDetailModal.tag.html | 84 ++++ .../main/web/tag/popup/ShowReportsModal.tag.html | 98 ++++ 16 files changed, 980 insertions(+), 173 deletions(-) create mode 100644 pollen-ui-riot-js/src/main/web/tag/components/Draggable.tag.html create mode 100644 pollen-ui-riot-js/src/main/web/tag/poll/EditVoteOrder.tag.html create mode 100644 pollen-ui-riot-js/src/main/web/tag/popup/AddReportModal.tag.html create mode 100644 pollen-ui-riot-js/src/main/web/tag/popup/ChoiceDetailModal.tag.html create mode 100644 pollen-ui-riot-js/src/main/web/tag/popup/ShowReportsModal.tag.html -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository pollen. See https://gitlab.nuiton.org/chorem/pollen.git commit 0af581788866b2f91b47327822d130f48c8cb42e Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Thu May 17 16:34:54 2018 +0200 refs #199 vote par ordre --- pollen-ui-riot-js/src/main/web/css/custom.css | 2 + pollen-ui-riot-js/src/main/web/css/main.css | 12 + 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/Poll.js | 4 + pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html | 14 + .../src/main/web/tag/components/Draggable.tag.html | 132 ++++++ .../web/tag/components/MultiLineLabel.tag.html | 6 +- .../src/main/web/tag/poll/ChoiceView.tag.html | 61 +-- .../src/main/web/tag/poll/EditVoteOrder.tag.html | 505 +++++++++++++++++++++ .../src/main/web/tag/poll/Report.tag.html | 112 +---- .../src/main/web/tag/poll/Votes.tag.html | 13 +- .../src/main/web/tag/poll/VotesTable.tag.html | 2 +- .../src/main/web/tag/popup/AddReportModal.tag.html | 102 +++++ .../main/web/tag/popup/ChoiceDetailModal.tag.html | 84 ++++ .../main/web/tag/popup/ShowReportsModal.tag.html | 98 ++++ 16 files changed, 980 insertions(+), 173 deletions(-) diff --git a/pollen-ui-riot-js/src/main/web/css/custom.css b/pollen-ui-riot-js/src/main/web/css/custom.css index 5de9e7fd..4b5153c2 100644 --- a/pollen-ui-riot-js/src/main/web/css/custom.css +++ b/pollen-ui-riot-js/src/main/web/css/custom.css @@ -34,6 +34,8 @@ --poll-voting: #fff2ba; --poll-closed: #ffd9ba; + --choice-tile: #d1eaea; + --link: #96a8b2; --separator: #b2c7d3; diff --git a/pollen-ui-riot-js/src/main/web/css/main.css b/pollen-ui-riot-js/src/main/web/css/main.css index 8d7aefcc..f2131bf1 100644 --- a/pollen-ui-riot-js/src/main/web/css/main.css +++ b/pollen-ui-riot-js/src/main/web/css/main.css @@ -223,6 +223,18 @@ ul { background-color: var(--poll-closed); } +.choice-tile { + background-color: var(--choice-tile); +} + +.choicesScale, +.choicesStash { + background-color: var(--h1-background); + color: var(--h1); + opacity: 0.4; + border-radius: 10px; +} + .info-label { font-size: 0.8em; color: var(--info); 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 a6d87a58..c37ddfe1 100644 --- a/pollen-ui-riot-js/src/main/web/i18n/en.json +++ b/pollen-ui-riot-js/src/main/web/i18n/en.json @@ -145,6 +145,9 @@ "poll_votes_totals": "Total", "poll_votes_eliminated": "Eliminated", "poll_votes_noVote": "No vote", + "poll_votes_choicesScaleHelper": "Place here the choices below in the order of your preferences", + "poll_votes_choicesScaleHelper_withMax": "Place here the {0} firsts choices below in the order of your preferences", + "poll_votes_choicesStashHelper": "Stash here the choices not selected", "poll_votes_results_unit_1_one": "vote", "poll_votes_results_unit_1_many": "votes", "poll_votes_results_unit_2_one": "point", 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 dfc12241..3ac0d4a0 100644 --- a/pollen-ui-riot-js/src/main/web/i18n/fr.json +++ b/pollen-ui-riot-js/src/main/web/i18n/fr.json @@ -145,6 +145,9 @@ "poll_votes_totals": "Total", "poll_votes_eliminated": "Éliminé", "poll_votes_noVote": "Aucun vote", + "poll_votes_choicesScaleHelper": "Placez ici les choix ci-dessous dans l'ordre de vos préférences", + "poll_votes_choicesScaleHelper_withMax": "Placez ici vos {0} premiers choix ci-dessous dans l'ordre de vos préférences", + "poll_votes_choicesStashHelper": "Remisez ici les choix non selectionnés", "poll_votes_results_unit_1_one": "vote", "poll_votes_results_unit_1_many": "votes", "poll_votes_results_unit_2_one": "point", diff --git a/pollen-ui-riot-js/src/main/web/js/Poll.js b/pollen-ui-riot-js/src/main/web/js/Poll.js index 8f4ed9a3..e3114ac5 100644 --- a/pollen-ui-riot-js/src/main/web/js/Poll.js +++ b/pollen-ui-riot-js/src/main/web/js/Poll.js @@ -412,6 +412,10 @@ class Poll { return Promise.reject("Init poll after get invalid emails"); } + isVoteByOrder() { + return [3, 5, 6, 7].indexOf(this.voteCountingType) >= 0; + } + } export default singleton(Poll); diff --git a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html index 4a689c36..9cfd4e59 100644 --- a/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/Pollen.tag.html @@ -46,10 +46,16 @@ import "./favoriteList/FavoriteList.tag.html"; import "./popup/ConfirmPopup.tag.html"; import "./popup/InformationPopup.tag.html"; import "./popup/GtuChangeModal.tag.html"; +import "./popup/ChoiceDetailModal.tag.html"; +import "./popup/AddReportModal.tag.html"; +import "./popup/ShowReportsModal.tag.html"; <Pollen class="body-wrapper colors-default"> <ConfirmPopup/> <InformationPopup/> <GtuChangeModal/> + <ChoiceDetailModal/> + <AddReportModal/> + <ShowReportsModal/> <PollenHeader/> <PollenWaiter parent-id="body-content"/> <div id="body-content" class="body-content"> @@ -302,6 +308,14 @@ import "./popup/GtuChangeModal.tag.html"; } }; + window.onresize = () => { + this.bus.trigger("resize"); + }; + + window.addEventListener("orientationchange", () => { + setTimeout(() => this.bus.trigger("orientationchange"), 500); + }); + </script> <style> diff --git a/pollen-ui-riot-js/src/main/web/tag/components/Draggable.tag.html b/pollen-ui-riot-js/src/main/web/tag/components/Draggable.tag.html new file mode 100644 index 00000000..52c3c035 --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/components/Draggable.tag.html @@ -0,0 +1,132 @@ +<!-- + #%L + Pollen :: UI RiotJs + %% + 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% + --> +<Draggable onmousedown={startDesktop} + ontouchstart={startModbil} + ontouchmove={moveMobil} + ontouchend={endMobil}> + <yield/> + + <script type="es6"> + + this.positionMouseInit = {x: 0, y: 0}; + this.positionElementInit = {x: 0, y: 0}; + this.positionCurent = {x: 0, y: 0}; + + this.setPosition = (x, y) => { + this.positionCurent.x = Math.round(x); + this.positionCurent.y = Math.round(y); + this.root.style.transform = "translate(" + this.positionCurent.x + "px, " + this.positionCurent.y + "px)"; + // this.logger.log("Move at " + x + ", " + y); + }; + + this.getX = () => this.positionCurent.x; + this.getY = () => this.positionCurent.y; + + this.startPosition = (x, y) => { + this.positionMouseInit.x = x; + this.positionMouseInit.y = y; + this.positionElementInit.x = this.positionCurent.x; + this.positionElementInit.y = this.positionCurent.y; + this.root.classList.add("drag"); + // this.logger.log("Start at " + this.positionMouseInit.x + ", " + this.positionMouseInit.y); + }; + + this.movePosition = (x, y) => { + // calculate the new cursor position: + this.setPosition( + this.positionElementInit.x + x - this.positionMouseInit.x, + this.positionElementInit.y + y - this.positionMouseInit.y + ); + }; + + this.endPosition = (x, y) => { + this.movePosition(x, y); + this.root.classList.remove("drag"); + this.opts.onDrop && this.opts.onDrop(this, this.positionCurent.x, this.positionCurent.y); + }; + + /*********************************** + ** Desktop Drage & Drop + *************************************/ + + this.startDesktop = event => { + this.startPosition(event.clientX, event.clientY); + document.onmousemove = event2 => this.moveDesktop(event2); + document.onmouseup = event1 => this.endDesktop(event1); + document.onclick = event3 => {event3.stopImmediatePropagation();}; + event.stopImmediatePropagation(); + }; + + this.moveDesktop = event => { + this.movePosition(event.clientX, event.clientY); + event.stopImmediatePropagation(); + }; + + this.endDesktop = event => { + this.endPosition(event.clientX, event.clientY); + /* stop moving when mouse button is released:*/ + document.onmouseup = null; + document.onmousemove = null; + document.onclick = null; + event.stopImmediatePropagation(); + }; + + /*********************************** + ** Mobil Drage & Drop + *************************************/ + + this.startModbil = event => { + let touch = event.changedTouches[0]; + this.startPosition(touch.pageX, touch.pageY); + event.stopPropagation(); + }; + + this.moveMobil = event => { + let touch = event.changedTouches[0]; + this.movePosition(touch.pageX, touch.pageY); + event.stopPropagation(); + event.preventDefault(); + }; + + this.endMobil = event => { + let touch = event.changedTouches[0]; + this.endPosition(touch.pageX, touch.pageY); + event.stopPropagation(); + }; + + </script> + <style> + draggable { + position: absolute; + top: 0; + left: 0; + user-select: none; + transition: transform 500ms; + } + + draggable.drag { + z-index: 1; + transition: none; + } + + </style> + +</Draggable> diff --git a/pollen-ui-riot-js/src/main/web/tag/components/MultiLineLabel.tag.html b/pollen-ui-riot-js/src/main/web/tag/components/MultiLineLabel.tag.html index 5cbfe5a2..aab89abf 100644 --- a/pollen-ui-riot-js/src/main/web/tag/components/MultiLineLabel.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/components/MultiLineLabel.tag.html @@ -8,12 +8,12 @@ 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% @@ -23,7 +23,7 @@ <span ref="label"></span> <script> - this.on("mount", () => { + this.on("update", () => { let labelText; if (this.opts.label) { labelText = this.opts.label; diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/ChoiceView.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/ChoiceView.tag.html index 78f1f090..3eb25302 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/ChoiceView.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/ChoiceView.tag.html @@ -38,11 +38,11 @@ import "../components/MultiLineLabel.tag.html"; {formatDate(parseInt(opts.choice.choiceValue, 10), "L LT")} </span> <div class="choice-ressource" - if={opts.choice.choiceType === "RESOURCE" && meta && isImage(meta)}> + if={opts.choice.choiceType === "RESOURCE" && meta && isImage()}> <img class="image-preview" src="{resourceService.getPreviewUrl(opts.choice.choiceValue)}"/> </div> <div class="choice-ressource" - if={opts.choice.choiceType === "RESOURCE" && meta && !isImage(meta)}> + if={opts.choice.choiceType === "RESOURCE" && meta && !isImage()}> {meta.name} </div> <div if={opts.choice.choiceType === "RESOURCE" || opts.choice.description && !opts.showdescription} class="info-label"> @@ -54,43 +54,6 @@ import "../components/MultiLineLabel.tag.html"; </div> <Report target="{opts.choice}" if="{!opts.hidereport}"/> - <div if={showModalImage} onclick={closeModalImage}> - <div class="c-overlay"></div> - <div class="modal-image o-modal"> - <div class="c-card"> - <header class="c-card__header"> - <h2 if={opts.choice.choiceType === "TEXT"}> - {opts.choice.choiceValue} - </h2> - <h2 if={opts.choice.choiceType === "DATE"}> - {formatDate(parseInt(opts.choice.choiceValue, 10),"LLL")} - </h2> - <h2 if={opts.choice.choiceType === "DATETIME"}> - {formatDate(parseInt(opts.choice.choiceValue, 10))} - </h2> - <div if={opts.choice.choiceType === "RESOURCE" && meta && isImage(meta)}> - <img if={meta && isImage(meta)} - class="image-preview" - src="{session.configuration.endPoint}/v1/resources/{opts.choice.choiceValue}"/> - </div> - <div if={opts.choice.choiceType === "RESOURCE" && meta && !isImage(meta)}> - <h2>{meta.name}</h2> - <a class="c-button c-button--info" - if={meta && !isImage(meta)} - href="{session.configuration.endPoint}/v1/resources/{opts.choice.choiceValue}/download" - target="_blank"> - <i class="fa fa-download"></i> - {__.download} - </a> - </div> - </header> - <div class="c-card__body"> - <MultiLineLabel label="{opts.choice.description}"></MultiLineLabel> - </div> - </div> - </div> - </div> - <script type="es6"> import session from "../../js/Session"; import resourceService from "../../js/ResourceService"; @@ -108,8 +71,8 @@ import "../components/MultiLineLabel.tag.html"; }); } - this.isImage = (meta) => { - return meta.contentType.match(/^image\//i); + this.isImage = () => { + return this.meta.contentType.match(/^image\//i); }; this.standardTooltip = () => { @@ -118,18 +81,7 @@ import "../components/MultiLineLabel.tag.html"; this.openModalImage = () => { if (this.opts.choice.description && !this.opts.showdescription || this.opts.choice.choiceType === "RESOURCE") { - this.showModalImage = true; - } - }; - - this.closeModalImage = () => { - this.showModalImage = false; - }; - - this.onEscape = () => { - if (this.showModalImage) { - this.closeModalImage(); - this.update(); + this.bus.trigger("openChoiceDetail", this.opts.choice, this.meta); } }; @@ -145,8 +97,6 @@ import "../components/MultiLineLabel.tag.html"; }); } - this.listen("escape", this.onEscape); - </script> <style> @@ -170,6 +120,7 @@ import "../components/MultiLineLabel.tag.html"; .choice-ressource .image-preview{ max-width: 7em; max-height: 3em; + pointer-events: none; } .modal-image { diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/EditVoteOrder.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/EditVoteOrder.tag.html new file mode 100644 index 00000000..204aea04 --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/poll/EditVoteOrder.tag.html @@ -0,0 +1,505 @@ +<!-- + #%L + Pollen :: UI RiotJs + %% + 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% + --> + +<EditVoteOrder class="no-anim"> + + <div class="form-wrapper"> + <form id="voteForm" class="voter" ref="formAddVote"> + <HumanInput onsubmit="{voteInEdition ? updateVote : addVote}"></HumanInput> + <div class="current-voter"> + <div class="o-field o-field--icon-left o-field--icon-right" + if={poll.canVote || voteInEdition} > + <i class="fa fa-fw fa-user c-icon"></i> + <input class="c-field {c-field--error: !voteInEdition && error && error['voter.name']}" + type="text" + ref="voterName" + name="voterName" + required + maxlength="255" + tabindex="1" + placeholder={_t.authorPlaceHolder} + value={poll.voterName}> + </div> + <div if={!poll.canVote && !voteInEdition} + class="choices-label"> + {_t.choices} + </div> + </div> + <div class="choicesScale" ref="scale"> + <i class="fa fa-plus-circle"></i> + {maxChoiceNumber ? _l("choicesScaleHelper_withMax", maxChoiceNumber) : _t.choicesScaleHelper} + <i class="fa fa-minus-circle"></i> + </div> + <div class="choicesDock" ref="dock"> + <button if={!voteInEdition} + class="c-button c-button--brand pull-right" + type="submit" + name="newVote" + disabled={voting || !orderedAllChoices || maxChoiceNumber && maxChoiceNumber < choicesSelectedCount}> + <i class="fa fa-envelope"></i> + {_t.toVote} + </button> + <button if="{voteInEdition}" + class="c-button c-button--error" + type="button" + onclick="{cancelEditVote}" + disabled={voting}> + <i class="fa fa-remove"></i> + {_t.cancelEdition} + </button> + <button if="{voteInEdition}" + class="c-button c-button--success" + disabled={voting || !orderedAllChoices || maxChoiceNumber && maxChoiceNumber < choicesSelectedCount} + type="submit"> + <i class="fa fa-check"></i> + {_t.validateEdition} + </button> + </div> + + <div class="c-hint--static c-hint--error"> + <span if="{maxChoiceNumber && maxChoiceNumber < choicesSelectedCount}"> + {_t.tooManyChoicesSelected} {maxChoiceNumber} + </span> + </div> + <div class="choicesStash" ref="stash" show={maxChoiceNumber}> + <i class="fa fa-trash"></i> + {_t.choicesStashHelper} + </div> + <Draggable each={choice in poll.choices && poll.choices.slice().reverse() || []} + ref="choices" + on-drop={dropChoice} + class="choice-draggable"> + <div class={choice-tile: true, noFirst: !isFirstGroup, noLast: !isLastGroup}> + <div class="choice-order">{voteOrder !== undefined ? voteOrder + 1 : ""}</div> + <ChoiceView choice={choice} center="true" tooltip-placement="left"></ChoiceView> + <div if={parent.poll.resultIsVisible} class="score-choice"> + <span if={!choice.score}>{parent.parent._t.noVote}</span> + <span if={choice.score}> + <i if="{choice.score.scoreOrder === 0}" class="fa fa-trophy fa-15x winner"></i> + {choice.score.scoreValue} + <span if={choice.score.scoreValue || !parent.pollTypeCoombs}> + {parent.parent._t["results_unit_" + parent.poll.voteCountingType + "_" + (choice.score.scoreValue > 1 ? "many" : "one")]} + </span> + <span if={!choice.score.scoreValue && parent.pollTypeCoombs}> + {parent.parent._t.eliminated} + </span> + </span> + </div> + </div> + </Draggable> + </form> + + </div> + <script type="es6"> + import "../components/Draggable.tag.html"; + import session from "../../js/Session"; + import moment from "moment"; + import poll from "../../js/Poll.js"; + + this.loaded = false; + this.moment = moment; + this.installBundle(session, "poll_votes"); + + this.voting = false; + this.orderedAllChoices = false; + + this.poll = poll; + this.poll.loadForVotes().then(() => { + this.update(); + }); + + this.onPollChange = poll2 => { + this.poll = poll2; + this.pollTypeCoombs = poll2.voteCountingType && poll2.voteCountingType === 7; + this.maxChoiceNumber = this.poll.voteCountingConfig.maxChoiceNumber; + this.update(); + this.placeChoices(true); + }; + + this.listen("resize", () => this.placeChoices(true)); + this.listen("orientationchange", () => this.placeChoices(true)); + + this.listen("poll", this.onPollChange); + this.listen("user", (user, oldUser) => { + if (user !== oldUser) { + if (user && this.refs.voterName && this.refs.voterName.value !== "") { + this.refs.voterName.value = user.name; + } + this.update(); + } + }); + + this.placeChoices = noAnim => { + if (!this.refs.choices) { + return; + } + if (noAnim) { + this.root.classList.add("no-anim"); + } + + // on place les choix selectionnés sur l'échelle + let scale = this.refs.scale; + let scaleCenterX = scale.offsetLeft + scale.offsetWidth / 2; + + + let choicesSelected = this.refs.choices + .filter(choice1 => choice1.voteOrder !== undefined) + .sort((c1, c2) => Math.sign(c1.choice.choiceOrder - c2.choice.choiceOrder)); + + this.choicesSelectedCount = choicesSelected.length; + + let scaleHeight = choicesSelected + .map(c => (c.choiceType === "RESOURCE" ? 56 : 40) + 20) // FIXME dans 95% une ressource est une image + .reduce((h1, h2) => h1 + h2, 20); + scale.style.height = scaleHeight + "px"; + + let choicesHeightTot = choicesSelected + .map(choice1 => choice1.root.offsetHeight) + .reduce((h1, h2) => h1 + h2, 0); + + let maxIndex = choicesSelected + .map(choice1 => choice1.voteOrder) + .reduce((order1, order2) => Math.max(order1, order2), 0); + + let paddingChoice = (scale.offsetHeight - choicesHeightTot) / (maxIndex + 2); + let currentY = scale.offsetTop + paddingChoice; + + for (let index = 0; index <= maxIndex; index++) { + let choices = choicesSelected.filter(choice => choice.voteOrder === index); + for (let index2 = 0, l = choices.length; index2 < l; index2++) { + let choice = choices[index2]; + choice.isFirstGroup = index2 === 0; + choice.isLastGroup = index2 === l - 1; + let x = scaleCenterX - (choice.root.offsetWidth / 2); + choice.setPosition(x, currentY); + currentY += choice.root.offsetHeight; + } + currentY += paddingChoice; + } + + // on place les choix en attent sur la pile + const dockOffset = 3; + const dockChoiceVisibleMax = 3; + let nbDock = this.refs.choices + .filter(choice1 => choice1.voteOrder === undefined && !choice1.stashed) + .length; + let dockChoiceVisible = Math.min(dockChoiceVisibleMax, nbDock); + + let dock = this.refs.dock; + let dockX = dock.offsetLeft + (dock.offsetWidth + dockOffset * dockChoiceVisible) / 2; + let dockY = dock.offsetTop + (dock.offsetHeight + dockOffset * dockChoiceVisible) / 2; + let dockIndex = 0; + + this.orderedAllChoices = true; + + this.refs.choices + .filter(choice => choice.voteOrder === undefined && !choice.stashed) + .forEach(choice => { + let offset = Math.min(0, (nbDock - dockChoiceVisible) - dockIndex) * dockOffset; + let y = dockY - choice.root.offsetHeight / 2 + offset; + let x = dockX - choice.root.offsetWidth / 2 + offset; + dockIndex++; + choice.isFirstGroup = true; + choice.isLastGroup = true; + choice.setPosition(x, y); + this.orderedAllChoices = false; + }); + + // on place les choix remisé dans la remise + if (this.maxChoiceNumber) { + + let stash = this.refs.stash; + let stashCenterX = stash.offsetLeft + stash.offsetWidth / 2; + let stashHeight = 0; + currentY = stash.offsetTop; + this.refs.choices + .filter(choice => choice.stashed) + .sort((c1, c2) => Math.sign(c1.choice.choiceOrder - c2.choice.choiceOrder)) + .forEach((choice) => { + let y = currentY; + let x = stashCenterX - choice.root.offsetWidth / 2; + choice.isFirstGroup = true; + choice.isLastGroup = true; + choice.setPosition(x, y); + currentY += choice.root.offsetHeight; + stashHeight += choice.root.offsetHeight; + }); + stash.style.height = stashHeight + "px"; + } + + setTimeout(() => { + this.root.classList.remove("no-anim"); + }, 100); + this.update(); + }; + + this.dropChoice = (choiceElt, x, y) => { + let scale = this.refs.scale; + let yMin = scale.offsetTop - choiceElt.root.offsetHeight / 2; + let yMax = scale.offsetTop + scale.offsetHeight - choiceElt.root.offsetHeight / 2; + + choiceElt.voteOrder = undefined; + let curentChoices = this.refs.choices + .filter(choice1 => choice1.voteOrder !== undefined && choice1 !== choiceElt) + .sort((c1, c2) => Math.sign(c1.getY() - c2.getY())); + + if (yMin <= y && y <= yMax) { + + curentChoices.forEach(c => { + c.voteOrder = 1 + c.voteOrder * 2; // on multiplie par 2 pour toujours avoir un index d'insertion de libre + }); + + let index = 0; + curentChoices.forEach(choice1 => { + let choice1Min = choice1.getY() - choiceElt.root.offsetHeight / 2; + let choice1Max = choice1Min + choice1.root.offsetHeight; + if (choice1Min <= y && y <= choice1Max) { // le choix est palcé au méme endroit + index = choice1.voteOrder; + } else if (y > choice1Max) { // le choix est palcé strictement après + index = choice1.voteOrder + 1; + } + this.logger.log("drop :" + choice1Min + ", " + choice1Max + ", " + y + ", " + choice1.voteOrder + " ==> " + index); + }); + choiceElt.voteOrder = index; + } + + if (this.maxChoiceNumber) { + yMin = this.refs.stash.offsetTop - choiceElt.root.offsetHeight / 2; + choiceElt.stashed = yMin <= y; + } + + this.compactVoteOrders(); + choiceElt.root.style["z-index"] = "1"; + + this.placeChoices(); + + setTimeout(() => { + choiceElt.root.style["z-index"] = ""; + }, 600); + }; + + this.compactVoteOrders = () => { + // on recompacte la liste pour ne plus avoir de trous dans les index; + let newIndex = -1; + let currentIndex = -1; + this.refs.choices + .filter(choice1 => choice1.voteOrder !== undefined) + .sort((c1, c2) => Math.sign(c1.voteOrder - c2.voteOrder)) + .forEach(choice1 => { + if (choice1.voteOrder > currentIndex) { + currentIndex = choice1.voteOrder; + newIndex++; + } + choice1.voteOrder = newIndex; + }); + }; + + this.resetVoteForm = () => { + if (this.poll.canVote) { + this.refs.voterName.value = null; + this.refs.choices.forEach(choice => { + choice.voteOrder = undefined; + choice.stashed = false; + }); + this.placeChoices(); + } + }; + + this.cancelEditVote = () => { + this.voteInEdition = null; + this.resetVoteForm(); + }; + + this.editVote = vote => { + this.voteInEdition = vote; + this.update(); + this.refs.voterName.value = vote.voterName; + this.refs.choices.forEach(choiceElt => { + let choice = vote.choice.find(c => c.choiceId === choiceElt.choice.id); + if (this.maxChoiceNumber && !choice.voteValue) { + choiceElt.voteOrder = undefined; + choiceElt.stashed = true; + } else { + choiceElt.voteOrder = choice && choice.voteValue; + choiceElt.stashed = false; + } + }); + this.compactVoteOrders(); + this.placeChoices(); + }; + + this.addVote = e => { + e.preventDefault(); + e.stopPropagation(); + + this.voting = true; + this.update(); + + let vote = { + id: null, + voterName: this.refs.voterName.value, + choice: [] + }; + + this.refs.choices.forEach(c => { + vote.choice.push({ + choiceId: c.choice.id, + voteValue: c.voteOrder + 1 + }); + }); + + this.poll.addVote(vote).then(() => { + //this.resetVoteForm(); + if (this.poll.resultIsVisible) { + this.poll.loadResults().then(() => { + this.voting = false; + this.update(); + }, () => { + this.voting = false; + this.update(); + }); + } else { + this.voting = false; + this.update(); + } + }, + (error) => { + this.error = error; + this.voting = false; + this.update(); + }); + }; + + this.updateVote = e => { + e.preventDefault(); + e.stopPropagation(); + + this.voting = true; + this.update(); + + let updateVote = Object.assign({}, this.voteInEdition); // don't modify original vote + updateVote.voterName = this.refs.voterName.value; + + this.refs.choices.forEach(c => { + let voteChoice = this.poll.getVoteChoice(updateVote, c.choice); + if (!voteChoice) { + voteChoice = { + choiceId: c.choice.id + }; + updateVote.choice.push(voteChoice); + } + voteChoice.voteValue = c.voteOrder + 1; + }); + + this.poll.updateVote(updateVote).then(() => { + this.voteInEdition = null; + this.resetVoteForm(); + if (this.poll.resultIsVisible) { + this.poll.loadResults().then(() => { + this.voting = false; + this.update(); + }, () => { + this.voting = false; + this.update(); + }); + } else { + this.voting = false; + this.update(); + } + }, + (error) => { + this.voting = false; + this.error = error; + this.update(); + }); + }; + + </script> + <style> + + .form-wrapper { + width: 320px; + max-width: 90%; + margin: 0 auto; + position: relative; + } + + .choice-draggable { + width: 95%; + + } + + .choicesScale, + .choicesStash { + min-height: 200px; + display: flex; + flex-direction: column; + justify-content: space-around; + align-items: center; + text-align: center; + margin: 5px 0; + font-size: 2.4em; + } + + .choicesScale { + min-height: 400px; + } + + .choicesDock { + height: 50px; + display: flex; + justify-content: space-around; + align-items: center; + } + + .choice-tile { + width: 100%; + min-height: 40px; + font-size: 1.2em; + display: flex; + justify-content: space-between; + align-items: center; + box-shadow: 1px 1px 1px hsla(0,0%,7%,.6); + padding: 5px; + border-radius: 10px; + } + + .choice-tile.noFirst { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + .choice-tile.noLast { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + + editvoteorder.no-anim .choice-draggable { + transition: none; + } + + .c-hint--static { + height: 1.2em; + } + + + </style> +</EditVoteOrder> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Report.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/Report.tag.html index 5f08f02f..323aa905 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Report.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Report.tag.html @@ -33,64 +33,6 @@ <i class="fa fa-exclamation-circle" aria-hidden="true"></i> </span> - <modal ref="modalAddReport" header={__.add_title} onsubmit={submitAddReport} label={__.add_action} type="warning"> - <fieldset class="o-fieldset"> - <legend class="o-fieldset__legend">{parent.__.level}</legend> - <label class="c-field c-field--choice"> - <input type="radio" - name="level" - ref="level" - required - value="SPAM"> - {parent.__.level_spam_detail} - </label> - <label class="c-field c-field--choice"> - <input type="radio" - name="level" - ref="level" - value="OFF_TOPIC"> - {parent.__.level_off_topic_detail} - </label> - <label class="c-field c-field--choice"> - <input type="radio" - name="level" - ref="level" - value="ILLEGAL"> - {parent.__.level_illegal_detail} - </label> - </fieldset> - - <div class="o-form-element"> - <label class="c-label" for="email">{parent.__.email}</label> - <input type="email" - id="email" - ref="email" - placeholder="{parent.__.email_placeholder}" - value={parent.session.getUser() && parent.session.getUser().email} - required - maxlength="255" - class="c-field"/> - </div> - </modal> - - <modal ref="modalReports" header={__.reports_title} only-ok="true"> - <div each={level in parent.reportLevels}> - <div class="c-card__item c-card__item--{error : level == 'ILLEGAL', warning: level == 'SPAM', info : level == 'OFF_TOPIC'}"> - {parent.parent.__['level_' + level.toLowerCase()]} - </div> - <div class="c-card__item"> - <div each={report in parent.parent.filterReports(level)} class="report {ignore : report.ignore}"> - <div class="report-email"> - {report.email} - </div> - <a onclick={parent.parent.parent.toggleIgnoreReport(report)} title={report.ignore ? parent.parent.parent.__.toEnable : parent.parent.parent.__.toIgnore}> - <i class="fa fa-{report.ignore ? 'bell' : 'bell-slash'}" aria-hidden="true"></i> - </a> - </div> - </div> - </div> - </modal> - <script type="es6"> import session from "../../js/Session"; import poll from "../../js/Poll.js"; @@ -105,63 +47,13 @@ this.openModalAddReport = e => { e.preventDefault(); e.stopPropagation(); - this.refs.modalAddReport.refs.level.forEach(radio => {radio.checked = false;}); - this.refs.modalAddReport.open().then(() => {this.update();}, () => {}); - }; - - this.submitAddReport = () => { - let report = { - level: this.refs.modalAddReport.refs.level.find(radio => radio.checked).value, - email: this.refs.modalAddReport.refs.email.value - }; - - return this.poll.addReport(this.opts.target, report); - + this.bus.trigger("openAddReport", this.opts.target); }; this.openReports = e => { e.preventDefault(); e.stopPropagation(); - this.poll.getReports(this.opts.target).then(reports => { - this.reports = reports; - this.reportLevels = this.levels.filter(level => reports.find(report => report.level === level)); - this.refs.modalReports.open().then(() => { - if (this.poll.isPoll(this.opts.target)) { - this.poll.reloadPoll(); - } else if (this.poll.isComment(this.opts.target)) { - this.poll.loadComments(); - } else if (this.poll.isChoice(this.opts.target)) { - this.poll.loadChoices(); - } else { - this.update(); - } - }, () => {}); - this.update(); - }); - }; - - this.toggleIgnoreReport = report => () => { - let report2 = Object.assign({}, report); - report2.ignore = !report2.ignore; - this.poll.saveReport(this.opts.target, report2).then(() => { - this.poll.getReports(this.opts.target).then(reports => { - this.reports = reports; - this.reportLevels = this.levels.filter(level => reports.find(report3 => report3.level === level)); - this.update(); - }); - }); - }; - - this.getReportLevels = () => { - this.levels.filter(level => this.reports.find(report => report.level === level)); - }; - - this.hasReports = level => { - return this.filterReports(level).length > 0; - }; - - this.filterReports = level => { - return this.reports.filter(report => report.level === level); + this.bus.trigger("openReports", this.opts.target); }; </script> diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html index 19d88d5f..4d679bf0 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Votes.tag.html @@ -21,6 +21,7 @@ import "./Choice.tag.html"; import "./ChoiceView.tag.html"; import "./EditVote.tag.html"; +import "./EditVoteOrder.tag.html"; import "./VotesTable.tag.html"; import "../components/HumanInput.tag.html"; import "../components/LazyLoad.tag.html"; @@ -28,7 +29,10 @@ import "../components/Avatar.tag.html"; <Votes> <div class="container" show="{loaded}"> - <EditVote ref="editVote"/> + <EditVote ref="editVote" + if={!poll.isVoteByOrder()}/> + <EditVoteOrder ref="editVoteOrder" + if={poll.isVoteByOrder()}/> <!-- Form to vote --> <p class="warning-label warning" if="{loaded && !poll.canVote}"> @@ -59,7 +63,7 @@ import "../components/Avatar.tag.html"; </form> <!-- Show votes --> - <VotesTable if="{poll.voteIsVisible && poll.voteCount > 0}" /> + <VotesTable if="{poll.voteIsVisible && poll.voteCount > 0}" on-edit-vote={editVote}/> </div> @@ -85,8 +89,9 @@ import "../components/Avatar.tag.html"; this.listen("poll", this.onPollChange); - this.editVote = (vote) => () => { - this.refs.editVote.editVote(vote); + this.editVote = (vote) => { + let editor = this.poll.isVoteByOrder() ? this.refs.editVoteOrder : this.refs.editVote; + editor.editVote(vote); }; this.addChoice = (e) => { diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/VotesTable.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/VotesTable.tag.html index d9bf12e3..fded9bc5 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/VotesTable.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/VotesTable.tag.html @@ -146,7 +146,7 @@ }); this.editVote = (vote) => () => { - this.parent.refs.editVote.editVote(vote); + this.opts.onEditVote && this.opts.onEditVote(vote); }; this.deleteVote = (vote) => (e) => { diff --git a/pollen-ui-riot-js/src/main/web/tag/popup/AddReportModal.tag.html b/pollen-ui-riot-js/src/main/web/tag/popup/AddReportModal.tag.html new file mode 100644 index 00000000..34335b8b --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/popup/AddReportModal.tag.html @@ -0,0 +1,102 @@ +<!-- + #%L + Pollen :: UI RiotJs + %% + 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 "./Modal.tag.html"; +<AddReportModal> + <modal ref="modal" + header={_t.add_title} + onsubmit={submit} + label={_t.add_action} + type="warning"> + <fieldset class="o-fieldset"> + <legend class="o-fieldset__legend">{parent._t.level}</legend> + <label class="c-field c-field--choice"> + <input type="radio" + name="level" + ref="level" + required + value="SPAM"> + {parent._t.level_spam_detail} + </label> + <label class="c-field c-field--choice"> + <input type="radio" + name="level" + ref="level" + value="OFF_TOPIC"> + {parent._t.level_off_topic_detail} + </label> + <label class="c-field c-field--choice"> + <input type="radio" + name="level" + ref="level" + value="ILLEGAL"> + {parent._t.level_illegal_detail} + </label> + </fieldset> + + <div class="o-form-element"> + <label class="c-label" for="email">{parent._t.email}</label> + <input type="email" + id="email" + ref="email" + placeholder="{parent._t.email_placeholder}" + value={parent.session.getUser() && parent.session.getUser().email} + required + maxlength="255" + class="c-field"/> + </div> + </modal> + + <script type="es6"> + import session from "../../js/Session"; + import poll from "../../js/Poll.js"; + + this.session = session; + this.installBundle(this.session, "report"); + this.poll = poll; + this.levels = ["ILLEGAL", "SPAM", "OFF_TOPIC"]; + this.reports = []; + this.ignoreReports = []; + + this.open = (target) => { + this.target = target; + this.refs.modal.refs.level.forEach(radio => {radio.checked = false;}); + let promise = this.refs.modal.open().then(() => { + this.update(); + }, () => {}); + this.update(); + return promise; + }; + + this.submit = () => { + let report = { + level: this.refs.modal.refs.level.find(radio => radio.checked).value, + email: this.refs.modal.refs.email.value + }; + + return this.poll.addReport(this.target, report); + + }; + + this.listen("openAddReport", this.open); + + </script> + +</AddReportModal> diff --git a/pollen-ui-riot-js/src/main/web/tag/popup/ChoiceDetailModal.tag.html b/pollen-ui-riot-js/src/main/web/tag/popup/ChoiceDetailModal.tag.html new file mode 100644 index 00000000..a5a58728 --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/popup/ChoiceDetailModal.tag.html @@ -0,0 +1,84 @@ +<!-- + #%L + Pollen :: UI RiotJs + %% + 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/MultiLineLabel.tag.html"; +<ChoiceDetailModal> + <Modal header={title} + ref="modal" + only-ok="true"> + <div if={parent.choice && parent.choice.choiceType === "RESOURCE" && parent.meta}> + <img if={parent.isImage()} + class="image-preview" + src="{parent.session.configuration.endPoint}/v1/resources/{parent.choice.choiceValue}"/> + <a class="c-button c-button--info" + if={!parent.isImage()} + href="{parent.session.configuration.endPoint}/v1/resources/{parent.choice.choiceValue}/download" + target="_blank"> + <i class="fa fa-download"></i> + {parent._t.download} + </a> + </div> + <MultiLineLabel label="{parent.choice && parent.choice.description}"></MultiLineLabel> + </Modal> + + <script type="es6"> + import session from "../../js/Session"; + import resourceService from "../../js/ResourceService"; + + this.session = session; + this.resourceService = resourceService; + this.installBundle(this.session, "choice"); + + this.open = (choice, meta) => { + this.choice = choice; + this.meta = meta; + if (this.choice.choiceType === "TEXT") { + this.title = this.choice.choiceValue; + } else if (this.choice.choiceType === "DATE") { + this.title = this.formatDate(parseInt(this.choice.choiceValue, 10), "LLL"); + } else if (this.choice.choiceType === "DATETIME") { + this.title = this.formatDate(parseInt(this.choice.choiceValue, 10)); + } else if (this.choice.choiceType === "RESOURCE") { + this.title = this.meta && this.meta.name; + } + let promise = this.refs.modal.open().then(() => { + this.update(); + }, () => {}); + this.update(); + return promise; + }; + + + this.isImage = () => { + return this.meta.contentType.match(/^image\//i); + }; + + this.listen("openChoiceDetail", this.open); + + </script> + + <style> + .modal-image img { + max-width: 100%; + max-height:100%; + } + + </style> +</ChoiceDetailModal> diff --git a/pollen-ui-riot-js/src/main/web/tag/popup/ShowReportsModal.tag.html b/pollen-ui-riot-js/src/main/web/tag/popup/ShowReportsModal.tag.html new file mode 100644 index 00000000..2a2f5100 --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/popup/ShowReportsModal.tag.html @@ -0,0 +1,98 @@ +<!-- + #%L + Pollen :: UI RiotJs + %% + 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 "../popup/Modal.tag.html"; +<ShowReportsModal> + <modal ref="modalReports" header={_t.reports_title} only-ok="true"> + <div each={level in parent.reportLevels}> + <div class="c-card__item c-card__item--{error : level == 'ILLEGAL', warning: level == 'SPAM', info : level == 'OFF_TOPIC'}"> + {parent.parent._t['level_' + level.toLowerCase()]} + </div> + <div class="c-card__item"> + <div each={report in parent.parent.filterReports(level)} class="report {ignore : report.ignore}"> + <div class="report-email"> + {report.email} + </div> + <a onclick={parent.parent.parent.toggleIgnoreReport(report)} title={report.ignore ? parent.parent.parent._t.toEnable : parent.parent.parent._t.toIgnore}> + <i class="fa fa-{report.ignore ? 'bell' : 'bell-slash'}" aria-hidden="true"></i> + </a> + </div> + </div> + </div> + </modal> + + <script type="es6"> + import session from "../../js/Session"; + import poll from "../../js/Poll.js"; + + this.session = session; + this.installBundle(this.session, "report"); + this.poll = poll; + this.levels = ["ILLEGAL", "SPAM", "OFF_TOPIC"]; + this.reports = []; + this.ignoreReports = []; + + this.open = target => { + this.target = target; + this.poll.getReports(this.target).then(reports => { + this.reports = reports; + this.reportLevels = this.levels.filter(level => reports.find(report => report.level === level)); + this.refs.modalReports.open().then(() => { + if (this.poll.isPoll(this.target)) { + this.poll.reloadPoll(); + } else if (this.poll.isComment(this.target)) { + this.poll.loadComments(); + } else if (this.poll.isChoice(this.target)) { + this.poll.loadChoices(); + } + this.update(); + }, () => {}); + this.update(); + }); + }; + + this.toggleIgnoreReport = report => () => { + let report2 = Object.assign({}, report); + report2.ignore = !report2.ignore; + this.poll.saveReport(this.target, report2).then(() => { + this.poll.getReports(this.target).then(reports => { + this.reports = reports; + this.reportLevels = this.levels.filter(level => reports.find(report3 => report3.level === level)); + this.update(); + }); + }); + }; + + this.getReportLevels = () => { + this.levels.filter(level => this.reports.find(report => report.level === level)); + }; + + this.hasReports = level => { + return this.filterReports(level).length > 0; + }; + + this.filterReports = level => { + return this.reports.filter(report => report.level === level); + }; + + this.listen("openReports", this.open); + + </script> +</ShowReportsModal> -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.
participants (1)
-
chorem.org scm