From 6592b792d21b4e4e74f5ca6983bfef1d87dc3379 Mon Sep 17 00:00:00 2001 From: Eduardo Teixeira Date: Mon, 22 Dec 2025 17:23:44 +0000 Subject: [PATCH] added pagination to AIP and File sideBar, for representations and disseminations. --- .../org/roda/core/data/utils/SorterUtils.java | 23 +++ .../org/roda/wui/client/browse/BrowseAIP.java | 4 +- .../roda/wui/client/browse/BrowseFile.java | 3 +- .../client/browse/BrowseRepresentation.java | 6 +- .../cards/AIPDisseminationCardList.java | 9 +- .../cards/AIPRepresentationCardList.java | 2 + .../cards/FileDisseminationCardList.java | 8 +- .../RepresentationDisseminationCardList.java | 7 +- .../common/cards/ThumbnailCardList.java | 137 ++++++++++++++---- .../common/cards/ThumbnailCardList.ui.xml | 25 +++- .../roda/wui/client/common/resources/main.gss | 45 +++++- 11 files changed, 221 insertions(+), 48 deletions(-) create mode 100644 roda-common/roda-common-data/src/main/java/org/roda/core/data/utils/SorterUtils.java diff --git a/roda-common/roda-common-data/src/main/java/org/roda/core/data/utils/SorterUtils.java b/roda-common/roda-common-data/src/main/java/org/roda/core/data/utils/SorterUtils.java new file mode 100644 index 0000000000..e3b8e114e7 --- /dev/null +++ b/roda-common/roda-common-data/src/main/java/org/roda/core/data/utils/SorterUtils.java @@ -0,0 +1,23 @@ +package org.roda.core.data.utils; + +import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.v2.index.sort.SortParameter; +import org.roda.core.data.v2.index.sort.Sorter; + +/** + * + * @author Eduardo Teixeira + */ +public class SorterUtils { + public static Sorter dipsDefault(){ + return new Sorter(new SortParameter(RodaConstants.DIP_ID, false)); + } + + public static Sorter representationDefault(){ + return new Sorter(new SortParameter(RodaConstants.REPRESENTATION_ID, false)); + } + + public static Sorter filesDefault(){ + return new Sorter(new SortParameter(RodaConstants.FILE_ORIGINALNAME, false)); + } +} diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java index 0e04be65fc..b23c196d24 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseAIP.java @@ -17,9 +17,11 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; + import org.roda.core.data.common.RodaConstants; import org.roda.core.data.exceptions.AuthorizationDeniedException; import org.roda.core.data.exceptions.NotFoundException; +import org.roda.core.data.utils.SorterUtils; import org.roda.core.data.v2.generics.LongResponse; import org.roda.core.data.v2.index.CountRequest; import org.roda.core.data.v2.index.FindRequest; @@ -235,7 +237,7 @@ public void onSuccess(Actionable.ActionImpact impact) { if (response.getDipCount().getResult() > 0) { showSidePanel = true; - this.disseminationCards.add(new AIPDisseminationCardList(aipId)); + this.disseminationCards.add(new AIPDisseminationCardList(aipId, SorterUtils.dipsDefault(), response.getDipCount().getResult().intValue())); } this.sidePanel.setVisible(showSidePanel); diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseFile.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseFile.java index d1c1d55984..9f7b2187df 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseFile.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseFile.java @@ -17,6 +17,7 @@ import java.util.concurrent.CompletableFuture; import org.roda.core.data.common.RodaConstants; +import org.roda.core.data.utils.SorterUtils; import org.roda.core.data.v2.generics.LongResponse; import org.roda.core.data.v2.index.CountRequest; import org.roda.core.data.v2.index.IndexedFileRequest; @@ -278,7 +279,7 @@ public BrowseFile(Viewers viewers, final BrowseFileResponse response, IndexedFil } else { if (longResponse.getResult() > 0) { this.disseminationCards.add(new FileDisseminationCardList(response.getIndexedAIP().getId(), - response.getIndexedRepresentation().getId(), indexedFile.getId(), indexedFile.getUUID())); + response.getIndexedRepresentation().getId(), indexedFile.getId(), indexedFile.getUUID(), SorterUtils.dipsDefault(),longResponse.getResult().intValue())); } else { this.sidePanel.setVisible(false); } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseRepresentation.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseRepresentation.java index 0a9c3e4c8a..2bc3ff8d4f 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseRepresentation.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/browse/BrowseRepresentation.java @@ -19,10 +19,10 @@ import org.roda.core.data.common.RodaConstants; import org.roda.core.data.exceptions.AuthorizationDeniedException; +import org.roda.core.data.utils.SorterUtils; import org.roda.core.data.v2.generics.LongResponse; import org.roda.core.data.v2.index.CountRequest; import org.roda.core.data.v2.index.IndexedRepresentationRequest; -import org.roda.core.data.v2.index.filter.AndFiltersParameters; import org.roda.core.data.v2.index.filter.Filter; import org.roda.core.data.v2.index.filter.SimpleFilterParameter; import org.roda.core.data.v2.ip.AIPState; @@ -30,8 +30,6 @@ import org.roda.core.data.v2.ip.IndexedDIP; import org.roda.core.data.v2.ip.IndexedRepresentation; import org.roda.core.data.v2.ip.metadata.DescriptiveMetadataInfos; -import org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent; -import org.roda.core.data.v2.risks.RiskIncidence; import org.roda.wui.client.browse.tabs.BrowseRepresentationTabs; import org.roda.wui.client.common.BrowseRepresentationActionsToolbar; import org.roda.wui.client.common.LastSelectedItemsSingleton; @@ -195,7 +193,7 @@ public BrowseRepresentation(BrowseRepresentationResponse response) { // CARDS if (dipCounterResponse.getResult() > 0) { - this.disseminationCards.add(new RepresentationDisseminationCardList(aipId, repId)); + this.disseminationCards.add(new RepresentationDisseminationCardList(aipId, repId, SorterUtils.representationDefault(), dipCounterResponse.getResult().intValue())); } else { this.sidePanel.setVisible(false); } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/AIPDisseminationCardList.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/AIPDisseminationCardList.java index 34d917b208..5451fdc784 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/AIPDisseminationCardList.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/AIPDisseminationCardList.java @@ -12,10 +12,10 @@ import java.util.List; import java.util.Map; -import com.google.gwt.core.client.GWT; import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.index.filter.Filter; import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.index.sort.Sorter; import org.roda.core.data.v2.ip.IndexedDIP; import org.roda.wui.client.browse.BrowseDIP; import org.roda.wui.client.common.cards.utils.CardBuilder; @@ -35,7 +35,7 @@ * @author Alexandre Flores */ public class AIPDisseminationCardList extends ThumbnailCardList { - public AIPDisseminationCardList(String aipId) { + public AIPDisseminationCardList(String aipId, Sorter sorter, int cardsTotal) { super(messages.someOfAObject(IndexedDIP.class.getName()), ConfigurationManager.getString(RodaConstants.UI_ICONS_CLASS, IndexedDIP.class.getSimpleName()), IndexedDIP.class, new Filter(new SimpleFilterParameter(RodaConstants.DIP_ALL_AIP_UUIDS, aipId)), new CardBuilder() { @@ -66,6 +66,9 @@ public ThumbnailCard constructCard(ClientMessages messages, IndexedDIP dip) { return new ThumbnailCard(title, iconThumbnailHTML, tags, attributes, thumbnailClickHandler); } - }); + }, sorter); + withPagination(5, cardsTotal); + // Initialize + refresh(); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/AIPRepresentationCardList.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/AIPRepresentationCardList.java index 17c2826e2b..fe9181ab17 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/AIPRepresentationCardList.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/AIPRepresentationCardList.java @@ -77,5 +77,7 @@ public ThumbnailCard constructCard(ClientMessages messages, IndexedRepresentatio return new ThumbnailCard(title, iconThumbnailHTML, tags, attributes, thumbnailClickHandler); } }); + // Initialize + refresh(); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/FileDisseminationCardList.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/FileDisseminationCardList.java index 9e30d21c37..cadb36605d 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/FileDisseminationCardList.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/FileDisseminationCardList.java @@ -15,6 +15,7 @@ import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.index.filter.Filter; import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.index.sort.Sorter; import org.roda.core.data.v2.ip.IndexedDIP; import org.roda.wui.client.browse.BrowseDIP; import org.roda.wui.client.common.cards.utils.CardBuilder; @@ -34,7 +35,7 @@ * @author Alexandre Flores */ public class FileDisseminationCardList extends ThumbnailCardList { - public FileDisseminationCardList(String aipId, String representationId, String fileId, String fileUUID) { + public FileDisseminationCardList(String aipId, String representationId, String fileId, String fileUUID, Sorter sorter, int cardsTotal) { super(messages.someOfAObject(IndexedDIP.class.getName()), ConfigurationManager.getString(RodaConstants.UI_ICONS_CLASS, IndexedDIP.class.getSimpleName()), IndexedDIP.class, new Filter(new SimpleFilterParameter(RodaConstants.DIP_FILE_UUIDS, fileUUID)), new CardBuilder() { @@ -65,6 +66,9 @@ public ThumbnailCard constructCard(ClientMessages messages, IndexedDIP dip) { return new ThumbnailCard(title, iconThumbnailHTML, tags, attributes, thumbnailClickHandler); } - }); + }, sorter); + withPagination(5, cardsTotal); + // Initialize + refresh(); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/RepresentationDisseminationCardList.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/RepresentationDisseminationCardList.java index 9ffef1e096..b1d037bbbc 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/RepresentationDisseminationCardList.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/RepresentationDisseminationCardList.java @@ -15,6 +15,7 @@ import org.roda.core.data.common.RodaConstants; import org.roda.core.data.v2.index.filter.Filter; import org.roda.core.data.v2.index.filter.SimpleFilterParameter; +import org.roda.core.data.v2.index.sort.Sorter; import org.roda.core.data.v2.ip.IndexedDIP; import org.roda.wui.client.browse.BrowseDIP; import org.roda.wui.client.common.cards.utils.CardBuilder; @@ -34,7 +35,7 @@ * @author Alexandre Flores */ public class RepresentationDisseminationCardList extends ThumbnailCardList { - public RepresentationDisseminationCardList(String aipId, String representationId) { + public RepresentationDisseminationCardList(String aipId, String representationId, Sorter sorter, int cardsTotal) { super(messages.someOfAObject(IndexedDIP.class.getName()), ConfigurationManager.getString(RodaConstants.UI_ICONS_CLASS, IndexedDIP.class.getSimpleName()), IndexedDIP.class, new Filter(new SimpleFilterParameter(RodaConstants.DIP_REPRESENTATION_IDS, representationId)), @@ -66,6 +67,8 @@ public ThumbnailCard constructCard(ClientMessages messages, IndexedDIP dip) { return new ThumbnailCard(title, iconThumbnailHTML, tags, attributes, thumbnailClickHandler); } - }); + }, sorter); + // Initialize + refresh(); } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/ThumbnailCardList.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/ThumbnailCardList.java index 1c07f1f58f..02925be0cd 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/ThumbnailCardList.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/ThumbnailCardList.java @@ -9,13 +9,13 @@ import java.util.ArrayList; -import org.roda.core.data.common.RodaConstants; +import com.google.gwt.user.client.ui.HTML; import org.roda.core.data.v2.index.FindRequest; import org.roda.core.data.v2.index.IndexResult; import org.roda.core.data.v2.index.IsIndexed; import org.roda.core.data.v2.index.filter.Filter; -import org.roda.core.data.v2.index.filter.SimpleFilterParameter; -import org.roda.core.data.v2.ip.AIPState; +import org.roda.core.data.v2.index.sort.Sorter; +import org.roda.core.data.v2.index.sublist.Sublist; import org.roda.wui.client.common.cards.utils.CardBuilder; import org.roda.wui.client.common.labels.Header; import org.roda.wui.client.services.Services; @@ -30,6 +30,7 @@ import com.google.gwt.user.client.ui.Widget; import config.i18n.client.ClientMessages; +import org.roda.wui.common.client.widgets.wcag.AccessibleFocusPanel; /** * @@ -46,68 +47,76 @@ public class ThumbnailCardList extends Composite { FlowPanel cardsPanel; @UiField - FlowPanel buttons; + FlowPanel pagination; + + @UiField + AccessibleFocusPanel prev; + + @UiField + AccessibleFocusPanel next; + + @UiField + HTML pageInfo; private static final ClientLogger LOGGER = new ClientLogger(ThumbnailCardList.class.getName()); protected final ArrayList cards; - private final FindRequest findRequest; + private final FindRequest baseRequest; private final Class objectClass; private IndexResult results; - private Filter resultsFilter; private CardBuilder cardBuilder; + // pagination + private int pageSize = 1; + private int pageIndex = 0; + private long total = -1; + private boolean paged = false; + protected ThumbnailCardList() { // UI Elements - initWidget(uiBinder.createAndBindUi(this)); + initUI(); this.objectClass = null; this.results = null; - this.resultsFilter = null; - this.findRequest = null; + this.baseRequest = null; this.cards = new ArrayList<>(); } public ThumbnailCardList(String title, String icon, Class objectClass, FindRequest findRequest, CardBuilder cardBuilder) { // UI Elements - initWidget(uiBinder.createAndBindUi(this)); - this.title.setHeaderStyleName("noMargin"); - this.title.setHeaderText(title); - this.title.setLevel(5); - this.title.setIcon(icon); + initUI(); + setHeaderAndIcon(title, icon); // Data this.objectClass = objectClass; this.results = new IndexResult<>(); - this.findRequest = findRequest; + this.baseRequest = findRequest; this.cardBuilder = cardBuilder; this.cards = new ArrayList<>(); - // Initialize - refresh(); } public ThumbnailCardList(String title, String icon, Class objectClass, Filter resultsFilter, - CardBuilder cardBuilder) { + CardBuilder cardBuilder, Sorter sorter) { // UI Elements - initWidget(uiBinder.createAndBindUi(this)); - this.title.setHeaderStyleName("noMargin"); - this.title.setHeaderText(title); - this.title.setLevel(5); - this.title.setIcon(icon); + initUI(); + setHeaderAndIcon(title, icon); // Data this.objectClass = objectClass; this.results = new IndexResult<>(); - if (resultsFilter != null) { - this.resultsFilter = resultsFilter; - } - this.findRequest = FindRequest.getBuilder(resultsFilter, true).build(); + this.baseRequest = FindRequest.getBuilder(resultsFilter, true).withSorter(sorter).build(); this.cardBuilder = cardBuilder; this.cards = new ArrayList<>(); - // Initialize - refresh(); + } + + private void initUI() { + initWidget(uiBinder.createAndBindUi(this)); + this.pagination.setVisible(false); + + this.prev.addClickHandler(e -> goToPage(this.pageIndex - 1)); + this.next.addClickHandler(e -> goToPage(this.pageIndex + 1)); } public void refresh() { @@ -119,12 +128,19 @@ private void clearCards() { this.cardsPanel.clear(); } + private void setHeaderAndIcon(String title, String icon) { + this.title.setHeaderStyleName("noMargin"); + this.title.setHeaderText(title); + this.title.setLevel(5); + this.title.setIcon(icon); + } + private void updateResultsAndRebuildCards() { this.cards.clear(); this.cardsPanel.clear(); Services services = new Services("Retrieve AIP representations or dissemination", "get"); - services.rodaEntityRestService(s -> s.find(findRequest, LocaleInfo.getCurrentLocale().getLocaleName()), objectClass) - .whenComplete((requestResults, error) -> { + services.rodaEntityRestService(s -> s.find(buildFindRequest(), LocaleInfo.getCurrentLocale().getLocaleName()), + objectClass).whenComplete((requestResults, error) -> { if (error == null) { this.results = requestResults; for (T object : this.results.getResults()) { @@ -142,6 +158,65 @@ private void updateResultsAndRebuildCards() { }); } + // pagination + private FindRequest buildFindRequest() { + FindRequest.FindRequestBuilder fr = FindRequest + .getBuilder(this.baseRequest.getFilter(), this.baseRequest.isOnlyActive()) + .withSorter(this.baseRequest.getSorter()); + if (this.paged) + fr.withSublist(new Sublist(this.pageIndex * this.pageSize, this.pageSize)); + else if (this.baseRequest.getSublist() != null) { + fr.withSublist(this.baseRequest.getSublist()); + } + return fr.build(); + } + + private void goToPage(int newIndex) { + if (!this.paged) + return; + int maxPage = (int) Math.max(0, (long) Math.ceil((double) total / this.pageSize) - 1); + this.pageIndex = Math.max(0, Math.min(newIndex, maxPage)); + updatePaginationState(); + refresh(); + } + + private void updatePaginationState() { + if (!this.paged) + return; + + int totalPages = (int) Math.max(1, Math.ceil((double) this.total / this.pageSize)); + this.pageInfo.setHTML("" + (this.pageIndex + 1) + " / " + totalPages + ""); + + boolean showPrev = this.pageIndex > 0; + boolean showNext = this.pageIndex < totalPages - 1; + setHidden(this.prev, !showPrev); + setHidden(this.next, !showNext); + } + + public void withPagination(int pageSize, long total) { + this.pageSize = Math.max(1, pageSize); + this.total = total; + this.pageIndex = 0; + if (total <= this.pageSize) { + this.paged = false; + this.pagination.setVisible(false); + this.pageInfo.setHTML(""); + setHidden(prev, true); + setHidden(next, true); + return; + } + this.paged = true; + this.pagination.setVisible(true); + + updatePaginationState(); + } + + private void setHidden(Widget w, boolean hidden){ + if (hidden){ + w.addStyleName("isHidden"); + }else w.removeStyleName("isHidden"); + } + interface MyUiBinder extends UiBinder> { } } diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/ThumbnailCardList.ui.xml b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/ThumbnailCardList.ui.xml index 9169fe8542..cfb9a8bde2 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/ThumbnailCardList.ui.xml +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/cards/ThumbnailCardList.ui.xml @@ -1,13 +1,32 @@ + xmlns:commonlabels="urn:import:org.roda.wui.client.common.labels" + xmlns:wcag="urn:import:org.roda.wui.common.client.widgets.wcag"> - + + + + + + + + + + + + + + + + + + + + - diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss index 0ef824225c..9455266b4e 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/client/common/resources/main.gss @@ -3314,9 +3314,10 @@ td.datePickerMonth, td.datePickerYear { flex-direction: column; padding: 24px 32px; background: COLOR_GREY_DARK; - gap: 32px; + gap: 12px; } + .browseSidePanel .actionable-menu { display: flex; flex-direction: column; @@ -3351,11 +3352,53 @@ td.datePickerMonth, td.datePickerYear { gap: 12px; } +.thumbnailCardListHeader{ + display: flex; + align-items: center; + justify-content: space-between; +} + .thumbnailCardListTitle { color: #CCCCCC; text-transform: capitalize; } +.thumbnailCardListButtons{ + display: flex; + align-items: center; + color: #CCCCCC; + +} + +.thumbnailCardListButtons .paginationBtn{ + display: inline-flex; + align-items: center; + justify-content: center; + width: 18px; + height: 18px; + flex: 0 0 18px; + cursor: pointer; +} + +.thumbnailCardListButtons .paginationBtn.isHidden{ + visibility: hidden; + pointer-events: none; +} + +.thumbnailCardListButtons .paginationBtn:hover{ + color: #CCCCCC; + background-color: COLOR_GREY_VERYLIGHT; + border-radius: 13px; +} + +.thumbnailCardListButtons .paginationInfo { + font-size: 13px; + text-align: center; + padding: 0 2px; + min-width: 20px; + +} + .thumbnailCardListCards { display: flex; flex-direction: column;