All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.imunity.furms.ui.views.community.projects.ProjectsView Maven / Gradle / Ivy

There is a newer version: 4.3.1
Show newest version
/*
 * Copyright (c) 2020 Bixbit s.c. All rights reserved.
 * See LICENSE file for licensing information.
 */

package io.imunity.furms.ui.views.community.projects;

import com.vaadin.componentfactory.Tooltip;
import com.vaadin.componentfactory.TooltipAlignment;
import com.vaadin.componentfactory.TooltipPosition;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.grid.ColumnTextAlign;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.treegrid.TreeGrid;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouterLink;
import io.imunity.furms.api.project_installation.ProjectInstallationsService;
import io.imunity.furms.api.projects.ProjectService;
import io.imunity.furms.domain.communities.CommunityId;
import io.imunity.furms.domain.project_installation.ProjectInstallationJobStatus;
import io.imunity.furms.domain.project_installation.ProjectUpdateJobStatus;
import io.imunity.furms.domain.projects.ProjectId;
import io.imunity.furms.domain.sites.SiteId;
import io.imunity.furms.ui.components.FurmsDialog;
import io.imunity.furms.ui.components.FurmsViewComponent;
import io.imunity.furms.ui.components.GridActionMenu;
import io.imunity.furms.ui.components.GridActionsButtonLayout;
import io.imunity.furms.ui.components.MenuButton;
import io.imunity.furms.ui.components.PageTitle;
import io.imunity.furms.ui.components.RouterGridLink;
import io.imunity.furms.ui.components.StatusLayout;
import io.imunity.furms.ui.components.ViewHeaderLayout;
import io.imunity.furms.ui.views.community.CommunityAdminMenu;
import io.imunity.furms.ui.views.project.resource_access.DenseTreeGrid;
import io.imunity.furms.utils.UTCTimeUtils;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.vaadin.flow.component.icon.VaadinIcon.EDIT;
import static com.vaadin.flow.component.icon.VaadinIcon.PIE_CHART;
import static com.vaadin.flow.component.icon.VaadinIcon.PLUS_CIRCLE;
import static com.vaadin.flow.component.icon.VaadinIcon.REFRESH;
import static com.vaadin.flow.component.icon.VaadinIcon.SEARCH;
import static com.vaadin.flow.component.icon.VaadinIcon.TIME_BACKWARD;
import static com.vaadin.flow.component.icon.VaadinIcon.TRASH;
import static com.vaadin.flow.component.icon.VaadinIcon.USERS;
import static io.imunity.furms.ui.utils.ResourceGetter.getCurrentResourceId;
import static io.imunity.furms.ui.utils.VaadinExceptionHandler.handleExceptions;
import static io.imunity.furms.ui.views.community.projects.ProjectConst.ADMINISTRATORS_PARAM;
import static io.imunity.furms.ui.views.community.projects.ProjectConst.ALLOCATIONS_PARAM;
import static io.imunity.furms.ui.views.community.projects.ProjectConst.PARAM_NAME;
import static java.util.Collections.emptyList;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

@Route(value = "community/admin/projects", layout = CommunityAdminMenu.class)
@PageTitle(key = "view.community-admin.projects.page.title")
public class ProjectsView extends FurmsViewComponent {

	private final ProjectService projectService;
	private final ProjectInstallationsService projectInstallationsService;
	private final TreeGrid grid;
	private ProjectsViewDataSnapshot projectsViewDataSnapshot;

	public ProjectsView(ProjectService projectService, ProjectInstallationsService projectInstallationsService) {
		this.projectService = projectService;
		this.projectInstallationsService = projectInstallationsService;
		this.grid = createCommunityGrid();
		this.projectsViewDataSnapshot = new ProjectsViewDataSnapshot();
		Button addButton = createAddButton();
		loadGridContent();

		getContent().add(createHeaderLayout(addButton), createSearchFilterLayout(grid, addButton), grid);
	}

	private HorizontalLayout createHeaderLayout(Button addButton) {
		return new ViewHeaderLayout(getTranslation("view.community-admin.projects.header"), addButton);
	}

	private Button createAddButton() {
		Button addButton = new Button(getTranslation("view.community-admin.projects.button.add"), PLUS_CIRCLE.create());
		addButton.addClickListener(x -> UI.getCurrent().navigate(ProjectFormView.class));
		return addButton;
	}

	private TreeGrid createCommunityGrid() {
		TreeGrid grid = new DenseTreeGrid<>();

		grid.addComponentHierarchyColumn(this::createNameComponent)
			.setHeader(getTranslation("view.community-admin.projects.grid.column.1"))
			.setSortable(true)
			.setComparator(x -> x.name.toLowerCase());
		grid.addColumn(c -> c.description)
			.setHeader(getTranslation("view.community-admin.projects.grid.column.2"))
			.setSortable(true);
		grid.addColumn(c -> c.siteName)
			.setHeader(getTranslation("view.community-admin.projects.grid.column.3"))
			.setSortable(true);
		grid.addComponentColumn(c -> new StatusLayout(c.status, c.message, getContent()))
			.setHeader(getTranslation("view.community-admin.projects.grid.column.4"))
			.setSortable(true);
		grid.addComponentColumn(this::createLastColumnContent)
			.setHeader(getTranslation("view.community-admin.projects.grid.column.5"))
			.setTextAlign(ColumnTextAlign.END);

		grid.setAllRowsVisible(false);
		return grid;
	}

	private HorizontalLayout createNameComponent(ProjectViewGridModel projectViewModel) {
		HorizontalLayout nameComponent = new HorizontalLayout();

		if (projectViewModel.expired) {
			final Icon warningIcon = TIME_BACKWARD.create();
			final Tooltip warningTooltip = new Tooltip(warningIcon, TooltipPosition.BOTTOM, TooltipAlignment.CENTER);
			warningTooltip.add(getTranslation("view.community-admin.projects.grid.column.1.expired.project.tooltip"));
			getContent().add(warningTooltip);

			nameComponent.add(warningIcon);
		}

		nameComponent.add(new RouterGridLink(projectViewModel.name, projectViewModel.id,
				ProjectView.class, PARAM_NAME, ADMINISTRATORS_PARAM));

		return nameComponent;
	}

	private HorizontalLayout createSearchFilterLayout(Grid grid, Button addButton) {
		TextField textField = new TextField();
		textField.setPlaceholder(getTranslation("view.community-admin.projects.field.search"));
		textField.setPrefixComponent(SEARCH.create());
		textField.setValueChangeMode(ValueChangeMode.EAGER);
		textField.setClearButtonVisible(true);
		textField.addValueChangeListener(event -> {
			String value = textField.getValue().toLowerCase();
			loadGridContent(value);
			addButton.focus();
			textField.focus();
		});

		HorizontalLayout search = new HorizontalLayout(textField);
		search.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
		return search;
	}

	private HorizontalLayout createLastColumnContent(ProjectViewGridModel projectViewModel) {
		if(projectViewModel.communityId == null){
			GridActionMenu contextMenu = new GridActionMenu();
			contextMenu.addItem(new MenuButton(
					getTranslation("view.community-admin.projects.menu.refresh"), REFRESH),
				event -> loadGridContent()
			);
			return new GridActionsButtonLayout(contextMenu.getTarget());
		}
		return new GridActionsButtonLayout(
			new RouterGridLink(USERS, projectViewModel.projectId.id.toString(), ProjectView.class, PARAM_NAME,
				ADMINISTRATORS_PARAM),
			new RouterGridLink(PIE_CHART, projectViewModel.projectId.id.toString(), ProjectView.class, PARAM_NAME, ALLOCATIONS_PARAM),
			createContextMenu(projectViewModel.projectId, projectViewModel.name, projectViewModel.communityId)
		);
	}

	private Component createContextMenu(ProjectId projectId, String projectName, CommunityId communityId) {
		GridActionMenu contextMenu = new GridActionMenu();

		if(projectService.isProjectInTerminalState(communityId, projectId)){
			contextMenu.addItem(new MenuButton(
					getTranslation("view.community-admin.projects.menu.edit"), EDIT),
				event -> UI.getCurrent().navigate(ProjectFormView.class, projectId.id.toString())
			);

			Dialog confirmDialog = createConfirmDialog(projectId, projectName, communityId);

			contextMenu.addItem(new MenuButton(
					getTranslation("view.community-admin.projects.menu.delete"), TRASH),
				event -> confirmDialog.open()
			);
		}

		MenuButton adminComponent = new MenuButton(
			getTranslation("view.community-admin.projects.menu.administrators"),
			USERS
		);
		RouterLink administratorsPool = new RouterGridLink(adminComponent, projectId.id.toString(), ProjectView.class, PARAM_NAME,
				ADMINISTRATORS_PARAM);
		contextMenu.addItem(administratorsPool);

		MenuButton allocationComponent = new MenuButton(
			getTranslation("view.community-admin.projects.menu.allocations"),
			PIE_CHART
		);

		RouterLink allocationsPool = new RouterGridLink(allocationComponent, projectId.id.toString(), ProjectView.class, PARAM_NAME,
				ALLOCATIONS_PARAM);
		contextMenu.addItem(allocationsPool);
		getContent().add(contextMenu);
		return contextMenu.getTarget();
	}

	private Dialog createConfirmDialog(ProjectId projectId, String projectName, CommunityId communityId) {
		FurmsDialog furmsDialog = new FurmsDialog(getTranslation("view.community-admin.projects.dialog.text", projectName));
		furmsDialog.addConfirmButtonClickListener(event -> {
			handleExceptions(() -> projectService.delete(projectId, communityId));
			loadGridContent();
		});
		return furmsDialog;
	}

	private void loadGridContent() {
		Set currentExpandedItems = projectsViewDataSnapshot.projectViewGridModels.stream()
			.filter(grid::isExpanded)
			.collect(Collectors.toSet());
		projectsViewDataSnapshot = new ProjectsViewDataSnapshot();
		grid.setItems(projectsViewDataSnapshot.projectViewGridModels, key -> projectsViewDataSnapshot.getProjectStatuses(key.projectId));
		grid.expand(currentExpandedItems);
	}

	private void loadGridContent(String value) {
		List filteredUsers = projectsViewDataSnapshot.projectViewGridModels.stream()
			.filter(project -> project.matches(value))
			.collect(toList());
		Set currentExpandedItems = filteredUsers.stream()
			.filter(grid::isExpanded)
			.collect(Collectors.toSet());
		projectsViewDataSnapshot = new ProjectsViewDataSnapshot();
		grid.setItems(filteredUsers, key -> projectsViewDataSnapshot.getProjectStatuses(key.projectId));
		grid.expand(currentExpandedItems);
	}

	private class ProjectsViewDataSnapshot {
		public final List projectViewGridModels;
		private final Map> projectInstallationJobStatusByProjectId;

		ProjectsViewDataSnapshot() {
			CommunityId communityId = new CommunityId(getCurrentResourceId());

			this.projectViewGridModels = handleExceptions(() -> projectService.findAllByCommunityId(communityId))
				.orElseGet(Collections::emptySet)
				.stream()
				.map(p -> ProjectViewGridModel.builder()
					.id(p.getId().id.toString())
					.projectId(p.getId())
					.communityId(p.getCommunityId())
					.name(p.getName())
					.description(p.getDescription())
					.expired(UTCTimeUtils.isExpired(p.getUtcEndTime()))
					.build())
				.sorted(comparing(projectViewModel -> projectViewModel.name.toLowerCase()))
				.collect(toList());

			Map> collect =
				projectInstallationsService.findAllUpdatesByCommunityId(communityId).stream()
				.collect(
					groupingBy(
						job -> job.projectId,
						toMap(job -> job.siteId, Function.identity())
					)
				);

			this.projectInstallationJobStatusByProjectId = projectInstallationsService.findAllByCommunityId(communityId).stream()
				.collect(groupingBy(
					jobStatus -> jobStatus.projectId,
					collectingAndThen(
						mapping(
							jobStatus -> {
								String status = getString(collect, jobStatus);
								String message = jobStatus.errorMessage.map(y -> y.message).orElse(null);
								return mapGrid(jobStatus, status, message);
								},
							toList()
						),
						jobStatuses -> {
							jobStatuses.sort(Comparator.comparing(jobStatus -> jobStatus.siteName));
							return jobStatuses;
						})
				));
		}

		private ProjectViewGridModel mapGrid(ProjectInstallationJobStatus jobStatus, String status, String message) {
			return ProjectViewGridModel.builder()
				.id(jobStatus.projectId.id.toString() + jobStatus.siteId.id.toString())
				.siteName(jobStatus.siteName)
				.status(status)
				.message(message)
				.build();
		}

		private String getString(Map> collect,
		                         ProjectInstallationJobStatus x) {
			return Optional.ofNullable(collect.get(x.projectId))
				.flatMap(y -> Optional.ofNullable(y.get(x.siteId)))
				.map(y -> getTranslation("project.update.status." + y.status.getPersistentId()))
				.orElse(getTranslation("project.installation.status." + x.status.getPersistentId()));
		}

		public List getProjectStatuses(ProjectId projectId) {
			return projectInstallationJobStatusByProjectId.getOrDefault(projectId, emptyList());
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy