com.mycollab.module.project.view.milestone.MilestoneRoadmapViewImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.mycollab.module.project.view.milestone.MilestoneRoadmapViewImpl.java

Source

/**
 * This file is part of mycollab-web.
 *
 * mycollab-web 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 3 of the License, or
 * (at your option) any later version.
 *
 * mycollab-web 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 mycollab-web.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.mycollab.module.project.view.milestone;

import com.google.common.eventbus.Subscribe;
import com.hp.gagawa.java.elements.Img;
import com.mycollab.common.i18n.GenericI18Enum;
import com.mycollab.common.i18n.OptionI18nEnum.StatusI18nEnum;
import com.mycollab.configuration.SiteConfiguration;
import com.mycollab.configuration.StorageFactory;
import com.mycollab.core.utils.BeanUtility;
import com.mycollab.core.utils.StringUtils;
import com.mycollab.db.arguments.BasicSearchRequest;
import com.mycollab.db.arguments.NumberSearchField;
import com.mycollab.db.arguments.SearchCriteria;
import com.mycollab.db.arguments.SetSearchField;
import com.mycollab.db.query.LazyValueInjector;
import com.mycollab.eventmanager.ApplicationEventListener;
import com.mycollab.eventmanager.EventBusFactory;
import com.mycollab.module.project.CurrentProjectVariables;
import com.mycollab.module.project.ProjectRolePermissionCollections;
import com.mycollab.module.project.ProjectTypeConstants;
import com.mycollab.module.project.domain.ProjectTicket;
import com.mycollab.module.project.domain.SimpleMilestone;
import com.mycollab.module.project.domain.criteria.MilestoneSearchCriteria;
import com.mycollab.module.project.domain.criteria.ProjectTicketSearchCriteria;
import com.mycollab.module.project.event.MilestoneEvent;
import com.mycollab.module.project.i18n.MilestoneI18nEnum;
import com.mycollab.module.project.i18n.OptionI18nEnum.BugStatus;
import com.mycollab.module.project.i18n.OptionI18nEnum.MilestoneStatus;
import com.mycollab.module.project.i18n.ProjectCommonI18nEnum;
import com.mycollab.module.project.i18n.ProjectI18nEnum;
import com.mycollab.module.project.service.MilestoneService;
import com.mycollab.module.project.service.ProjectTicketService;
import com.mycollab.module.project.ui.ProjectAssetsManager;
import com.mycollab.module.project.ui.ProjectAssetsUtil;
import com.mycollab.module.project.ui.components.BlockRowRender;
import com.mycollab.module.project.ui.components.IBlockContainer;
import com.mycollab.module.project.view.service.MilestoneComponentFactory;
import com.mycollab.module.project.view.ticket.ToggleTicketSummaryField;
import com.mycollab.spring.AppContextUtil;
import com.mycollab.vaadin.MyCollabUI;
import com.mycollab.vaadin.UserUIContext;
import com.mycollab.vaadin.addon.webcomponents.FloatingComponent;
import com.mycollab.vaadin.mvp.ViewComponent;
import com.mycollab.vaadin.mvp.view.AbstractLazyPageView;
import com.mycollab.vaadin.ui.ELabel;
import com.mycollab.vaadin.ui.UIConstants;
import com.mycollab.vaadin.web.ui.ToggleButtonGroup;
import com.mycollab.vaadin.web.ui.WebThemes;
import com.vaadin.server.FontAwesome;
import com.vaadin.shared.ui.MarginInfo;
import com.vaadin.ui.*;
import org.vaadin.viritin.button.MButton;
import org.vaadin.viritin.layouts.MHorizontalLayout;
import org.vaadin.viritin.layouts.MVerticalLayout;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author MyCollab Ltd
 * @since 5.2.0
 */
@ViewComponent
public class MilestoneRoadmapViewImpl extends AbstractLazyPageView
        implements MilestoneRoadmapView, IBlockContainer {

    private MilestoneService milestoneService = AppContextUtil.getSpringBean(MilestoneService.class);

    private ApplicationEventListener<MilestoneEvent.NewMilestoneAdded> newMilestoneHandler = new ApplicationEventListener<MilestoneEvent.NewMilestoneAdded>() {
        @Override
        @Subscribe
        public void handle(MilestoneEvent.NewMilestoneAdded event) {
            MilestoneRoadmapViewImpl.this.removeAllComponents();
            displayView();
        }
    };

    private ApplicationEventListener<MilestoneEvent.MilestoneDeleted> deletedMilestoneHandler = new ApplicationEventListener<MilestoneEvent.MilestoneDeleted>() {
        @Override
        @Subscribe
        public void handle(MilestoneEvent.MilestoneDeleted event) {
            displayWidget();
        }
    };

    private MVerticalLayout roadMapView;
    private MVerticalLayout filterPanel;
    private ELabel headerText;
    private CheckBox closeMilestoneSelection, inProgressMilestoneSelection, futureMilestoneSelection;
    private MilestoneSearchCriteria baseCriteria;

    @Override
    public void attach() {
        EventBusFactory.getInstance().register(newMilestoneHandler);
        EventBusFactory.getInstance().register(deletedMilestoneHandler);
        super.attach();
    }

    @Override
    public void detach() {
        EventBusFactory.getInstance().unregister(newMilestoneHandler);
        EventBusFactory.getInstance().unregister(deletedMilestoneHandler);
        super.detach();
    }

    @Override
    protected void displayView() {
        initUI();

        baseCriteria = new MilestoneSearchCriteria();
        baseCriteria.setProjectIds(new SetSearchField<>(CurrentProjectVariables.getProjectId()));
        baseCriteria.setOrderFields(Arrays.asList(new SearchCriteria.OrderField("startdate", SearchCriteria.DESC),
                new SearchCriteria.OrderField("enddate", SearchCriteria.DESC)));
        displayMilestones();

        closeMilestoneSelection = new CheckBox("", true);
        inProgressMilestoneSelection = new CheckBox("", true);
        futureMilestoneSelection = new CheckBox("", true);

        closeMilestoneSelection.addValueChangeListener(
                valueChangeEvent -> displayMilestones(baseCriteria, closeMilestoneSelection.getValue(),
                        inProgressMilestoneSelection.getValue(), futureMilestoneSelection.getValue()));
        inProgressMilestoneSelection.addValueChangeListener(
                valueChangeEvent -> displayMilestones(baseCriteria, closeMilestoneSelection.getValue(),
                        inProgressMilestoneSelection.getValue(), futureMilestoneSelection.getValue()));
        futureMilestoneSelection.addValueChangeListener(
                valueChangeEvent -> displayMilestones(baseCriteria, closeMilestoneSelection.getValue(),
                        inProgressMilestoneSelection.getValue(), futureMilestoneSelection.getValue()));
        futureMilestoneSelection.setIcon(FontAwesome.CLOCK_O);

        filterPanel.with(closeMilestoneSelection, inProgressMilestoneSelection, futureMilestoneSelection);
        displayWidget();
    }

    private void displayWidget() {
        final MilestoneSearchCriteria tmpCriteria = BeanUtility.deepClone(baseCriteria);
        tmpCriteria.setStatuses(new SetSearchField<>(MilestoneStatus.Closed.name()));
        int totalCloseCount = milestoneService.getTotalCount(tmpCriteria);
        closeMilestoneSelection.setCaption(String.format("%s (%d)",
                UserUIContext.getMessage(MilestoneI18nEnum.WIDGET_CLOSED_PHASE_TITLE), totalCloseCount));
        closeMilestoneSelection.setIcon(FontAwesome.MINUS_CIRCLE);
        filterPanel.addComponent(closeMilestoneSelection);

        tmpCriteria.setStatuses(new SetSearchField<>(MilestoneStatus.InProgress.name()));
        int totalInProgressCount = milestoneService.getTotalCount(tmpCriteria);
        inProgressMilestoneSelection.setCaption(String.format("%s (%d)",
                UserUIContext.getMessage(MilestoneI18nEnum.WIDGET_INPROGRESS_PHASE_TITLE), totalInProgressCount));
        inProgressMilestoneSelection.setIcon(FontAwesome.SPINNER);
        filterPanel.addComponent(inProgressMilestoneSelection);

        tmpCriteria.setStatuses(new SetSearchField<>(MilestoneStatus.Future.name()));
        int totalFutureCount = milestoneService.getTotalCount(tmpCriteria);
        futureMilestoneSelection.setCaption(String.format("%s (%d)",
                UserUIContext.getMessage(MilestoneI18nEnum.WIDGET_FUTURE_PHASE_TITLE), totalFutureCount));
    }

    private void displayMilestones(MilestoneSearchCriteria milestoneSearchCriteria, boolean closeSelection,
            boolean inProgressSelection, boolean futureSelection) {
        baseCriteria = milestoneSearchCriteria;
        List<String> statuses = new ArrayList<>();
        if (closeSelection) {
            statuses.add(MilestoneStatus.Closed.name());
        }
        if (inProgressSelection) {
            statuses.add(MilestoneStatus.InProgress.name());
        }
        if (futureSelection) {
            statuses.add(MilestoneStatus.Future.name());
        }
        if (statuses.size() > 0) {
            baseCriteria.setStatuses(new SetSearchField<>(statuses));
            displayMilestones();
        } else {
            roadMapView.removeAllComponents();
        }
    }

    private void displayMilestones() {
        roadMapView.removeAllComponents();
        List<SimpleMilestone> milestones = milestoneService
                .findPageableListByCriteria(new BasicSearchRequest<>(baseCriteria));
        for (SimpleMilestone milestone : milestones) {
            roadMapView.addComponent(new MilestoneBlock(milestone));
        }

        headerText.setValue(
                String.format("%s %s", ProjectAssetsManager.getAsset(ProjectTypeConstants.MILESTONE).getHtml(),
                        UserUIContext.getMessage(MilestoneI18nEnum.OPT_ROADMAP_VALUE, milestones.size())));
    }

    @Override
    public void refresh() {
        headerText.setValue(String.format("%s %s",
                ProjectAssetsManager.getAsset(ProjectTypeConstants.MILESTONE).getHtml(),
                UserUIContext.getMessage(MilestoneI18nEnum.OPT_ROADMAP_VALUE, roadMapView.getComponentCount())));
    }

    private void initUI() {
        headerText = ELabel.h2("");

        MHorizontalLayout header = new MHorizontalLayout().withStyleName("hdr-view").withFullWidth()
                .withMargin(true).with(headerText, createHeaderRight()).withAlign(headerText, Alignment.MIDDLE_LEFT)
                .expand(headerText);
        this.addComponent(header);
        roadMapView = new MVerticalLayout().withSpacing(false);
        filterPanel = new MVerticalLayout().withWidth("250px").withStyleName(WebThemes.BOX);
        FloatingComponent floatingComponent = FloatingComponent.floatThis(filterPanel);
        floatingComponent.setContainerId("main-body");
        this.addComponent(
                new MHorizontalLayout().withFullWidth().with(roadMapView, filterPanel).expand(roadMapView));
    }

    private HorizontalLayout createHeaderRight() {
        MButton createBtn = new MButton(UserUIContext.getMessage(MilestoneI18nEnum.NEW), clickEvent -> {
            SimpleMilestone milestone = new SimpleMilestone();
            milestone.setSaccountid(MyCollabUI.getAccountId());
            milestone.setProjectid(CurrentProjectVariables.getProjectId());
            UI.getCurrent().addWindow(new MilestoneAddWindow(milestone));
        }).withIcon(FontAwesome.PLUS).withStyleName(WebThemes.BUTTON_ACTION)
                .withVisible(CurrentProjectVariables.canWrite(ProjectRolePermissionCollections.MILESTONES));

        MButton printBtn = new MButton("", clickEvent -> UI.getCurrent()
                .addWindow(new MilestoneCustomizeReportOutputWindow(new LazyValueInjector() {
                    @Override
                    protected Object doEval() {
                        return baseCriteria;
                    }
                }))).withIcon(FontAwesome.PRINT).withStyleName(WebThemes.BUTTON_OPTION)
                        .withDescription(UserUIContext.getMessage(GenericI18Enum.ACTION_EXPORT));

        MButton boardBtn = new MButton(UserUIContext.getMessage(ProjectCommonI18nEnum.OPT_BOARD),
                clickEvent -> EventBusFactory.getInstance().post(new MilestoneEvent.GotoList(this, null)))
                        .withIcon(FontAwesome.SERVER).withWidth("100px");

        MButton roadmapBtn = new MButton(UserUIContext.getMessage(ProjectCommonI18nEnum.OPT_LIST))
                .withIcon(FontAwesome.NAVICON).withWidth("100px");

        ToggleButtonGroup viewButtons = new ToggleButtonGroup();
        viewButtons.addButton(roadmapBtn);
        viewButtons.addButton(boardBtn);
        if (!SiteConfiguration.isCommunityEdition()) {
            MButton kanbanBtn = new MButton(UserUIContext.getMessage(ProjectCommonI18nEnum.OPT_KANBAN),
                    clickEvent -> EventBusFactory.getInstance()
                            .post(new MilestoneEvent.GotoKanban(MilestoneRoadmapViewImpl.this)))
                                    .withIcon(FontAwesome.TH).withWidth("100px");
            viewButtons.addButton(kanbanBtn);
        }

        viewButtons.withDefaultButton(roadmapBtn);
        return new MHorizontalLayout(createBtn, printBtn, viewButtons);
    }

    private static class MilestoneBlock extends BlockRowRender {
        private boolean showIssues = false;

        MilestoneBlock(final SimpleMilestone milestone) {
            this.withMargin(new MarginInfo(true, false, true, false)).withStyleName("roadmap-block");

            FontAwesome statusIcon = ProjectAssetsUtil.getPhaseIcon(milestone.getStatus());
            ELabel statusLbl = ELabel
                    .html(statusIcon.getHtml() + " "
                            + UserUIContext.getMessage(MilestoneStatus.class, milestone.getStatus()))
                    .withStyleName(UIConstants.BLOCK).withWidthUndefined();
            ToggleMilestoneSummaryField toggleMilestoneSummaryField = new ToggleMilestoneSummaryField(milestone,
                    false, true);
            MHorizontalLayout headerLayout = new MHorizontalLayout(statusLbl, toggleMilestoneSummaryField)
                    .expand(toggleMilestoneSummaryField).withFullWidth();
            this.with(headerLayout);

            CssLayout metaBlock = new CssLayout();
            MilestoneComponentFactory popupFieldFactory = AppContextUtil
                    .getSpringBean(MilestoneComponentFactory.class);
            metaBlock.addComponent(popupFieldFactory.createMilestoneAssigneePopupField(milestone, true));
            metaBlock.addComponent(popupFieldFactory.createStartDatePopupField(milestone));
            metaBlock.addComponent(popupFieldFactory.createEndDatePopupField(milestone));
            if (!SiteConfiguration.isCommunityEdition()) {
                metaBlock.addComponent(popupFieldFactory.createBillableHoursPopupField(milestone));
                metaBlock.addComponent(popupFieldFactory.createNonBillableHoursPopupField(milestone));
            }

            this.with(metaBlock);

            if (StringUtils.isNotBlank(milestone.getDescription())) {
                this.addComponent(ELabel.html(StringUtils.formatRichText(milestone.getDescription())));
            }

            MHorizontalLayout progressLayout = new MHorizontalLayout();
            progressLayout.setDefaultComponentAlignment(Alignment.MIDDLE_LEFT);
            int openAssignments = milestone.getNumOpenBugs() + milestone.getNumOpenTasks()
                    + milestone.getNumOpenRisks();
            int totalAssignments = milestone.getNumBugs() + milestone.getNumTasks() + milestone.getNumRisks();
            ELabel progressInfoLbl;
            if (totalAssignments > 0) {
                progressInfoLbl = new ELabel(UserUIContext.getMessage(ProjectI18nEnum.OPT_PROJECT_TICKET,
                        (totalAssignments - openAssignments), totalAssignments,
                        (totalAssignments - openAssignments) * 100 / totalAssignments))
                                .withStyleName(UIConstants.META_INFO);
            } else {
                progressInfoLbl = new ELabel(UserUIContext.getMessage(ProjectI18nEnum.OPT_NO_TICKET))
                        .withStyleName(UIConstants.META_INFO);
            }

            final MVerticalLayout issueLayout = new MVerticalLayout()
                    .withMargin(new MarginInfo(false, true, false, true));
            issueLayout.setVisible(false);

            progressLayout.with(progressInfoLbl);

            if (totalAssignments > 0) {
                final MButton viewIssuesBtn = new MButton(
                        UserUIContext.getMessage(ProjectI18nEnum.ACTION_VIEW_TICKETS))
                                .withStyleName(WebThemes.BUTTON_LINK);
                viewIssuesBtn.addClickListener(clickEvent -> {
                    showIssues = !showIssues;
                    if (showIssues) {
                        issueLayout.setVisible(true);
                        viewIssuesBtn.setCaption(UserUIContext.getMessage(ProjectI18nEnum.ACTION_HIDE_TICKETS));
                        ProjectTicketSearchCriteria searchCriteria = new ProjectTicketSearchCriteria();
                        searchCriteria.setProjectIds(new SetSearchField<>(CurrentProjectVariables.getProjectId()));
                        searchCriteria.setTypes(CurrentProjectVariables.getRestrictedTicketTypes());
                        searchCriteria.setMilestoneId(new NumberSearchField(milestone.getId()));
                        ProjectTicketService genericTaskService = AppContextUtil
                                .getSpringBean(ProjectTicketService.class);
                        List<ProjectTicket> tickets = genericTaskService
                                .findPageableListByCriteria(new BasicSearchRequest<>(searchCriteria));
                        for (ProjectTicket ticket : tickets) {
                            ToggleTicketSummaryField toggleTicketSummaryField = new ToggleTicketSummaryField(
                                    ticket);
                            MHorizontalLayout rowComp = new MHorizontalLayout(ELabel.EMPTY_SPACE());
                            rowComp.setDefaultComponentAlignment(Alignment.TOP_LEFT);
                            rowComp.with(ELabel.fontIcon(ProjectAssetsManager.getAsset(ticket.getType()))
                                    .withWidthUndefined());
                            String status = "";
                            if (ticket.isBug()) {
                                status = UserUIContext.getMessage(BugStatus.class, ticket.getStatus());
                            } else if (ticket.isMilestone()) {
                                status = UserUIContext.getMessage(MilestoneStatus.class, ticket.getStatus());
                            } else if (ticket.isRisk()) {
                                status = UserUIContext.getMessage(StatusI18nEnum.class, ticket.getStatus());
                            } else if (ticket.isTask()) {
                                status = UserUIContext.getMessage(StatusI18nEnum.class, ticket.getStatus());
                            }
                            rowComp.with(new ELabel(status).withStyleName(UIConstants.BLOCK).withWidthUndefined());
                            String avatarLink = StorageFactory.getAvatarPath(ticket.getAssignUserAvatarId(), 16);
                            Img img = new Img(ticket.getAssignUserFullName(), avatarLink)
                                    .setCSSClass(UIConstants.CIRCLE_BOX).setTitle(ticket.getAssignUserFullName());
                            rowComp.with(ELabel.html(img.write()).withWidthUndefined());

                            rowComp.with(toggleTicketSummaryField).expand(toggleTicketSummaryField);
                            issueLayout.addComponent(rowComp);
                        }
                    } else {
                        viewIssuesBtn.setCaption(UserUIContext.getMessage(ProjectI18nEnum.ACTION_VIEW_TICKETS));
                        issueLayout.removeAllComponents();
                        issueLayout.setVisible(false);
                    }
                });
                progressLayout.with(viewIssuesBtn);
            }

            this.with(progressLayout, issueLayout);
        }
    }
}