/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.rrm.ehour.ui.report.panel; import com.google.common.base.Joiner; import com.google.common.base.Optional; import net.rrm.ehour.config.EhourConfig; import net.rrm.ehour.data.DateRange; import net.rrm.ehour.report.criteria.UserSelectedCriteria; import net.rrm.ehour.report.reports.ReportData; import net.rrm.ehour.ui.common.border.GreyBlueRoundedBorder; import net.rrm.ehour.ui.common.component.CurrencyLabel; import net.rrm.ehour.ui.common.component.HoverPagingNavigator; import net.rrm.ehour.ui.common.model.DateModel; import net.rrm.ehour.ui.common.panel.AbstractBasePanel; import net.rrm.ehour.ui.common.report.*; import net.rrm.ehour.ui.common.session.EhourWebSession; import net.rrm.ehour.ui.common.util.HtmlUtil; import net.rrm.ehour.ui.common.wicket.Container; import net.rrm.ehour.ui.report.GreyReportBorder; import net.rrm.ehour.ui.report.model.TreeReportDataProvider; import net.rrm.ehour.ui.report.model.TreeReportElement; import net.rrm.ehour.ui.report.model.TreeReportModel; import org.apache.commons.lang.StringUtils; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.event.IEvent; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.border.Border; import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.RepeatingView; import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.IDataProvider; import org.apache.wicket.model.Model; import org.apache.wicket.model.ResourceModel; import org.apache.wicket.model.StringResourceModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import org.apache.wicket.util.convert.IConverter; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * Aggregate report data panel */ public class TreeReportDataPanel extends AbstractBasePanel<ReportData> { private static final long serialVersionUID = -6757047600645464803L; private static final AttributeModifier CSS_ALIGN_RIGHT = AttributeModifier.replace("style", "text-align: right;"); private static final String REPORT_CONTENT_ID = "reportContent"; private static final String REPORT_TABLE_ID = "reportTable"; private static final String NAVIGATOR_ID = "navigator"; private ReportConfig reportConfig; private UserSelectedCriteria criteria; private HoverPagingNavigator pagingNavigator; private Container reportFrameContainer; private Fragment reportFragment; public TreeReportDataPanel(String id, TreeReportModel reportModel, ReportConfig reportConfig, ExcelReport excelReport) { super(id, reportModel); setOutputMarkupId(true); this.reportConfig = reportConfig; criteria = reportModel.getReportCriteria().getUserSelectedCriteria(); Border outerFrame = new GreyReportBorder("greyFrame"); add(outerFrame); outerFrame.add(createReportHeaderLabel("reportHeader", reportModel.getReportRange(), EhourWebSession.getEhourConfig())); outerFrame.add(createReportContent(REPORT_CONTENT_ID, reportModel, excelReport)); } // Report period: 10/1/13 - 12/31/13 label private Label createReportHeaderLabel(String id, DateRange reportRange, EhourConfig config) { Label reportHeaderLabel = new Label(id, new StringResourceModel("report.header", this, null, new Object[]{new DateModel(reportRange.getDateStart(), config), new DateModel(reportRange.getDateEnd(), config)} )); reportHeaderLabel.setEscapeModelStrings(false); return reportHeaderLabel; } private WebMarkupContainer createReportContent(String id, TreeReportModel reportModel, ExcelReport excelReport) { boolean emptyReport = reportModel.getReportData().isEmpty(); WebMarkupContainer reportContent = emptyReport ? noDataReport(id) : withDataReport(id, reportModel, excelReport); reportContent.setOutputMarkupId(true); return reportContent; } private Fragment noDataReport(String id) { return new Fragment(id, "noData", this); } private Fragment withDataReport(String id, final TreeReportModel reportModel, final ExcelReport excelReport) { reportFragment = new Fragment(id, "withDataReport", this); reportFragment.add(createExcelLink(excelReport)); reportFragment.add(createZeroBookingSelector("reportOptionsPlaceholder")); reportFragment.add(createAdditionalOptions("additionalOptions")); createReportTableContainer(reportModel, reportFragment); return reportFragment; } private void createReportTableContainer(TreeReportModel reportModel, MarkupContainer parent) { Border reportFrame = new GreyBlueRoundedBorder("reportFrame") { @Override protected WebMarkupContainer createComponent() { WebMarkupContainer frame = super.createComponent(); frame.add(AttributeModifier.append("style", "margin: 0 5px 0 5px;")); return frame; } }; reportFrameContainer = new Container("reportFrameContainer"); reportFrame.add(reportFrameContainer); reportFrameContainer.add(addHeaderColumns("columnHeaders")); DataView<TreeReportElement> dataView = createDataView(reportModel); reportFrameContainer.add(dataView); reportFrameContainer.add(addGrandTotal("cell", reportModel)); pagingNavigator = createNav(dataView); parent.addOrReplace(pagingNavigator); parent.addOrReplace(reportFrame); } private WebMarkupContainer createZeroBookingSelector(String id) { if (reportConfig.isShowZeroBookings()) { return new ZeroBookingSelector(id, reportConfig, criteria); } else { return new Container(id); } } protected WebMarkupContainer createAdditionalOptions(String id) { return new Container(id); } @Override public void onEvent(IEvent<?> event) { if (event.getPayload() instanceof ZeroBookingSelectionChangedEvent) { updateDataTableForZeroBookingSelectionChange(event); } else if (event.getPayload() instanceof UpdateReportDataEvent) { updateReport(event); } } private void updateReport(IEvent<?> event) { UpdateReportDataEvent aggregateByChangedEvent = (UpdateReportDataEvent) event.getPayload(); reportConfig = aggregateByChangedEvent.getReportConfig(); createReportTableContainer((TreeReportModel) getPanelModel(), reportFragment); aggregateByChangedEvent.target().add(reportFragment); } private void updateDataTableForZeroBookingSelectionChange(IEvent<?> event) { TreeReportModel model = (TreeReportModel) getPanelModel(); model.detach(); DataView<TreeReportElement> dataView = createDataView(model); reportFrameContainer.addOrReplace(dataView); HoverPagingNavigator nav = createNav(dataView); pagingNavigator.replaceWith(nav); pagingNavigator = nav; ZeroBookingSelectionChangedEvent payload = (ZeroBookingSelectionChangedEvent) event.getPayload(); payload.target().add(reportFrameContainer, nav); } private HoverPagingNavigator createNav(DataView<TreeReportElement> dataView) { HoverPagingNavigator nav = new HoverPagingNavigator(NAVIGATOR_ID, dataView); nav.setOutputMarkupPlaceholderTag(true); return nav; } private Component createExcelLink(final ExcelReport excelReport) { if (excelReport != null) { return new ExcelReportLink("excelLink") { @Override protected ExcelReport createReportBuilder() { return excelReport; } }; } else { return HtmlUtil.getInvisibleLink("excelLink"); } } private RepeatingView addGrandTotal(String id, TreeReportModel reportModel) { RepeatingView totalView = new RepeatingView(id); // add cells for (ReportColumn column : reportConfig.getReportColumns()) { if (column.isVisible()) { Label label; String childId = totalView.newChildId(); switch (column.getColumnType()) { case HOUR: label = new Label(childId, new Model<>(reportModel.getTotalHours())); break; case TURNOVER: label = new CurrencyLabel(childId, reportModel.getTotalTurnover()); label.setEscapeModelStrings(false); break; default: label = HtmlUtil.getNbspLabel(childId); break; } Optional<String> optionalClass = addColumnTypeStyling(column.getColumnType()); if (optionalClass.isPresent()) { label.add(AttributeModifier.append("class", optionalClass.get())); } totalView.add(label); } } return totalView; } @SuppressWarnings("unchecked") private DataView<TreeReportElement> createDataView(TreeReportModel reportModel) { List<TreeReportElement> elements = (List<TreeReportElement>) reportModel.getReportData().getReportElements(); DataView<TreeReportElement> dataView = new TreeReportDataView(REPORT_TABLE_ID, new TreeReportDataProvider(elements)); dataView.setOutputMarkupId(true); dataView.setItemsPerPage(25); return dataView; } private RepeatingView addHeaderColumns(String id) { RepeatingView columnHeaders = new RepeatingView(id); for (ReportColumn reportColumn : reportConfig.getReportColumns()) { Label columnHeader = new Label(columnHeaders.newChildId(), new ResourceModel(reportColumn.getColumnHeaderResourceKey())); columnHeader.setVisible(reportColumn.isVisible()); columnHeaders.add(columnHeader); if (reportColumn.getColumnType().isNumeric()) { columnHeader.add(CSS_ALIGN_RIGHT); } } return columnHeaders; } private Optional<String> addColumnTypeStyling(ColumnType columnType) { if (columnType.isNumeric()) { return Optional.of("numeric"); } else if (columnType == ColumnType.COMMENT) { return Optional.of("comment"); } else { return Optional.absent(); } } private class TreeReportDataView extends DataView<TreeReportElement> { private static final long serialVersionUID = 1L; private long previousForPage = -1; private List<Serializable> previousCellValues; public TreeReportDataView(String id, IDataProvider<TreeReportElement> dataProvider) { super(id, dataProvider); } @Override protected void populateItem(Item<TreeReportElement> item) { RepeatingView cells = new RepeatingView("cell"); TreeReportElement row = item.getModelObject(); if (row.isEmpty()) { item.add(AttributeModifier.append("class", "emptyRow")); } List<Serializable> thisCellValues = new ArrayList<>(); boolean newValueInPreviousColumn = false; int column = 0; // add cells for a row for (final Serializable cellValue : row.getRow()) { thisCellValues.add(cellValue); ReportColumn reportColumn = reportConfig.getReportColumns()[column]; if (reportColumn.isVisible()) { List<String> cssClasses = new ArrayList<>(); Label cellLabel; final String id = Integer.toString(column); if (isDuplicate(column, cellValue) && !newValueInPreviousColumn) { cellLabel = new Label(id, new Model<>("")); newValueInPreviousColumn = false; cells.add(cellLabel); } else if (reportColumn.getColumnType() == ColumnType.LINK) { Fragment linkFragment = new Fragment(id, "linkFragment", TreeReportDataPanel.this); cells.add(linkFragment); Link link = new Link("link") { @Override public void onClick() { PageParameters pageParameters = new PageParameters(); pageParameters.add("id", cellValue); // unused for now } }; linkFragment.add(link); cellLabel = new Label("linkLabel", new Model<>(cellValue)); link.add(cellLabel); } else if (reportColumn.getConverter() != null) { final IConverter converter = reportColumn.getConverter(); cellLabel = new Label(id, new Model<>(cellValue)) { @SuppressWarnings("unchecked") @Override public <C> IConverter<C> getConverter(Class<C> type) { return converter; } }; Optional<String> optionalClass = addColumnTypeStyling(reportColumn.getColumnType()); if (optionalClass.isPresent()) { cssClasses.add(optionalClass.get()); } newValueInPreviousColumn = true; cells.add(cellLabel); } else { newValueInPreviousColumn = true; cellLabel = new Label(id, new Model<>(cellValue)); Optional<String> optionalClass = addColumnTypeStyling(reportColumn.getColumnType()); if (optionalClass.isPresent()) { cssClasses.add(optionalClass.get()); } cells.add(cellLabel); } cssClasses.add(item.getIndex() == 0 ? "firstRow" : ""); cssClasses.add(column == 0 ? " firstColumn" : ""); String cssClass = Joiner.on(" ").join(cssClasses); if (StringUtils.isNotEmpty(cssClass)) { cellLabel.add(AttributeModifier.replace("class", cssClass.trim())); } } column++; } item.add(cells); modifyClassForOddRows(item); previousForPage = getCurrentPage(); previousCellValues = thisCellValues; } private void modifyClassForOddRows(Item<?> item) { if (item.getIndex() % 2 == 1) { item.add(AttributeModifier.append("class", "oddRow")); } } private boolean isDuplicate(int i, Serializable cellValue) { return (!reportConfig.getReportColumns()[i].isAllowDuplicates() && previousCellValues != null && previousForPage == getCurrentPage() && previousCellValues.get(i) != null && previousCellValues.get(i).equals(cellValue)); } } }