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 90d0cc871046b06d55b79a3c466cf6f92b2c389a Author: Sylvain Bavencoff <bavencoff@codelutin.com> Date: Tue Jul 18 10:49:57 2017 +0200 UI des utilisateurs --- .../persistence/entity/PollenUserTopiaDao.java | 20 +++ .../chorem/pollen/rest/api/v1/PollenUserApi.java | 5 +- .../pollen/services/service/PollenUserService.java | 4 +- .../services/service/PollenUserServiceTest.java | 2 +- pollen-ui-riot-js/src/main/web/i18n.json | 10 +- pollen-ui-riot-js/src/main/web/js/UserService.js | 6 +- .../src/main/web/tag/UserCard.tag.html | 165 ++++++++------------- .../src/main/web/tag/UserEditModal.tag.html | 73 +++++++++ pollen-ui-riot-js/src/main/web/tag/Users.tag.html | 103 +++++-------- .../src/main/web/tag/components/Card.tag.html | 7 +- .../web/tag/favoriteList/ChildListCard.tag.html | 16 +- .../web/tag/favoriteList/FavoriteListCard.tag.html | 14 +- .../main/web/tag/favoriteList/MemberCard.tag.html | 8 +- .../src/main/web/tag/poll/Polls.tag.html | 3 +- 14 files changed, 235 insertions(+), 201 deletions(-) diff --git a/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserTopiaDao.java b/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserTopiaDao.java index b6f841f0..a71e1cc1 100644 --- a/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserTopiaDao.java +++ b/pollen-persistence/src/main/java/org/chorem/pollen/persistence/entity/PollenUserTopiaDao.java @@ -21,7 +21,14 @@ package org.chorem.pollen.persistence.entity; * #L% */ +import com.google.common.collect.Maps; +import org.apache.commons.lang3.StringUtils; +import org.chorem.pollen.persistence.DaoUtils; +import org.nuiton.util.pagination.PaginationParameter; +import org.nuiton.util.pagination.PaginationResult; + import java.util.List; +import java.util.Map; /** * @author Tony Chemit - dev@tchemit.fr @@ -33,6 +40,19 @@ public class PollenUserTopiaDao extends AbstractPollenUserTopiaDao<PollenUser> { return forEmailEquals(email).exists(); } + public PaginationResult<PollenUser> findAll(PaginationParameter page, String search) { + + Map<String, Object> parameters = Maps.newHashMap(); + + String hql = "SELECT user FROM " + PollenUser.class.getName() + " as user"; + if (StringUtils.isNotBlank(search)) { + hql += " WHERE " + DaoUtils.getSearchClause("user", parameters, PollenUser.PROPERTY_NAME, search); + } + + return findPage(hql, parameters, page); + + } + @Override public void delete(PollenUser entity) { diff --git a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenUserApi.java b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenUserApi.java index d1218eb2..79c07e9f 100644 --- a/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenUserApi.java +++ b/pollen-rest-api/src/main/java/org/chorem/pollen/rest/api/v1/PollenUserApi.java @@ -61,9 +61,10 @@ public class PollenUserApi { @Path("/users") @GET public PaginationResultBean<PollenUserBean> getUsers(@Context PollenUserService pollenUserService, - @BeanParam PaginationParameterBean paginationParameter) { + @BeanParam PaginationParameterBean paginationParameter, + @QueryParam("search") String search) { - return pollenUserService.getUsers(paginationParameter); + return pollenUserService.getUsers(paginationParameter, search); } diff --git a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java index f7975f36..b399aa8b 100644 --- a/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java +++ b/pollen-services/src/main/java/org/chorem/pollen/services/service/PollenUserService.java @@ -64,7 +64,7 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer return input; }; - public PaginationResultBean<PollenUserBean> getUsers(PaginationParameterBean paginationParameter) { + public PaginationResultBean<PollenUserBean> getUsers(PaginationParameterBean paginationParameter, String search) { checkIsConnected(); checkIsAdmin(); @@ -73,7 +73,7 @@ public class PollenUserService extends PollenServiceSupport implements PollenSer if (CollectionUtils.isEmpty(page.getOrderClauses())) { page.getOrderClauses().add(new PaginationOrder(PollenUser.PROPERTY_NAME, false)); } - PaginationResult<PollenUser> pollenUsers = getPollenUserDao().forAll().findPage(page); + PaginationResult<PollenUser> pollenUsers = getPollenUserDao().findAll(page, search); return toPaginationListBean(PollenUserBean.class, pollenUsers, pollenUserFunction); diff --git a/pollen-services/src/test/java/org/chorem/pollen/services/service/PollenUserServiceTest.java b/pollen-services/src/test/java/org/chorem/pollen/services/service/PollenUserServiceTest.java index c3cb03db..a06e0bcb 100644 --- a/pollen-services/src/test/java/org/chorem/pollen/services/service/PollenUserServiceTest.java +++ b/pollen-services/src/test/java/org/chorem/pollen/services/service/PollenUserServiceTest.java @@ -68,7 +68,7 @@ public class PollenUserServiceTest extends AbstractPollenServiceTest { login("jean@pollen.fake", "fake"); - PaginationResultBean<PollenUserBean> users = service.getUsers(null); + PaginationResultBean<PollenUserBean> users = service.getUsers(null, ""); Assert.assertNotNull(users); Assert.assertTrue(CollectionUtils.isNotEmpty(users.getElements())); diff --git a/pollen-ui-riot-js/src/main/web/i18n.json b/pollen-ui-riot-js/src/main/web/i18n.json index a729fe70..8f847fa7 100644 --- a/pollen-ui-riot-js/src/main/web/i18n.json +++ b/pollen-ui-riot-js/src/main/web/i18n.json @@ -350,10 +350,9 @@ "poll_created_editUrl": "Gérer le sondage", "poll_created_voteUrl": "Voter", "users_title": "Les utilisateurs", - "users_sort": "Trier par", - "users_name": "Nom", - "users_email": "Courriel", "users_noUser": "Aucun utilisateur", + "users_one": "utilisateur", + "users_many": "utilisateurs", "user_banned": "Banni", "user_emailValidate": "En cours de validation", "user_administrator": "Administrateur", @@ -854,10 +853,9 @@ "poll_created_editUrl": "Manage poll", "poll_created_voteUrl": "Vote on poll", "users_title": "All users", - "users_sort": "Sort by", - "users_name": "Name", - "users_email": "Email", "users_noUser": "No user", + "users_one": "user", + "users_many": "users", "user_banned": "banned", "user_emailValidate": "email validation waiting", "user_administrator": "Administrator", diff --git a/pollen-ui-riot-js/src/main/web/js/UserService.js b/pollen-ui-riot-js/src/main/web/js/UserService.js index 13f41cb1..e614ce68 100644 --- a/pollen-ui-riot-js/src/main/web/js/UserService.js +++ b/pollen-ui-riot-js/src/main/web/js/UserService.js @@ -31,9 +31,11 @@ class UserService extends FetchService { return url; } - users(pagination) { + users(pagination, search) { + let params = Object.assign({}, pagination); + params.search = search || ""; let url = this._getUrlPrefix(); - return this.get(url, pagination); + return this.get(url, params); } user(userId, permission) { diff --git a/pollen-ui-riot-js/src/main/web/tag/UserCard.tag.html b/pollen-ui-riot-js/src/main/web/tag/UserCard.tag.html index f10dd637..a1db6823 100644 --- a/pollen-ui-riot-js/src/main/web/tag/UserCard.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/UserCard.tag.html @@ -1,66 +1,42 @@ -require("./components/HumanInput.tag.html"); -<UserCard class="card"> - <div class="user-card-view" if={!editing}> - <div class="login"> - <span> - {opts.user.name} - {opts.user.email} - <i class="fa fa-refresh" if={!user.emailIsValidate} title={__.emailValidate}></i> - <i class="fa fa-ban" if={user.banned} title={__.banned}></i> - <i class="fa fa-cog" if={user.administrator} title={__.administrator}></i> - </span> - </div> - - <div class="flags"> - - </div> - - <div class="actions"> - <i class="fa fa-pencil-square-o" - title={__.edit} - onclick={startEdition}></i> - <i class="fa fa-ban" - if={!user.isBanned} - title={__.banUser} - onclick={banUser}></i> - <i class="fa fa-times" - title={__.deleteUser} - onclick={deleteUser}></i> - </div> - - </div> +require("./components/Card.tag.html"); +require("./UserEditModal.tag.html"); +<UserCard> + + <Card name={opts.user.name} + ondelete={delete} + onedit={edit} + class="user-card"> + <yield to="actions"> + <a class="warning" + if={!parent.opts.user.banned} + title={parent.__.banUser} + onclick={parent.banUser}> + <i class="fa fa-ban"></i> + </a> + </yield> + <yield to="detail"> + <div class="user-email"> + {parent.opts.user.email} + <i class="fa fa-refresh" if={!parent.opts.user.emailIsValidate} title={parent.__.emailValidate}></i> + </div> + <div class="user-badges"> + <span class="c-badge c-badge--rounded c-badge--success" + if={parent.opts.user.administrator}> + <i class="fa fa-cog"/> {parent.__.administrator} + </span> + <span class="c-badge c-badge--rounded c-badge--error" + if={parent.opts.user.banned}> + <i class="fa fa-ban"/> {parent.__.banned} + </span> + </div> + </yield> + </card> + + <UserEditModal ref="editModal" + user={opts.user}/> <form class="user-card-edit" if={editing}> <HumanInput onsubmit={saveUser}/> - <div class="o-form-element"> - <label class="c-label" for="name">{__.name}</label> - <input type="text" - id="name" - ref="name" - class="c-field" - value={opts.user.name}> - </div> - <div class="o-form-element"> - <label class="c-label" for="name">{__.email}</label> - <input type="email" - id="email" - ref="email" - required - class="c-field {c-field--error: errors.email}" - value={opts.user.email}> - <div class="c-hint c-hint--static c-hint--error" each={error in errors.email}>{error}</div> - </div> - <div class="o-form-element"> - <label class="c-toggle c-toggle--info"> - {__.administrator} - <input type="checkbox" - id="administrator" - ref="administrator" - checked={opts.user.administrator}> - <div class="c-toggle__track"> - <div class="c-toggle__handle"></div> - </div> - </label> - </div> <div class="actions"> <div class="actions-left"> <button type="button" @@ -106,7 +82,20 @@ require("./components/HumanInput.tag.html"); }); }; - this.deleteUser = () => { + this.edit = () => { + this.refs.editModal.open().then(() => { + if (this.opts.onUserChange) { + this.opts.onUserChange(); + } else { + this.update(); + } + }, () => { + this.update(); + }); + this.update(); + }; + + this.delete = () => { this.confirm(this.__.deleteUserMessage).then((confirm) => { if (confirm) { userService.deleteUser(this.opts.user.id).then(() => { @@ -116,49 +105,25 @@ require("./components/HumanInput.tag.html"); }); }; - this.saveUser = e => { - e.preventDefault(); - e.stopPropagation(); - this.user.name = this.refs.name.value; - this.user.email = this.refs.email.value; - this.user.administrator = this.refs.administrator.checked; - - userService.saveUser(this.user).then(() => { - this.errors = {}; - this.cancelEdition(); - this.opts.onUserChange(); - }, errors => { - this.errors = errors; - this.update(); - }); - }; + </script> <style> - .user-card-view { - display: flex; - justify-content: space-between; - } - - .user-card-edit .o-form-element .c-label:first-child { - width: 25%; - display: inline-block; - text-align: right; - float: left; - padding-top: 0.5em; - padding-right: 5px; - } - - .user-card-edit .o-form-element .c-field { - width: 75%; - display: inline-block; - } - - .user-card-edit .o-form-element .c-toggle, - .user-card-edit .o-form-element .c-hint { - margin-left: 25%; - } + .user-email { + width: 100%; + white-space: nowrap; + overflow: hidden; + text-align: center; + text-overflow: ellipsis; + margin: 10px 0; + } + + .user-badges { + width: 100%; + display: flex; + justify-content: space-around; + } </style> </UserCard> diff --git a/pollen-ui-riot-js/src/main/web/tag/UserEditModal.tag.html b/pollen-ui-riot-js/src/main/web/tag/UserEditModal.tag.html new file mode 100644 index 00000000..7c2eb995 --- /dev/null +++ b/pollen-ui-riot-js/src/main/web/tag/UserEditModal.tag.html @@ -0,0 +1,73 @@ +require("./popup/Modal.tag.html"); + +<UserEditModal> + <Modal ref="modal" + header={__.edit} + label={__.save} + type="success" + onsubmit={save}> + <div class="o-form-element"> + <label class="c-label" for="name">{parent.__.name}</label> + <input type="text" + id="name" + ref="name" + class="c-field" + value={parent.opts.user.name}> + </div> + <div class="o-form-element"> + <label class="c-label" for="name">{parent.__.email}</label> + <input type="email" + id="email" + ref="email" + required + class="c-field {c-field--error: parent.errors.email}" + value={parent.opts.user.email}> + <div class="c-hint c-hint--static c-hint--error" each={error in parent.errors.email}>{error}</div> + </div> + <div class="o-form-element"> + <label class="c-toggle c-toggle--info"> + {parent.__.administrator} + <input type="checkbox" + id="administrator" + ref="administrator" + checked={parent.opts.user.administrator}> + <div class="c-toggle__track"> + <div class="c-toggle__handle"></div> + </div> + </label> + </div> + + </Modal> + + <script type="es6"> + let session = require("../js/Session"); + let Message = require("../js/Message"); + this.installBundle(session, "user"); + let userService = require("../js/UserService"); + + this.errors = {}; + + this.open = () => { + return this.refs.modal.open(); + }; + + this.save = () => { + let user2 = Object.assign({}, this.opts.user); + user2.name = this.refs.modal.refs.name.value; + user2.email = this.refs.modal.refs.email.value; + user2.administrator = this.refs.modal.refs.administrator.checked; + + return userService.saveUser(user2).then(() => { + this.errors = {}; + this.update(); + }, errors => { + this.errors = errors; + this.bus.trigger("message", new Message(errors, "error")); + this.update(); + return Promise.reject(); + }); + }; + + </script> + +</UserEditModal> diff --git a/pollen-ui-riot-js/src/main/web/tag/Users.tag.html b/pollen-ui-riot-js/src/main/web/tag/Users.tag.html index 61bfb85c..c37775e2 100644 --- a/pollen-ui-riot-js/src/main/web/tag/Users.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/Users.tag.html @@ -1,38 +1,27 @@ -require("./Pagination.tag.html"); +require("./components/LazyLoad.tag.html"); +require("./components/LoadingCard.tag.html"); +require("./components/Search.tag.html"); require("./UserCard.tag.html"); <Users> - <div class="container" > - <div show="{loaded}"> - <h1 class="c-heading">{__.title}</h1> + <div class="container" show="{loaded}"> + <h1 class="c-heading">{__.title}</h1> - <div show={users.length > 0} > - <div class="o-form-element sort"> - <label class="c-label" for="sort">{__.sort}</label> - <div class="c-input-group"> - <div class="o-field"> - <select class="c-field" - onchange={selectSort}> - <option value="name">{__.name}</option> - <option value="email">{__.email}</option> - </select> - </div> - <button class="c-button c-button--brand" - onclick={toggleSort}> - <i class="fa {pagination.desc ? 'fa-sort-amount-desc' : 'fa-sort-amount-asc'}"></i> - </button> - </div> - </div> - - <Pagination pagination={pagination} onchange={refresh}/> - - <UserCard each={user in users} user={user} on-user-change={parent.refresh}/> - - <Pagination pagination={pagination} onchange={refresh}/> - </div> - <div show={users.length === 0} class="c-alert c-alert--info"> - {__.noUser} - </div> + <div class="c-alert c-alert--info"> + {count === 0 ? __.noUser : (count + " " + (count === 1 ? __.one : __.many))} </div> + + <Search onsearch="{refresh}" search="{search}" /> + + <LazyLoad pagination={pagination} onload={lazyLoad} load-size="20" ref="lazyLoad" class="elements"> + <yield to="element"> + <UserCard user={element} on-user-change={parent.parent.refresh}/> + </yield> + <yield to="loading"> + <LoadingCard loading={nbNextGroup}> + {parent.parent._l("loading", nbNext)} + </LoadingCard> + </yield> + </LazyLoad> </div> <script type="es6"> @@ -43,54 +32,34 @@ require("./UserCard.tag.html"); this.pagination = { order: "name", desc: false, - pageSize: 5, + pageSize: -1, pageNumber: 0 }; - this.users = []; + this.search = {value: ""}; let userService = require("../js/UserService"); this.refresh = () => { - return userService.users(this.pagination).then((result) => { - this.users = result.elements; - Object.assign(this.pagination, result.pagination); - this.loaded = true; - this.update(); - return result; - }); - }; - - this.refresh(); - - this.toggleSort = () => { - this.pagination.desc = !this.pagination.desc; - this.refresh(); + this.refs.lazyLoad.reload(); }; - this.selectSort = (e) => { - this.pagination.order = e.target.value; - this.refresh(); + this.lazyLoad = pagination => { + return userService.users(pagination, this.search.value).then((result) => { + if (!this.loaded) { + this.count = result.pagination.count; + this.loaded = true; + this.update(); + } + return result; + }); }; </script> <style> - - .c-heading { - text-align: center; - } - - .o-form-element.sort .c-label:first-child { - width: 70px; - display: inline-block; - text-align: right; - float: left; - padding-top: 0.5em; - padding-right: 5px; - } - - .o-form-element.sort .c-input-group { - width: 200px; + .elements { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; } - </style> </Users> diff --git a/pollen-ui-riot-js/src/main/web/tag/components/Card.tag.html b/pollen-ui-riot-js/src/main/web/tag/components/Card.tag.html index 7e225428..59d80110 100644 --- a/pollen-ui-riot-js/src/main/web/tag/components/Card.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/components/Card.tag.html @@ -7,6 +7,7 @@ require("./LetterAvatar.tag.html"); onclick={opts.onedit}> <i class="fa fa-pencil"></i> </a> + <yield from="actions"/> <a class="error" title={parent.__.delete} onclick={opts.ondelete}> @@ -29,8 +30,7 @@ require("./LetterAvatar.tag.html"); </div> <div class="card-detail"> - <yield/> - + <yield from="detail"/> </div> <div class="card-weight" if={opts.weight}> @@ -46,6 +46,7 @@ require("./LetterAvatar.tag.html"); padding: 10px 0; margin: 10px; box-shadow: 0 0 10px hsla(0,0%,7%,.6); + height: 207px; width: 180px; overflow: hidden; } @@ -54,7 +55,7 @@ require("./LetterAvatar.tag.html"); position: absolute; top: 0; right: 0; - transform: translate3d(32px, 0px, 0px); + transform: translate3d(100%, 0px, 0px); transition: transform 0.6s; } diff --git a/pollen-ui-riot-js/src/main/web/tag/favoriteList/ChildListCard.tag.html b/pollen-ui-riot-js/src/main/web/tag/favoriteList/ChildListCard.tag.html index f9555658..fdd7fba8 100644 --- a/pollen-ui-riot-js/src/main/web/tag/favoriteList/ChildListCard.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/favoriteList/ChildListCard.tag.html @@ -9,13 +9,15 @@ require("./ChildListEditModal.tag.html"); onedit={edit} href="#favoriteLists/{opts.childList.child.id}" class="list-card"> - <div> - {parent.opts.childList.child.countChildren} <i class="fa fa-users"></i> - </div> - <div> - {parent.opts.childList.child.countMembers} <i class="fa fa-user"></i> - </div> - </card> + <yield to="detail"> + <div> + {parent.opts.childList.child.countChildren} <i class="fa fa-users"></i> + </div> + <div> + {parent.opts.childList.child.countMembers} <i class="fa fa-user"></i> + </div> + </yield> + </Card> <ChildListEditModal ref="editModal" child-list={opts.childList} diff --git a/pollen-ui-riot-js/src/main/web/tag/favoriteList/FavoriteListCard.tag.html b/pollen-ui-riot-js/src/main/web/tag/favoriteList/FavoriteListCard.tag.html index b79361e7..a17c0d81 100644 --- a/pollen-ui-riot-js/src/main/web/tag/favoriteList/FavoriteListCard.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/favoriteList/FavoriteListCard.tag.html @@ -7,12 +7,14 @@ require("./FavoriteListEditModal.tag.html"); ondelete={delete} onedit={edit} class="list-card"> - <div> - {parent.opts.favoriteList.countChildren} <i class="fa fa-users"></i> - </div> - <div> - {parent.opts.favoriteList.countMembers} <i class="fa fa-user"></i> - </div> + <yield to="detail"> + <div> + {parent.opts.favoriteList.countChildren} <i class="fa fa-users"></i> + </div> + <div> + {parent.opts.favoriteList.countMembers} <i class="fa fa-user"></i> + </div> + </yield> </Card> <FavoriteListEditModal ref="editModal" diff --git a/pollen-ui-riot-js/src/main/web/tag/favoriteList/MemberCard.tag.html b/pollen-ui-riot-js/src/main/web/tag/favoriteList/MemberCard.tag.html index 2a4cfe8a..2f29dc96 100644 --- a/pollen-ui-riot-js/src/main/web/tag/favoriteList/MemberCard.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/favoriteList/MemberCard.tag.html @@ -7,9 +7,11 @@ require("./MemberEditModal.tag.html"); ondelete={delete} onEdit={edit} class="member-card"> - <span title={parent.opts.member.email}> - {parent.opts.member.email} - </span> + <yield to="detail"> + <span title={parent.opts.member.email}> + {parent.opts.member.email} + </span> + </yield> </Card> <MemberEditModal ref="editModal" diff --git a/pollen-ui-riot-js/src/main/web/tag/poll/Polls.tag.html b/pollen-ui-riot-js/src/main/web/tag/poll/Polls.tag.html index 396f4035..296de2a4 100644 --- a/pollen-ui-riot-js/src/main/web/tag/poll/Polls.tag.html +++ b/pollen-ui-riot-js/src/main/web/tag/poll/Polls.tag.html @@ -1,7 +1,7 @@ require("./PollCard.tag.html"); require("../components/LazyLoad.tag.html"); require("../components/LoadingCard.tag.html"); -require("../components/HumanInput.tag.html"); +require("../components/Search.tag.html"); <Polls> <div class="container" show="{loaded}"> <h1>{__[opts.method]}</h1> @@ -60,7 +60,6 @@ require("../components/HumanInput.tag.html"); pageSize: -1, pageNumber: 0 }; - this.polls = []; this.search = {value: ""}; let pollService = require("../../js/PollService"); -- To stop receiving notification emails like this one, please contact chorem.org SCM administrator <admin+scm@chorem.org>.