Input:
import com.buschmais.backend.users.User;
import com.buschmais.frontend.MainLayout;
import com.buschmais.frontend.views.ADRExplorer.ADRExplorerView;
import com.buschmais.frontend.views.adrCreate.ADRRichCreateView;
import com.buschmais.frontend.views.adrVote.ADRVoteView;
import com.buschmais.frontend.views.kanban.KanbanView;
import com.buschmais.frontend.views.login.LoginView;
import com.buschmais.frontend.views.logout.LogoutView;
import com.buschmais.frontend.views.notifications.NotiView;
import com.buschmais.frontend.views.user.UserView;
import com.buschmais.frontend.views.usercontrol.GroupsInformationView;
import com.buschmais.frontend.views.usercontrol.UserControlView;
import com.buschmais.frontend.views.welcome.WelcomeView;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.router.RouterLayout;
import lombok.Getter;
import lombok.NonNull;

Context:
src/main/java/com/buschmais/frontend/vars/OtherConstantsFrontend.java
package com.buschmais.frontend.vars;

public class OtherConstantsFrontend {

	public static final User DEFAULT_DELETED_USER = new User(StringConstantsFrontend.DEFAULT_DELETED_USER_NAME, Double.toString(Math.random()*1000000000000d));

	public static class MenuItemInfo {

		private String text;
		private String iconClass;
		private Class<? extends Component> view;

		public MenuItemInfo(String text, String iconClass, Class<? extends Component> view) {
			this.text = text;
			this.iconClass = iconClass;
			this.view = view;
		}

		public String getText() {
			return text;
		}

		public String getIconClass() {
			return iconClass;
		}

		public Class<? extends Component> getView() {
			return view;
		}
	}
	public static final MenuItemInfo[] MENU_ITEM_INFOS = new MenuItemInfo[]{
			new MenuItemInfo(StringConstantsFrontend.MENU_TITLE_LOGIN, "la la-user", LoginView.class),

			new MenuItemInfo(StringConstantsFrontend.MENU_TITLE_OVERVIEW, "la la-clipboard", KanbanView.class),

			new MenuItemInfo(StringConstantsFrontend.MENU_TITLE_EXPLORER, "la la-archive", ADRExplorerView.class),

			new MenuItemInfo(StringConstantsFrontend.MENU_TITLE_CREATE_ADR, "la la-pencil", ADRRichCreateView.class),

			new MenuItemInfo(StringConstantsFrontend.MENU_TITLE_USER, "la la-user", UserView.class),

			new MenuItemInfo(StringConstantsFrontend.MENU_TITLE_USER_CONTROL, "la la-users", UserControlView.class),

			//new MenuItemInfo("ADRDetails", "la la-info-circle", ADRDetailsView.class),

	};

	public static class Route {

		@NonNull
		@Getter
		private String path;

		@NonNull
		@Getter
		private Class<? extends Component> view;

		@NonNull
		@Getter
		private Class<? extends RouterLayout> parentView;

		public Route(@NonNull String path, @NonNull Class<? extends Component> view) {
			this.path = path;
			this.view = view;
			this.parentView = MainLayout.class;
		}

		public Route(@NonNull String path, @NonNull Class<? extends Component> view, @NonNull Class<? extends RouterLayout> parentView) {
			this.path = path;
			this.view = view;
			this.parentView = parentView;
		}
	}
	public static final Route[] ALWAYS_ACCESSIBLE_ROUTES = new Route[] {
			new Route(StringConstantsFrontend.LANDING_PAGE_PATH, WelcomeView.class),
			new Route(StringConstantsFrontend.LANDING_PAGE_PATH_ALIAS, WelcomeView.class)
	};
	public static final Route[] LOGGED_OUT_ACCESSIBLE_ROUTES = new Route[] {
			new Route(StringConstantsFrontend.LOGIN_PATH, LoginView.class)
/*			new Route(StringConstantsFrontend.ADR_CREATE_PATH, ADRCreateView.class),
			new Route(StringConstantsFrontend.ADR_CREATE_PATH, ADRCreateView.class),
			new Route(StringConstantsFrontend.ADR_CREATE_PATH, ADRCreateView.class)
*/	};
	public static final Route[] LOGGED_IN_ACCESSIBLE_ROUTES = new Route[] {
			new Route(StringConstantsFrontend.ADR_CREATE_PATH, ADRRichCreateView.class),
			new Route(StringConstantsFrontend.OVERVIEW_PATH, KanbanView.class),
			new Route(StringConstantsFrontend.ADR_DETAILS_PATH, ADRVoteView.class),
			new Route(StringConstantsFrontend.USER_PATH, UserView.class),
			new Route(StringConstantsFrontend.LOGOUT_PATH, LogoutView.class),
			new Route(StringConstantsFrontend.NOTI_PATH, NotiView.class),
			new Route(StringConstantsFrontend.EXPL_PATH, ADRExplorerView.class),

//			new Route(StringConstantsFrontend.ADR_CREATE_PATH, ADRCreateView.class)
	};
	public static final Route[] ADMIN_ACCESSIBLE_ROUTES = new Route[] {

src/main/java/com/buschmais/frontend/views/welcome/WelcomeView.java
@PageTitle("Willkommen")
//@Route(value = StringConstantsFrontend.LANDING_PAGE_PATH, layout = MainLayout.class)
//@RouteAlias(value = "", layout = MainLayout.class)
@CssImport(value = "./themes/adr-workbench/welcome/welcome.css")
@Tag("hello-world-view")
//@WebServlet(value = "/*", asyncSupported = true)
public class WelcomeView extends VerticalLayout {

	private UserDao userDao;

	private Label welcomeLabel;
	private Label motivationLabel;
	private Label joinLabel;
	private Label loginLabel;

	private Html motivationSpan;
	private Html loginSpan;
	private Html registerSpan;
	private Html blankLineP;

	private Div welcomeDiv;
	private Div motivationDiv;
	private Div joinDiv;

	private Button loginButton;

	public WelcomeView(@Autowired UserDao userDao) {
		this.userDao = userDao;

		setupComponents();
		addComponentsToLayouts();
	}

	private void setupComponents() {

		// define
		{
			this.welcomeLabel = new Label("Willkommen bei ADR-Workbench");
			this.motivationLabel = new Label("Motivation");
			this.joinLabel = new Label("Projektbeteiligung");
			this.loginLabel = new Label("Login");

			this.motivationSpan = new Html("
 Die Dokumentation während der Entwicklung eines Softwareprojekte ist kein besonders beliebtes Thema. " +
					"Dennoch ist Dokumentation das nahezu wichtigste, um den Überblick über ein Projekt zu behalten und um dieses schlussendlich erfolgreich fertigstellen zu können." +
					"Um diese Arbeit zu erleichtern wird das Tool ADR-Workbench entworfen. " +
					"Hiermit soll es möglich sein, sogenannte Architecture Decision Records (kurz ADRs) kollaborativ zu entwerfen. " +
					"Ein ADR stellt dabei eine beliebige Architekturentscheidung (z.B. es wird nur noch Logger X genutzt) dar, die von allen Mitarbeitern akzeptiert, kommentiert und abgelehnt werden kann." +
					"So ist es möglich die Entscheidungen einheitlich zu speichern und immer geordnet abrufen zu können. " +
					"Die grafische Oberfläche (Web-Applikation) soll dabei eine einfache Bedienbarkeit bereitstellen, um ein möglichst effizientes Arbeiten zu ermöglichen.
");

			this.loginSpan = new Html("
Du bist bereits am Projekt beteiligt? Dann kommst du hier zum Login:
");
			this.registerSpan = new Html("
Du hast noch keine Zugangsdaten? Kein Problem, frag einfach deinen Projektleiter nach einem Zugang!
");
			this.blankLineP = new Html("

");

			this.welcomeDiv = new Div();
			this.motivationDiv = new Div();
			this.joinDiv = new Div();

			this.loginButton = new Button("Login");
		}

		// set properties
		{

			this.welcomeDiv.addClassName("welcome-div");
			this.motivationDiv.addClassName("motivation-div");
			this.joinDiv.addClassName("join-div");

			this.welcomeLabel.addClassName("heading");
			this.motivationLabel.addClassName("sub-heading");
			this.joinLabel.addClassName("sub-heading");

			this.loginButton.addClassName("login-button");
			this.registerSpan.getElement().getStyle().set("padding-top", "20px");
		}

		// listener
		addButtonListener();

	}

	private void addComponents() {
		this.welcomeDiv.add(this.welcomeLabel);

		this.motivationDiv.add(this.motivationLabel);
		this.motivationDiv.add(this.motivationSpan);

		if(userDao.getCurrentUser() == null) {
			this.joinDiv.add(this.joinLabel);
			this.joinDiv.add(this.loginSpan);
			this.joinDiv.add(this.loginButton);
			this.joinDiv.add(this.blankLineP);
			this.joinDiv.add(this.registerSpan);
		}
	}

	private void addComponentsToLayouts() {
		addComponents();

		add(this.welcomeDiv);
		add(this.motivationDiv);
		add(this.joinDiv);
	}

	private void addButtonListener(){
		this.loginButton.addClickListener(event -> {
			if(this.getUI().isPresent()) this.getUI().get().navigate("login");
		});
	}
}

src/main/java/com/buschmais/frontend/views/usercontrol/GroupsInformationView.java
@PageTitle("Group Information")
@CssImport(value = "./themes/adr-workbench/UserControl/GroupComponent.css")
public class GroupsInformationView extends VerticalLayout implements HasUrlParameter<String>, BroadcastListener, BeforeLeaveObserver {

	private final ADRAccessDao adrAccessDao;
	private final UserDao userDao;

	private String groupId;

	private HorizontalLayout groupsHeadLayout;
	private HorizontalLayout grouprightsLayout;
	private HorizontalLayout membersmanagementLayout;

	private Span readableYes;
	private Span readableNo;
	private Span writableYes;
	private Span writableNo;
	private Span votableYes;
	private Span votableNo;

	private Button deleteGroupButton;
	private Button manageUsers;
	private Button readable;
	private Button unreadable;
	private Button writable;
	private Button unwritable;
	private Button votable;
	private Button unvotable;

	private Dialog deleteConfirmDialog;

	public GroupsInformationView(@Autowired ADRAccessDao accessDao, @Autowired UserDao userDao){
		this.adrAccessDao = accessDao;
		this.userDao = userDao;

		Broadcaster.registerListener(this);
	}

	@Override
	public void setParameter(BeforeEvent beforeEvent, String groupname) {
		synchronized (adrAccessDao){
			Optional<AccessGroup> group1 = adrAccessDao.findByName(groupname);
			group1.ifPresent((group) -> {
				this.groupId = group.getId();

				Collection<User> members = group.getUsers();

				groupsHeadLayout = new HorizontalLayout();

				Label groupsname = new Label(group.getName());
				groupsname.getElement().getThemeList().add("badge primary");
				Button back = new Button(new Icon(VaadinIcon.ANGLE_DOUBLE_LEFT));
				back.addClickListener(event -> {
					String route = RouteConfiguration.forSessionScope().getUrl(UserControlView.class);
					this.getUI().ifPresent(page -> page.navigate(route));
				});
				deleteGroupButton = new Button(StringConstantsFrontend.GROUP_DELETE);
				deleteGroupButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_PRIMARY);
				deleteGroupButton.addClickListener(event -> {
					deleteConfirmDialog = new ConfirmDialog("Group löschen", "Die Gruppe \"" + group.getName() + "\" wirklich löschen?", "löschen", confirmClickEvent -> {
						synchronized (adrAccessDao){
							adrAccessDao.findByName(group.getName()).ifPresent(adrAccessDao::delete);
						}
						this.deleteConfirmDialog.close();
						new SuccessNotification("Die Gruppe wurde erfolgreich gelöscht!");
						String route = RouteConfiguration.forSessionScope().getUrl(UserControlView.class);
						this.removeAll();
						this.getUI().ifPresent(page -> page.navigate(route));
						Broadcaster.broadcastMessage(Event.GROUP_CHANGED, groupId, this);
					}, StringConstantsFrontend.GENERAL_DIALOG_BUTTON_CANCEL, cancelEvent -> {
						this.deleteConfirmDialog.close();
						new ErrorNotification("Es gab einen Fehler beim Löschen der Gruppe!").open();
					});
					this.deleteConfirmDialog.open();
				});

				manageUsers = new Button("Nutzer verwalten");
				manageUsers.addClickListener(e -> {
					ChangeGroupUsersDialog dialog = new ChangeGroupUsersDialog(this.userDao, this.adrAccessDao, this.groupId);

					dialog.addDetachListener(event -> {
						String route = RouteConfiguration.forSessionScope().getUrl(GroupsInformationView.class, group.getName());
						this.removeAll();
						this.getUI().ifPresent(page -> page.navigate(route));
					});
					dialog.open();
				});

				groupsHeadLayout.add(back,groupsname,manageUsers, deleteGroupButton);
				membersmanagementLayout = new HorizontalLayout();
				//addUserConfigure(group);
				//removeUserConfigure(group);

				this.setSizeFull();

				this.add(groupsHeadLayout);
				groupRightsConfigure(group);
				buttonsListenerConfigure(group);
				membersListConfigure(members);
				this.add(membersmanagementLayout);
			});
		}
	}

	private void groupRightsConfigure(AccessGroup group){

		readableYes = new Span(new Icon(VaadinIcon.CHECK), new Span(StringConstantsFrontend.GROUP_READABLE));
		readableNo = new Span(new Icon(VaadinIcon.CLOSE), new Span(StringConstantsFrontend.GROUP_READABLE));
		writableYes = new Span(new Icon(VaadinIcon.CHECK), new Span(StringConstantsFrontend.GROUP_WRITABLE));
		writableNo = new Span(new Icon(VaadinIcon.CLOSE), new Span(StringConstantsFrontend.GROUP_WRITABLE));
		votableYes = new Span(new Icon(VaadinIcon.CHECK), new Span(StringConstantsFrontend.GROUP_VOTABLE));
		votableNo = new Span(new Icon(VaadinIcon.CLOSE), new Span(StringConstantsFrontend.GROUP_VOTABLE));

		readable = new Button(StringConstantsFrontend.GROUP_READABLE);
		unreadable = new Button(StringConstantsFrontend.GROUP_UNREADABLE);
		writable = new Button(StringConstantsFrontend.GROUP_WRITABLE);
		unwritable = new Button(StringConstantsFrontend.GROUP_UNWRITABLE);
		votable = new Button(StringConstantsFrontend.GROUP_VOTABLE);
		unvotable = new Button(StringConstantsFrontend.GROUP_UNVOTABLE);

		readableYes.addClassName("readable-yes-span");
		readableNo.addClassName("readable-no-span");
		writableYes.addClassName("writable-yes-span");
		writableNo.addClassName("writable-no-span");
		votableYes.addClassName("votable-yes-span");
		votableNo.addClassName("votable-no-span");
		readable.addClassName("readable-button");
		unreadable.addClassName("unreadable-button");
		writable.addClassName("writable-button");
		unwritable.addClassName("unwritable-button");
		votable.addClassName("votable-button");
		unvotable.addClassName("unvotable-button");

		readableYes.getElement().getThemeList().add("badge");
		readableNo.getElement().getThemeList().add("badge");
		writableYes.getElement().getThemeList().add("badge");
		writableNo.getElement().getThemeList().add("badge");
		votableYes.getElement().getThemeList().add("badge");
		votableNo.getElement().getThemeList().add("badge");

		readable.addThemeVariants(ButtonVariant.LUMO_SMALL);
		unreadable.addThemeVariants(ButtonVariant.LUMO_SMALL);
		writable.addThemeVariants(ButtonVariant.LUMO_SMALL);
		unwritable.addThemeVariants(ButtonVariant.LUMO_SMALL);
		votable.addThemeVariants(ButtonVariant.LUMO_SMALL);
		unvotable.addThemeVariants(ButtonVariant.LUMO_SMALL);

		grouprightsLayout = new HorizontalLayout();

		if(group.getRights().isReadable()){
			grouprightsLayout.add(readableYes);
			grouprightsLayout.add(unreadable);
		}else{
			grouprightsLayout.add(readableNo);
			grouprightsLayout.add(readable);
		}
		if(group.getRights().isWritable()){
			grouprightsLayout.add(writableYes);
			grouprightsLayout.add(unwritable);
		}else{
			grouprightsLayout.add(writableNo);
			grouprightsLayout.add(writable);
		}
		if(group.getRights().isVotable()){
			grouprightsLayout.add(votableYes);
			grouprightsLayout.add(unvotable);
		}else{
			grouprightsLayout.add(votableNo);
			grouprightsLayout.add(votable);
		}

		this.add(grouprightsLayout);

	}

	private void membersListConfigure(Collection<User> members){
		Grid<User> userGrid = new Grid<>(User.class,false);

		userGrid.addColumn(User::getUserName).setHeader(StringConstantsFrontend.GROUP_NAME);
		userGrid.setItems(members);
		userGrid.addClassName("user-grid");
		userGrid.getColumns().forEach(col -> col.setAutoWidth(true));
		this.add(userGrid);
	}

	private void buttonsListenerConfigure(AccessGroup group){
		readable.addClickListener(event -> {
			synchronized (adrAccessDao) {
				group.getRights().setReadable(true);
				adrAccessDao.save(group);
				String route = RouteConfiguration.forSessionScope().getUrl(GroupsInformationView.class, group.getName());
				this.removeAll();
				this.getUI().ifPresent(page -> page.navigate(route));
			}
		});
		unreadable.addClickListener(event -> {
			synchronized (adrAccessDao) {
				group.getRights().setReadable(false);
				adrAccessDao.save(group);
				String route = RouteConfiguration.forSessionScope().getUrl(GroupsInformationView.class, group.getName());
				this.removeAll();
				this.getUI().ifPresent(page -> page.navigate(route));
			}
		});
		writable.addClickListener(event -> {
			synchronized (adrAccessDao) {
				group.getRights().setWritable(true);
				adrAccessDao.save(group);
				String route = RouteConfiguration.forSessionScope().getUrl(GroupsInformationView.class, group.getName());
				this.removeAll();
				this.getUI().ifPresent(page -> page.navigate(route));
			}
		});
		unwritable.addClickListener(event -> {
			synchronized (adrAccessDao) {
				group.getRights().setWritable(false);
				adrAccessDao.save(group);
				String route = RouteConfiguration.forSessionScope().getUrl(GroupsInformationView.class, group.getName());
				this.removeAll();
				this.getUI().ifPresent(page -> page.navigate(route));
			}
		});
		votable.addClickListener(event -> {
			synchronized (adrAccessDao) {
				group.getRights().setVotable(true);
				adrAccessDao.save(group);
				String route = RouteConfiguration.forSessionScope().getUrl(GroupsInformationView.class, group.getName());
				this.removeAll();
				this.getUI().ifPresent(page -> page.navigate(route));
			}
		});
		unvotable.addClickListener(event -> {
			synchronized (adrAccessDao) {
				group.getRights().setVotable(false);
				adrAccessDao.save(group);
				String route = RouteConfiguration.forSessionScope().getUrl(GroupsInformationView.class, group.getName());
				this.removeAll();
				this.getUI().ifPresent(page -> page.navigate(route));
			}
		});
	}

	@Override
	public void receiveBroadcast(BroadcastListener.Event event, String message) {
		if(event == Event.GROUP_CHANGED && groupId.equals(message)) {
			getUI().ifPresent(ui -> ui.access(() -> {
				new ErrorNotification(StringConstantsFrontend.GROUP_GOT_DELETED);
				ui.getPage().setLocation(StringConstantsFrontend.USER_CONTROL_PATH);
			}));
		}
/*			case ADR_CHANGED -> updateDescribingLabels();
			case ADR_REVIEW_STS_CHANGED -> {
				if (adrId != null && adrId.equals(message)) {
					getUI().ifPresent(ui -> ui.access(this::setVariableVotingSectionElements));
				}
			}
*/	}

	/*
	private void addUserConfigure(AccessGroup group){
		ComboBox<User> addUser = new ComboBox<>("add member");
		addUser.setPlaceholder("select user");
		addUser.setAllowCustomValue(false);
		ComboBox.ItemFilter<User> filter = (user, filterString) -> user.getUserName().toLowerCase().startsWith(filterString.toLowerCase());
		addUser.setItems(filter, userDao.findAll());
		addUser.setItemLabelGenerator(user -> {
			return user.getUserName();
		});
		Button add = new Button(StringConstantsFrontend.GROUP_ADD);
		add.addThemeVariants(ButtonVariant.LUMO_SMALL, ButtonVariant.LUMO_SUCCESS);
		add.addClickListener(event -> {
			synchronized (adrAccessDao) {
				if(!addUser.isEmpty()){
					User u= addUser.getValue();
					if(!group.containsUser(u)){
						group.addUser(u);
						adrAccessDao.save(group);
						for (AccessGroup accessGroup : adrAccessDao.findAll()) {
							if (accessGroup.containsUser(u)) {
								if(accessGroup.hashCode() != group.hashCode()){
									accessGroup.removeUser(u);
									adrAccessDao.save(accessGroup);
									break;
								}
							}
						}
						String route = RouteConfiguration.forSessionScope().getUrl(GroupsInformationView.class, group.getName());
						this.removeAll();
						this.getUI().ifPresent(page -> {
							page.navigate(route);
						});
					}
				}
			}
		});
		membersmanagementLayout.add(addUser,add);
	}
	private void removeUserConfigure(AccessGroup group){
		ComboBox<User> removeUser = new ComboBox<>("remove member");
		removeUser.setPlaceholder("select user");
		removeUser.setAllowCustomValue(false);
		ComboBox.ItemFilter<User> filter = (user, filterString) -> user.getUserName().toLowerCase().startsWith(filterString.toLowerCase());
		removeUser.setItems(filter, group.getUsers());
		removeUser.setItemLabelGenerator(user -> {
			return user.getUserName();
		});
		Button remove = new Button(StringConstantsFrontend.MEMBERCOMPONENT_REMOVE);
		remove.addThemeVariants(ButtonVariant.LUMO_SMALL, ButtonVariant.LUMO_ERROR);
		remove.addClickListener(event -> {
			synchronized (adrAccessDao) {
				if(!removeUser.isEmpty()){
					User u= removeUser.getValue();
					group.removeUser(u);
					adrAccessDao.save(group);
					String route = RouteConfiguration.forSessionScope().getUrl(GroupsInformationView.class, group.getName());
					this.removeAll();
					this.getUI().ifPresent(page -> {
						page.navigate(route);
					});
				}
			}
		});
		membersmanagementLayout.add(removeUser,remove);
	}
	 */

	@Override
	public void beforeLeave(BeforeLeaveEvent beforeLeaveEvent) {
		Broadcaster.unregisterListener(this);
	}
}

src/main/java/com/buschmais/frontend/views/adrVote/ADRVoteView.java
@CssImport(value = "./themes/adr-workbench/adrVoting/label.css")
@CssImport(value = "./themes/adr-workbench/adrVoting/comment-section.css")
@CssImport(value = "./themes/adr-workbench/vaadin-components/buttons.css")
@CssImport(value = "./themes/adr-workbench/adrVoting/adr-information.css")
@PageTitle("ADR Details")
public class ADRVoteView extends HorizontalLayout implements HasUrlParameter<String>, BroadcastListener, BeforeEnterObserver, BeforeLeaveObserver {

	/* ADR */
	private String adrId;
	private final ADRDao adrDao;
	private final ADRAccessDao accessGroupService;
	private final UserDao userDao;
	private final ImageDao imageService;

	/* Components */
	private ErrorNotification invalidIdNotification;
	private VoteResultBar voteResultBar;

	// overall layouts
	private VerticalLayout leftLayout;
	private VerticalLayout rightLayout;

	private HorizontalLayout adrInformationLayout;
	private HorizontalLayout adrInformationAuthorStatusLayout;
	private HorizontalLayout adrActionButtonsLayout;

	// information about adr (left side)
	private VerticalLayout adrTopInformationLayout;
	private VerticalLayout titleLayout;
	private VerticalLayout authorLayout;
	private VerticalLayout contextLayout;
	private VerticalLayout decisionLayout;
	private VerticalLayout consequencesLayout;
	private VerticalLayout supersedesLayout;

	// buttons layout
	private VerticalLayout adrButtonsLayout;
	private HorizontalLayout actionButtonsLayout;

	// voting and comments (right side)
	private VerticalLayout voteLayout;
	private HorizontalLayout voteTitleLayout;
	private VerticalLayout commentLayout;
	private VerticalLayout commentMessagesLayout;
	private HorizontalLayout sendCommentButtonLayout;

	private HorizontalLayout voteIconLayout;

	// Label
	private Label statusLabel;
	private Label titleLabel;
	private Label authorLabel;
	private RichTextEditor contextLabel;
	private RichTextEditor decisionLabel;
	private RichTextEditor consequencesLabel;
	private RichTextEditor supersedesLabel;

	private Label separatorAuthorStatus;

	private Label adrInformationLabel;
	private Label titleStringLabel;
	private Label authorStringLabel;
	private Label contextStringLabel;
	private Label decisionStringLabel;
	private Label consequencesStringLabel;
	private Label supersedesStringLabel;

	private Label voteLabel;
	private Label voteNotStartedYet;
	private Label votingSeparatorLabel;
	private Label votingProLabel;
	private Label votingContraLabel;
	private Label votingNotYetVotedLabel;
	private Label votingNotAllowedToVote;

	private Label commentLabel;

	// buttons
	private Button editButton;
	private Button accessGroupsButton;
	private Button sendButton;
	private Button startVoteButton;
	private Button endVoteButton;
	private Button inviteVoterButton;
	private Button proposeDirectlyButton;

	// text areas
	private TextArea sendMessageArea;

	// icons
	private Icon thumbsUpIcon;
	private Icon thumbsDownIcon;

	// divs
	private Div titleDiv;
	private Div authorDiv;
	private Div contextDiv;
	private Div decisionDiv;
	private Div consequencesDiv;
	private Div supersedesDiv;

	// collaboration
	private CollaborationMessageList collabMessageList;
	private CollaborationMessageInput collabMessageInput;
	private final CollaborationMessagePersister collaborationMessagePersister;
	private UserInfo userInfo;

	final String htmlRegEx = "<[^>]*>";

	public ADRVoteView(@Autowired ADRDao adrService, @Autowired ADRAccessDao accessGroupService, @Autowired UserDao userService, @Autowired ImageDao imageService, @Autowired CollaborationMessagePersister collaborationMessagePersister) {
		this.adrDao = adrService;
		this.accessGroupService = accessGroupService;
		this.userDao = userService;
		this.imageService = imageService;
		this.collaborationMessagePersister = collaborationMessagePersister;
		Broadcaster.registerListener(this);
		addDetachListener((DetachEvent e) -> Broadcaster.unregisterListener(this));

	}

	private void setupComponents(ADR adr) {

		User u = this.userDao.getCurrentUser();
		this.userInfo = new UserInfo(u.getId(), u.getUserName());

		// define components
		{
			// layouts
			this.leftLayout = new VerticalLayout();
			this.rightLayout = new VerticalLayout();

			this.adrTopInformationLayout = new VerticalLayout();
			this.adrInformationLayout = new HorizontalLayout();
			this.adrInformationAuthorStatusLayout = new HorizontalLayout();
			this.adrActionButtonsLayout = new HorizontalLayout();

			this.titleLayout = new VerticalLayout();
			this.authorLayout = new VerticalLayout();
			this.contextLayout = new VerticalLayout();
			this.decisionLayout = new VerticalLayout();
			this.consequencesLayout = new VerticalLayout();
			this.supersedesLayout = new VerticalLayout();

			this.adrButtonsLayout = new VerticalLayout();
			this.actionButtonsLayout = new HorizontalLayout();

			this.voteLayout = new VerticalLayout();
			this.voteTitleLayout = new HorizontalLayout();
			this.commentLayout = new VerticalLayout();
			this.commentMessagesLayout = new VerticalLayout();
			this.sendCommentButtonLayout = new HorizontalLayout();

			this.voteIconLayout = new HorizontalLayout();

			//this.adrInformationLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_ADR_INFORMATION);
			this.adrInformationLabel = new Label(adr.getTitle());

			this.separatorAuthorStatus = new Label("\u2012");

			this.titleStringLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_TITEL_STRING);
			this.authorStringLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_AUTHOR_STRING);
			this.contextStringLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_CONTEXT_STRING);
			this.decisionStringLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_DECISION_STRING);
			this.consequencesStringLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_CONSEQUENCES_STRING);
			this.supersedesStringLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_SUPERSEDES_STRING);
			this.commentLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_COMMENTS_STRING);

			this.voteLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_VOTE_STRING);
			this.voteNotStartedYet = new Label(StringConstantsFrontend.ADRVOTEVIEW_VOTING_HAS_NOT_STARTED_YET);
			this.votingSeparatorLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_VOTING_SEPARATOR_STRING);
			this.votingProLabel = new Label();
			this.votingContraLabel = new Label();
			this.votingNotYetVotedLabel = new Label(StringConstantsFrontend.ADRVOTEVIEW_LABEL_NOT_YET_VOTED);
			this.votingNotAllowedToVote = new Label(StringConstantsFrontend.ADRVOTEVIEW_ADR_NOT_ALLOWED_TO_VOTE);

			// buttons
			this.sendButton = new Button(StringConstantsFrontend.ADRVOTEVIEW_BUTTON_SEND_COMMENT);
			this.accessGroupsButton = new Button(StringConstantsFrontend.ADRVOTEVIEW_BUTTON_EDIT_ACCESS_GROUPS_BUTTON);
			this.editButton = new Button(StringConstantsFrontend.ADRVOTEVIEW_LABEL_BUTTON_EDIT_STRING);
			this.startVoteButton = new Button(
					adr.getStatus().getType().equals(ADRStatusType.CREATED)
							? StringConstantsFrontend.ADRVOTEVIEW_LABEL_BUTTON_START_INTERNAL_VOTING
							: StringConstantsFrontend.ADRVOTEVIEW_LABEL_BUTTON_START_VOTING);

			this.proposeDirectlyButton = new Button(StringConstantsFrontend.ADRVOTEVIEW_LABEL_BUTTON_PROPOSE_DIRECTLY);

			this.endVoteButton = new Button(
					adr.getStatus().getType().equals(ADRStatusType.INTERNALLY_PROPOSED)
							? StringConstantsFrontend.ADRVOTEVIEW_LABEL_BUTTON_END_INTERNAL_VOTING
							: StringConstantsFrontend.ADRVOTEVIEW_LABEL_BUTTON_END_VOTING);

			this.inviteVoterButton = new Button(StringConstantsFrontend.ADRVOTEVIEW_LABEL_BUTTON_INVITE_VOTER);

			// text areas
			this.sendMessageArea = new TextArea();
			this.sendMessageArea.setPlaceholder(StringConstantsFrontend.ADRVOTEVIEW_TEXTAREA_COMMENT_PLACEHOLDER);

			// icons
			this.thumbsUpIcon = new Icon(VaadinIcon.THUMBS_UP_O);
			this.thumbsDownIcon = new Icon(VaadinIcon.THUMBS_DOWN_O);

			// divs
			this.titleDiv = new Div();
			this.authorDiv = new Div();
			this.contextDiv = new Div();
			this.decisionDiv = new Div();
			this.consequencesDiv = new Div();
			this.supersedesDiv = new Div();

			// comments
			this.collabMessageList = new CollaborationMessageList(this.userInfo, "adr|" + this.adrId, this.collaborationMessagePersister);
			this.collabMessageInput = new CollaborationMessageInput(this.collabMessageList);
		}

		// methods
		addDescribingLabels();
		//this.updateDescribingLabels();
		setupStatusAndAuthorLabel(adr);

		// setup properties
		{
			this.leftLayout.setSpacing(false);

			this.leftLayout.addClassName("adr-vote-sub-main-layout");
			this.rightLayout.addClassName("adr-vote-sub-main-layout");

			this.adrInformationAuthorStatusLayout.addClassName("adr-information-layout-author-status");

			this.titleLayout.addClassName("adr-vote-information-layout");
			this.authorLayout.addClassName("adr-vote-information-layout");
			this.contextLayout.addClassName("adr-vote-information-layout");
			this.decisionLayout.addClassName("adr-vote-information-layout");
			this.consequencesLayout.addClassName("adr-vote-information-layout");
			this.supersedesLayout.addClassName("adr-vote-information-layout-superseded");

			this.separatorAuthorStatus.addClassName("adr-vote-separator-author-status");

			this.titleStringLabel.addClassName("adr-vote-small-heading");
			this.authorStringLabel.addClassName("adr-vote-small-heading");
			this.contextStringLabel.addClassName("adr-vote-small-heading");
			this.decisionStringLabel.addClassName("adr-vote-small-heading");
			this.consequencesStringLabel.addClassName("adr-vote-small-heading");
			this.supersedesStringLabel.addClassName("adr-vote-small-heading");

			this.titleDiv.addClassName("adr-information-div");
			this.authorDiv.addClassName("adr-information-div");
			this.contextDiv.addClassName("adr-information-div");
			this.decisionDiv.addClassName("adr-information-div");
			this.consequencesDiv.addClassName("adr-information-div");
			this.supersedesDiv.addClassName("adr-information-div-superseded");
			this.adrInformationLabel.addClassName("adr-vote-heading");
			this.voteLabel.addClassName("adr-vote-heading");
			this.voteLabel.getStyle().set("margin-bottom", "15px");

			this.commentLabel.addClassName("adr-vote-heading");

			this.thumbsUpIcon.setSize("35px");
			this.thumbsDownIcon.setSize("35px");
			this.thumbsUpIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR);
			this.thumbsDownIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR);
			if (!adr.getStatus().isVotable()) {
				this.thumbsUpIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR_DISABLED);
				this.thumbsDownIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR_DISABLED);
			}

			this.votingNotYetVotedLabel.getStyle().set("font-weight", "bold");
			this.votingNotYetVotedLabel.getStyle().set("padding", "10px");

			this.votingProLabel.addClassName("adr-vote-pro-label");
			this.votingContraLabel.addClassName("adr-vote-contra-label");
			this.votingSeparatorLabel.addClassName("adr-vote-separator-label");

			this.sendButton.addClassName("confirm-button");
			this.sendMessageArea.addClassName("adr-vote-message-text-area");

			this.commentMessagesLayout.addClassName("adr-vote-message-layout");

			this.sendCommentButtonLayout.addClassName("adr-vote-send-message-layout");

			this.editButton.addClassName("confirm-button");
			this.accessGroupsButton.addClassName("confirm-button");
			this.startVoteButton.addClassName("confirm-button");
			this.proposeDirectlyButton.addClassName("confirm-button");
			this.endVoteButton.addClassName("confirm-button");

			this.inviteVoterButton.addClassName("confirm-button");
		}

		// listener
		//addSendButtonListener();
		setMessageInputSubmitter();
		addSendButtonShortcut();
		addEditButtonListener();
		addEditAccessGroupsButtonListener();
		addInviteVoterButtonListener();
		addVoteIconsListener();
		addStartVoteButtonListener();
		addEndVoteButtonListener();

	}

	private void setupStatusAndAuthorLabel(ADR adr) {
		this.statusLabel = new Label(adr.getStatus().getType().getName());
		this.statusLabel.setClassName("adr-vote-status-label-" + adr.getStatus().getType().getName().toLowerCase().replaceAll(" ", "-"));
		this.authorLabel.addClassName("adr-vote-information-author-label");
	}

	private void addDescribingLabels() {
		adrDao.findById(adrId).ifPresent(adr -> {
			this.titleDiv.add(this.titleLabel = new Label(adr.getTitle()));
			this.authorDiv.add(this.authorLabel = new Label(adr.getAuthor().isPresent() ? adr.getAuthor().get().getUserName() : StringConstantsFrontend.GENERAL_UNKNOWN_AUTHOR));
			this.contextDiv.add(this.contextLabel = new RichTextEditor(adr.getContext()));
			this.decisionDiv.add(this.decisionLabel = new RichTextEditor(adr.getDecision()));
			this.consequencesDiv.add(this.consequencesLabel = new RichTextEditor(adr.getConsequences()));

			List<ADR> supersededADRs = new LinkedList<>();
			adr.getSupersededIds().forEach(id -> adrDao.findById(id).ifPresent(supersededADRs::add));
			supersededADRs.forEach(supersededAdr -> {
				Span tagSpan = new Span(supersededAdr.getTitle());
				tagSpan.addClassName("adr-vote-information-layout-superseded-adr-span");
				this.supersedesDiv.add(tagSpan);

			});
		});
	}

	private void updateDescribingLabels() {
		adrDao.findById(adrId).ifPresent(adr -> getUI().ifPresent(ui -> ui.access(() -> {
			this.titleDiv.remove(this.titleLabel);
			this.adrInformationAuthorStatusLayout.remove(this.statusLabel);
			this.adrInformationAuthorStatusLayout.remove(this.authorLabel);
			this.contextDiv.remove(this.contextLabel);
			this.decisionDiv.remove(this.decisionLabel);
			this.consequencesDiv.remove(this.consequencesLabel);
			this.supersedesLayout.remove(this.supersedesDiv);

			this.titleLabel.setText(adr.getTitle());
			this.statusLabel.setText(adr.getStatus().getType().getName());
			this.authorLabel.setText(adr.getAuthor().isPresent() ? adr.getAuthor().get().getUserName() : StringConstantsFrontend.GENERAL_UNKNOWN_AUTHOR);
			this.contextLabel = new RichTextEditor(adr.getContext());
			this.decisionLabel = new RichTextEditor(adr.getDecision());
			this.consequencesLabel = new RichTextEditor(adr.getConsequences());
			this.supersedesDiv = new Div();

			List<ADR> supersededADRs = new LinkedList<>();
			adr.getSupersededIds().forEach(id -> adrDao.findById(id).ifPresent(supersededADRs::add));
			supersededADRs.forEach(supersededAdr -> {
				Span tagSpan = new Span(supersededAdr.getTitle());
				tagSpan.addClassName("adr-vote-information-layout-superseded-adr-span");
				this.supersedesDiv.add(tagSpan);
			});

			this.titleDiv.add(this.titleLabel);
			this.adrInformationAuthorStatusLayout.add(this.statusLabel);
			this.adrInformationAuthorStatusLayout.addComponentAsFirst(this.authorLabel);
			this.contextDiv.add(this.contextLabel);
			this.decisionDiv.add(this.decisionLabel);
			this.consequencesDiv.add(this.consequencesLabel);
			this.supersedesLayout.add(this.supersedesDiv);
		})));
	}

	private void setMessageInputSubmitter() {
		this.collabMessageList.setSubmitter(activationContext -> {
			Registration registration = this.sendButton.addClickListener(event -> {
				String tempValue = this.sendMessageArea.getValue().trim();
				if (!tempValue.isEmpty()) {
					activationContext.appendMessage(tempValue);
					this.sendMessageArea.clear();
				}
			});

			return () -> {
				registration.remove();
				this.sendButton.setEnabled(false);
			};
		});
	}

	private synchronized void addComponentsToLayouts(@NonNull final ADR adr) {
		// setup
		setupComponents(adr);
		// add titles to main right and left side layout

		this.adrTopInformationLayout.add(this.adrInformationLayout, this.adrInformationAuthorStatusLayout, this.adrActionButtonsLayout);

		this.adrInformationLayout.add(this.adrInformationLabel);

/*		if (adr.canWrite(this.userDao.getCurrentUser())) {
			this.adrActionButtonsLayout.add(this.editButton);
		}

		if(adr.canEditAccessGroups(this.userDao.getCurrentUser())){
			this.adrActionButtonsLayout.add(this.accessGroupsButton);
		}
*/
		this.leftLayout.add(this.adrTopInformationLayout);

		/* Left side (ADR Information) */
		// adding components to vertical adr information layouts
		this.titleLayout.add(this.titleStringLabel, this.titleDiv);
		this.authorLayout.add(this.authorStringLabel, this.authorDiv);
		this.contextLayout.add(this.contextStringLabel, this.contextDiv);
		this.decisionLayout.add(this.decisionStringLabel, this.decisionDiv);
		this.consequencesLayout.add(this.consequencesStringLabel, this.consequencesDiv);

		// add only if not empty
		if (!adr.getSupersededIds().isEmpty()) {
			this.supersedesLayout.add(this.supersedesStringLabel, this.supersedesDiv);
		}

		// add vertical adr information layouts to main left layout - leftLayout
		//this.leftLayout.add(this.titleLayout, this.authorLayout, this.contextLayout, this.decisionLayout, this.consequencesLayout, this.supersedesLayout, this.adrButtonsLayout);
		this.leftLayout.add(this.contextLayout, this.decisionLayout, this.consequencesLayout, this.supersedesLayout, this.adrButtonsLayout);

		this.adrButtonsLayout.add(this.actionButtonsLayout);

		/* Right side (voting and comments) */
		this.voteTitleLayout.add(this.voteLabel);

		this.voteLayout.add(this.voteTitleLayout);

		this.commentLayout.add(this.commentLabel);

		this.voteLayout.add(this.voteIconLayout);

		this.voteResultBar = new VoteResultBar();
		this.voteLayout.add(this.voteResultBar);

		this.setVariableVotingSectionElements();

		this.commentMessagesLayout.add(this.collabMessageList);
		this.commentLayout.add(this.commentMessagesLayout);

		this.sendCommentButtonLayout.add(this.sendMessageArea, this.sendButton);

		this.commentLayout.add(this.sendCommentButtonLayout);

		this.rightLayout.add(this.voteLayout);
		this.rightLayout.add(this.commentLayout);

		// adding main side layouts to overall main layout
		this.add(this.leftLayout);
		this.add(this.rightLayout);
	}

	private void addSendButtonShortcut() {
		this.sendButton.addClickShortcut(Key.ENTER);
	}

	private void addEditButtonListener() {
		this.editButton.addClickListener(event -> {
			String route = RouteConfiguration.forSessionScope().getUrl(ADRRichCreateView.class, adrId);
			this.getUI().ifPresent((ui) -> ui.getPage().setLocation(route));
		});
	}

	private void addEditAccessGroupsButtonListener() {
		this.accessGroupsButton.addClickListener(event -> {
			AccessGroupDialog dialog = new AccessGroupDialog(this.accessGroupService, this.adrDao, this.adrId);
			//dialog.addDetachListener(event2 -> Broadcaster.broadcastMessage(Event.ADR_ACCESS_GROUPS_CHANGED, adrId, this));
			dialog.open();
		});
	}

	private void addInviteVoterButtonListener() {
		this.inviteVoterButton.addClickListener(event -> adrDao.findById(adrId).ifPresent(adr -> adr.getStatus().adrReviewAsOpt().ifElse(review -> {
					InviteVoterDialog dialog = new InviteVoterDialog(this.userDao, this.adrDao, adr);
					dialog.addDetachListener(event2 -> {
						Broadcaster.broadcastMessage(Event.ADR_REVIEW_STS_CHANGED, adrId, this);
						setVariableVotingSectionElements();
					});
					dialog.open();
				},
				() -> new ErrorNotification(StringConstantsFrontend.ADRVOTEVIEW_ADR_USERS_CURRENTLY_NOT_INVITABLE).open())));
	}

	private void addStartVoteButtonListener() {
		this.startVoteButton.addClickListener(event -> {
			synchronized (adrDao) {
				adrDao.findById(adrId).ifPresent((adr) -> {
					if (!adr.canStartVoting(userDao.getCurrentUser())) return;
					adr.propose();
					this.adrDao.save(adr);
					adr.getStatus().adrReviewAsOpt().ifPresent(review ->
							userDao
									.getCurrentUser()
									.pushNotification(new VotingPendingNotification(review, LocalDateTime.now())));
				});
				//this.getUI().ifPresent((ui) -> ui.getPage().reload());
			}
			Broadcaster.broadcastMessage(Event.ADR_REVIEW_STS_CHANGED, adrId, this);
			this.setVariableVotingSectionElements();
		});
		this.proposeDirectlyButton.addClickListener(event -> {
			synchronized (adrDao) {
				adrDao.findById(adrId).ifPresent((adr) -> {
					if (!adr.canPropose(userDao.getCurrentUser())) return;
					adr.propose();
					if (adr.canPropose(userDao.getCurrentUser())) adr.propose();
					this.adrDao.save(adr);
					adr.getStatus().adrReviewAsOpt().ifPresent(review ->
							userDao
									.getCurrentUser()
									.pushNotification(new VotingPendingNotification(review, LocalDateTime.now())));
				});
				this.getUI().ifPresent((ui) -> ui.getPage().reload());
			}
			Broadcaster.broadcastMessage(Event.ADR_REVIEW_STS_CHANGED, adrId, this);
			//this.setVariableVotingSectionElements();
		});
	}

	private void addEndVoteButtonListener() {
		this.endVoteButton.addClickListener(event -> {
			synchronized (adrDao) {
				adrDao.findById(adrId).ifPresent((adr) -> {
					ADR.VoteResult res = adr.endVoting(adrDao);
					if (res.equals(ADR.VoteResult.INTERNALLY_APPROVED)) {
						adr.getStatus().adrReviewAsOpt().ifPresent(review ->
								userDao
										.getCurrentUser()
										.pushNotification(new VotingPendingNotification(review, LocalDateTime.now())));
					}

					this.adrDao.save(adr);
					Broadcaster.broadcastMessage(Event.ADR_REVIEW_STS_CHANGED, adrId, this);
					this.setVariableVotingSectionElements();
				});
			}
		});
	}

	static private final class VoteSts {
		static final int ADR_NOT_PROPOSED = -2, USER_NOT_PERMITTED_TO_VOTE = -1, VOTE_WITHDRAWN = 0, VOTE_GIVEN = 1;
	}

	private void addVoteIconsListener() {
		this.thumbsUpIcon.addClickListener(event -> {
			AtomicInteger voteSuccess = new AtomicInteger(VoteSts.ADR_NOT_PROPOSED);
			synchronized (adrDao) {
				adrDao.findById(adrId).ifPresent((adr) -> adr.getStatus().adrReviewAsOpt().ifPresent(adrReview -> {
					if (adrReview.getUserVote(this.userDao.getCurrentUser()).isEmpty() || !adrReview.getUserVote(this.userDao.getCurrentUser()).get().equals(VoteType.FOR)) {
						try {
							if (adr.getStatus().isVotable()) {
								adrReview.putVote(this.userDao.getCurrentUser(), VoteType.FOR);
								this.adrDao.save(adr);
								voteSuccess.set(VoteSts.VOTE_GIVEN);
							}
						} catch (UserIsNotInvitedException e) {
							voteSuccess.set(VoteSts.USER_NOT_PERMITTED_TO_VOTE);
						}
					} else if (adr.getStatus().isVotable()) {
						try {
							adrReview.removeVote(this.userDao.getCurrentUser());
							this.adrDao.save(adr);
							voteSuccess.set(VoteSts.VOTE_WITHDRAWN);
						} catch (UserIsNotInvitedException e) {
							voteSuccess.set(VoteSts.USER_NOT_PERMITTED_TO_VOTE);
						}
					}
				}));
			}
			switch (voteSuccess.get()) {
				case VoteSts.VOTE_GIVEN:
					Broadcaster.broadcastMessage(Event.ADR_REVIEW_VOTE_CNT_CHANGED, adrId, this);
					new SuccessNotification(StringConstantsFrontend.ADRVOTEVIEW_ADR_SUCCESSFULLY_VOTED).open();
					setVariableVotingSectionElements();
					break;
				case VoteSts.VOTE_WITHDRAWN:
					Broadcaster.broadcastMessage(Event.ADR_REVIEW_VOTE_CNT_CHANGED, adrId, this);
					new SuccessNotification(StringConstantsFrontend.ADRVOTEVIEW_ADR_VOTE_REFUSED).open();
					setVariableVotingSectionElements();
					break;
				case VoteSts.USER_NOT_PERMITTED_TO_VOTE:
					new ErrorNotification(StringConstantsFrontend.ADRVOTEVIEW_ADR_NOT_ALLOWED_TO_VOTE).open();
				default:
					new ErrorNotification(StringConstantsFrontend.ADRVOTEVIEW_ADR_NO_LONGER_PROPOSED).open();
			}
		});

		this.thumbsDownIcon.addClickListener(event -> {

			AtomicInteger voteSuccess = new AtomicInteger(VoteSts.ADR_NOT_PROPOSED); //-2 = ADR not proposed; -1 = user not permitted to vote; 0 = vote successfully withdrawn; 1 = vote successfully given
			synchronized (adrDao) {
				adrDao.findById(adrId).ifPresent((adr) ->
						adr.getStatus().adrReviewAsOpt().ifPresent(adrReview -> {
							if (adrReview.getUserVote(this.userDao.getCurrentUser()).isEmpty() ||
									!adrReview.getUserVote(this.userDao.getCurrentUser()).get().equals(VoteType.AGAINST)) {
								try {
									if (adr.getStatus().isVotable()) {
										adrReview.putVote(this.userDao.getCurrentUser(), VoteType.AGAINST);
										this.adrDao.save(adr);
										voteSuccess.set(VoteSts.VOTE_GIVEN);
									}
								} catch (UserIsNotInvitedException e) {
									voteSuccess.set(-1);
								}
							}
							// voted
							else if (adr.getStatus().isVotable()) {
								try {
									adrReview.removeVote(this.userDao.getCurrentUser());
									this.adrDao.save(adr);
									voteSuccess.set(VoteSts.VOTE_WITHDRAWN);
								} catch (UserIsNotInvitedException e) {
									voteSuccess.set(VoteSts.USER_NOT_PERMITTED_TO_VOTE);
								}
							}
						})
				);
			}
			switch (voteSuccess.get()) {
				case VoteSts.VOTE_GIVEN:
					Broadcaster.broadcastMessage(Event.ADR_REVIEW_VOTE_CNT_CHANGED, adrId, this);
					new SuccessNotification(StringConstantsFrontend.ADRVOTEVIEW_ADR_SUCCESSFULLY_VOTED).open();
					setVariableVotingSectionElements();
					break;
				case VoteSts.VOTE_WITHDRAWN:
					Broadcaster.broadcastMessage(Event.ADR_REVIEW_VOTE_CNT_CHANGED, adrId, this);
					new SuccessNotification(StringConstantsFrontend.ADRVOTEVIEW_ADR_VOTE_REFUSED).open();
					setVariableVotingSectionElements();
					break;
				case VoteSts.USER_NOT_PERMITTED_TO_VOTE:
					new ErrorNotification(StringConstantsFrontend.ADRVOTEVIEW_ADR_NOT_ALLOWED_TO_VOTE).open();
				default:
					new ErrorNotification(StringConstantsFrontend.ADRVOTEVIEW_ADR_NO_LONGER_PROPOSED).open();
			}
		});
	}

	private void updateVotingResults() {
		VoteResultBar newBar = new VoteResultBar();
		this.voteLayout.replace(this.voteResultBar, newBar);
		this.voteResultBar = newBar;
	}

	private void updateVotingResults(ADRReview adrReview) {
		VoteResultBar newBar = new VoteResultBar(adrReview.getVoteResult().get(VoteType.FOR), adrReview.getVoteResult().get(VoteType.AGAINST), adrReview.getInvitedVoters().size());
		this.voteLayout.replace(this.voteResultBar, newBar);
		this.voteResultBar = newBar;
	}

	private void setVoteSection(int votedFor, boolean activeVoting) {
		this.voteIconLayout.removeAll();

		if (votedFor == 1) {
			if (activeVoting) {
				this.thumbsUpIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_UP_COLOR_VALUE);
				this.thumbsDownIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR);
			} else {
				this.thumbsUpIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_UP_COLOR_VALUE_DISABLED);
				this.thumbsDownIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR_DISABLED);
			}
		} else if (votedFor == -1) {
			if (activeVoting) {
				this.thumbsDownIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DOWN_COLOR_VALUE);
				this.thumbsUpIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR);
			} else {
				this.thumbsDownIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DOWN_COLOR_VALUE_DISABLED);
				this.thumbsUpIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR_DISABLED);
			}
		} else {
			if (activeVoting) {
				this.thumbsUpIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR);
				this.thumbsDownIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR);
			} else {
				this.thumbsUpIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR_DISABLED);
				this.thumbsDownIcon.setColor(StringConstantsFrontend.ADRVOTEVIEW_ICON_THUMBS_DEFAULT_COLOR_DISABLED);
			}
		}

		this.voteIconLayout.add(this.thumbsUpIcon);
		this.voteIconLayout.add(this.votingSeparatorLabel);
		this.voteIconLayout.add(this.thumbsDownIcon);
	}

	private void setVariableVotingSectionElements() {
		User user = userDao.getCurrentUser();
		if (user == null) return;

		this.adrDao.findById(adrId).ifPresent(adr -> {
			this.adrInformationAuthorStatusLayout.removeAll();
			this.setupStatusAndAuthorLabel(adr);
			this.adrInformationAuthorStatusLayout.add(this.authorLabel, this.separatorAuthorStatus, this.statusLabel);

			if (adr.canInviteVoters(this.userDao.getCurrentUser())) {
				this.voteTitleLayout.addComponentAsFirst(this.inviteVoterButton);
				this.voteTitleLayout.remove(this.voteLabel);
				this.voteTitleLayout.addComponentAsFirst(this.voteLabel);
			} else {
				this.voteTitleLayout.remove(this.inviteVoterButton);
			}

			if (adr.canEndVoting(this.userDao.getCurrentUser())) {
				this.voteTitleLayout.add(this.endVoteButton);
			} else {
				this.voteTitleLayout.remove(this.endVoteButton);
			}

			if (adr.canStartVoting(this.userDao.getCurrentUser())) {
				this.voteTitleLayout.add(this.startVoteButton);
			} else {
				this.voteTitleLayout.remove(this.startVoteButton);
				if (adr.getStatus().isVotingStartable()) {
					this.voteIconLayout.add(this.voteNotStartedYet);
				} else {
					this.voteIconLayout.remove(this.voteNotStartedYet);
				}
			}

			if (adr.canWrite(user)) {
				this.adrActionButtonsLayout.add(this.editButton);
			} else {
				this.adrActionButtonsLayout.remove(this.editButton);
			}

			if (adr.canEditAccessGroups(user)) {
				this.adrActionButtonsLayout.add(this.accessGroupsButton);
			} else {
				this.adrActionButtonsLayout.remove(this.accessGroupsButton);
			}

			if (adr.canPropose(this.userDao.getCurrentUser())) {
				this.voteTitleLayout.add(this.proposeDirectlyButton);
			} else {
				this.voteTitleLayout.remove(this.proposeDirectlyButton);
			}

			if (adr.getStatus().getType() != ADRStatusType.CREATED) {
				adr.getStatus().adrReviewAsOpt().ifPresent(review -> {
					if (review.userHasVoted(user).isPresent()) {
						if (review.userHasVoted(user).get() && review.getUserVote(user).isPresent()) {
							setVoteSection(review.getUserVote(user).get() == VoteType.FOR ? 1 : -1, adr.getStatus().isVotable());
						} else {
							setVoteSection(0, adr.getStatus().isVotable());
						}
					} else {
						this.voteIconLayout.removeAll();
					}
					updateVotingResults(review);
				});
			} else {
				updateVotingResults();
				this.voteIconLayout.removeAll();
			}
		});
	}

	/* Overrides */
	@Override
	public void setParameter(BeforeEvent beforeEvent, String adrId) {
		this.adrId = adrId;
		Optional<ADR> foundADR = this.adrDao.findById(adrId);
		if (foundADR.isEmpty()) {

			/* Error Message ADR not found */
			this.invalidIdNotification = new ErrorNotification();
			this.invalidIdNotification.setDuration(0);
			this.invalidIdNotification.open();

			Label msg = new Label(StringConstantsFrontend.ADRCREATE_ERROR_ID_NOT_FOUND);

			Button confirm = new Button(StringConstantsFrontend.GENERAL_BUTTON_CONFIRM);
			confirm.getStyle().set("margin-left", "auto");
			confirm.addClickShortcut(Key.ENTER);

			VerticalLayout layout = new VerticalLayout(msg, confirm);

			this.invalidIdNotification.add(layout);

			// confirm button click-listener
			confirm.addClickListener(event -> this.invalidIdNotification.close());

			// redirect user to kanban view
			this.invalidIdNotification.addOpenedChangeListener(event ->
					this.getUI().ifPresent(page ->
							page.getPage().setLocation(StringConstantsFrontend.OVERVIEW_PATH)));
		} else {
			/* constructor */

			addComponentsToLayouts(foundADR.get());
		}

	}

	@Override
	public void receiveBroadcast(Event event, String message) {
		switch (event) {
			case ADR_REVIEW_VOTE_CNT_CHANGED -> {
				if (adrId != null && adrId.equals(message))
					getUI().ifPresent(ui -> ui.access(() -> {
						synchronized (adrDao) {
							adrDao.findById(adrId)
									.flatMap(adr -> adr.getStatus().adrReviewAsOpt().stdOpt())
									.ifPresent(this::updateVotingResults);
						}
					}));
			}
			case ADR_CHANGED -> updateDescribingLabels();
			case ADR_REVIEW_STS_CHANGED -> {
				if (adrId != null && adrId.equals(message)) {
					getUI().ifPresent(ui -> ui.access(this::setVariableVotingSectionElements));
				}
			}
			case ADR_ACCESS_GROUPS_CHANGED -> {
				if (adrId != null && adrId.equals(message)) {
					getUI().ifPresent(ui -> ui.access(() -> {
						synchronized (adrDao) {
							adrDao.findById(adrId).ifPresent(adr -> {
								this.actionButtonsLayout.remove(editButton);
								if(adr.canWrite(userDao.getCurrentUser())){
									this.actionButtonsLayout.addComponentAsFirst(editButton);
								}
							});
						}
					}));
				}
			}
		}
	}

	@Override
	public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
		adrDao.findById(adrId).ifPresentOrElse(adr -> {
			if (adr.canRead(userDao.getCurrentUser())) beforeEnterEvent.rerouteTo(StringConstantsFrontend.LANDING_PAGE_PATH);
		}, () -> beforeEnterEvent.rerouteTo(StringConstantsFrontend.LANDING_PAGE_PATH));
	}

	@Override
	public void beforeLeave(BeforeLeaveEvent beforeLeaveEvent) {
		Broadcaster.unregisterListener(this);
	}
}

src/main/java/com/buschmais/frontend/views/adrCreate/ADRRichCreateView.java
@CssImport(value = "./themes/adr-workbench/vaadin-components/buttons.css")
@PageTitle(StringConstantsFrontend.ADRCREATE_EDITADR)
public class ADRRichCreateView extends VerticalLayout implements HasUrlParameter<String>, BeforeEnterObserver {

	//name of new ADR
	private final TextField name = new TextField(StringConstantsFrontend.ADRCREATE_NAME);
	//title of new ADR
	private final TextField title = new TextField(StringConstantsFrontend.ADRCREATE_TITLE);
	//context, decision and consequences of new ADR

	private final Label contextLabel = new Label(StringConstantsFrontend.ADRCREATE_CONTEXT);
	private final RichTextEditor context = new RichTextEditor();
	private final Label decisionLabel = new Label(StringConstantsFrontend.ADRCREATE_DECISION);
	private final RichTextEditor decision = new RichTextEditor();
	private final Label consequencesLabel = new Label(StringConstantsFrontend.ADRCREATE_CONSEQUENCES);
	private final RichTextEditor consequences = new RichTextEditor();
	private final TextField id = new TextField();

	private ErrorNotification errNot;
	private SuccessNotification succNot;

	//Scroller so that we can fit everything
	private Scroller scroller;

	//Contents of the Scroller
	private final VerticalLayout mainContent = new VerticalLayout();
	//Title of the Scroller
	H2 editADR = new H2();
	private final Header header = new Header();

	private TagSelect tagSelect;

	private StringSelect supersededSelect;

	private final ADRDao adrDao;
	private final UserDao userDao;

	private ADR adr;

	private boolean newadr = true;

	Binder<ADR> binder = new Binder<>(ADR.class);

	public ADRRichCreateView(@Autowired ADRDao adrDao, @Autowired UserDao userDao) {
		this.userDao = userDao;
		this.adrDao = adrDao;
		setSizeFull();

		//setClassNames();

		context.setWidthFull();
		context.setHeight("300px");

		//context.setValueChangeMode(ValueChangeMode.EAGER);
		//context.addValueChangeListener(e -> e.getSource().setHelperText(e.getValue().length() + "/" + 100));

		decision.setWidthFull();
		decision.setHeight("300px");
		//decision.setValueChangeMode(ValueChangeMode.EAGER);
		//decision.addValueChangeListener(e -> e.getSource().setHelperText(e.getValue().length() + "/" + 50));

		consequences.setWidthFull();
		consequences.setHeight("300px");
		//consequences.setValueChangeMode(ValueChangeMode.EAGER);
		//consequences.addValueChangeListener(e -> e.getSource().setHelperText(e.getValue().length() + "/" + 80));

		//create the Scroller with the Content of the Editor
		scroller = new Scroller(mainContentCreate());
		scroller.setSizeFull();
		scroller.setScrollDirection(Scroller.ScrollDirection.VERTICAL);

		//add the Title of the Page to the Header
		header.add(editADR);

		add(header, scroller);

		configureBinder();

	}

	//buttonsview to create
	private HorizontalLayout buttonCreate() {

		Button save = new Button(StringConstantsFrontend.GENERAL_DIALOG_BUTTON_SAVE);
		save.addClassName("confirm-button");
		Button cancel = new Button(StringConstantsFrontend.GENERAL_DIALOG_BUTTON_CANCEL);
		cancel.addClassName("cancel-button");
		Button create = new Button(StringConstantsFrontend.ADRCREATE_CREATE);
		create.addClassName("blue-button");
		Button load = new Button(StringConstantsFrontend.ADRCREATE_LOAD);
		load.addClassName("blue-button");

		save.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);

		save.addClickListener((event -> {
			boolean success = false;
			if (newadr) {
				adr = new ADR(
						Application.root,
						name.getValue(),
						title.getValue(),
						userDao.getCurrentUser(),
						context.getValue(), consequences.getValue(), decision.getValue());
				adr.setStatus(new ADRStatusCreated());

				adr.getTags().clear();
				tagSelect.getValue().forEach(tag -> adr.addTag(tag));

				synchronized (adrDao) {
					List<String> supersededTitles = supersededSelect.getSelected();
					Set<String> supersededIds = new HashSet<>();
					for (String name : supersededTitles) {
						supersededIds.addAll(adrDao.findAllByTitle(name).stream().map(ADR::getId).collect(Collectors.toSet()));
					}
					adr.setSupersededIds(supersededIds);

					adr = adrDao.save(adr);
				}

				success = true;
			} else {
				synchronized (adrDao) {
					adr = adrDao.findById(adr.getId()).orElse(null);
					if (adr != null) {
						adr.setTitle(title.getValue());
						adr.setName(name.getValue());
						adr.setContext(context.getValue());
						adr.setConsequences(consequences.getValue());
						adr.setDecision(decision.getValue());

						adr.getTags().clear();
						tagSelect.getValue().forEach(tag -> adr.addTag(tag));

						List<String> supersededTitles = supersededSelect.getSelected();
						Set<String> supersededIds = new HashSet<>();
						for (String name : supersededTitles) {
							supersededIds.addAll(adrDao.findAllByTitle(name).stream().map(ADR::getId).collect(Collectors.toSet()));
						}
						adr.setSupersededIds(supersededIds);

						try {
							adrDao.save(adr);
							success = true;
						} catch (Exception e) {
							System.out.println(Arrays.toString(e.getStackTrace()));
						}
					}
				}
			}

			if (success) {
				succNot = new SuccessNotification(StringConstantsFrontend.ADRCREATE_SUCC);
				succNot.open();
				Broadcaster.broadcastMessage(BroadcastListener.Event.ADR_CHANGED, adr.getId(), this);
				//Change URL to include adrId
				String route = RouteConfiguration.forSessionScope().getUrl(ADRVoteView.class, this.adr.getId());    //TODO: Eric hier ^^
				this.getUI().ifPresent(ui -> ui.navigate(route));
			} else {
				errNot = new ErrorNotification(StringConstantsFrontend.ADRCREATE_ERROR_GENERAL);
				errNot.open();
			}
		}));

		cancel.addThemeVariants(ButtonVariant.LUMO_ERROR);
		cancel.addClickListener((event -> {
		}));

		create.addClickListener((event -> {
			newadr = true;
			loadVals("", "", new LinkedList<>(), Stream.empty(), "", "", "");

			String route = RouteConfiguration.forSessionScope().getUrl(ADRRichCreateView.class, "");
			this.getUI().ifPresent(page -> page.navigate(route));
		}));

		load.addClickListener((event -> {

			Notification not = new Notification();
			TextField txt = new TextField();
			Div diag = new Div(new Text("Id to Load (Temp):"));
			Button conf = new Button(StringConstantsFrontend.ADRCREATE_LOAD);
			conf.addClickListener((event1 -> {
				ADR adr1 = adrDao.findById(txt.getValue()).orElse(null);
				if (adr1 != null) {

					loadADR(adr1);

					String route = RouteConfiguration.forSessionScope().getUrl(ADRRichCreateView.class, this.adr.getId());
					this.getUI().ifPresent(page -> page.navigate(route));

				} else {
					errNot = new ErrorNotification(StringConstantsFrontend.ADRCREATE_ERROR_ID_NOT_FOUND);
					errNot.open();
					//Notification errnot = Notification.show("ID not Found");
				}
				not.close();
			}));
			txt.setWidth("300px");

			HorizontalLayout layout = new HorizontalLayout(diag, txt, conf);
			layout.setAlignItems(Alignment.CENTER);

			not.add(layout);
			not.setPosition(Notification.Position.BOTTOM_CENTER);
			not.open();

		}));
		HorizontalLayout buttons = new HorizontalLayout();
		buttons.add(save, cancel, create);
		return buttons;

	}

	private VerticalLayout mainContentCreate() {
		synchronized (adrDao) {
			tagSelect = new TagSelect(StringConstantsFrontend.ADR_TAGS, adrDao.findAllTagsMatchRegex(".*"));
			supersededSelect = new StringSelect(StringConstantsFrontend.ADRCREATE_SUPERSEEDES_FOLLOWING_ADRS);

			List<ADR> supersededADRs = new LinkedList<>();
			if (adr != null) adr.getSupersededIds().forEach(id -> adrDao.findById(id).ifPresent(supersededADRs::add));
			supersededSelect.load(adrDao.findAll().stream()
							.filter(adr -> !adr.isSuperseded())
							.map(ADR::getTitle)
							.filter(n -> this.adr == null || !n.equals(this.adr.getTitle())),
					supersededADRs.stream().map(ADR::getTitle));
		}
		mainContent.add(name, title, tagSelect, supersededSelect, contextLabel, context, decisionLabel, decision, consequencesLabel, consequences, buttonCreate());
		mainContent.setSizeUndefined();
		return mainContent;
	}

	public void loadVals(String name,
						 String title,
						 List<ADRTag> Tags,
						 Stream<String> superseded,
						 String context,
						 String decision,
						 String consequences) {
		this.name.setValue(name);
		this.title.setValue(title);
		this.tagSelect.setValue(Tags);
		this.context.load(context);
		this.decision.load(decision);
		this.consequences.load(consequences);
		this.supersededSelect.load(adrDao.findAll().stream()
						.filter(adr -> !adr.isSuperseded())
						.map(ADR::getTitle)
						.filter(n -> !n.equals(name)),
				superseded);
	}

	public void loadADR(ADR adr) {
		newadr = false;
		this.adr = adr;
		List<ADR> supersededADRs = new LinkedList<>();
		if (adr != null) adr.getSupersededIds().forEach(id -> adrDao.findById(id).ifPresent(supersededADRs::add));
		loadVals(adr.getName(), adr.getTitle(), adr.getTags(), supersededADRs.stream().map(ADR::getTitle), adr.getContext(), adr.getDecision(), adr.getConsequences());

	}

	private void configureBinder() {
		binder.forField(name)
				.withValidator(
						name -> !name.isEmpty(),
						StringConstantsFrontend.ADRCREATE_NAME + StringConstantsFrontend.ADRCREATE_ERROR_FIELD_EMPTY)
				.bind(ADR::getName, ADR::setName);
		binder.bind(title, ADR::getTitle, ADR::setTitle);
		binder.bind(context, ADR::getContext, ADR::setContext);
		binder.bind(decision, ADR::getDecision, ADR::setDecision);
		binder.bind(consequences, ADR::getConsequences, ADR::setConsequences);

	}

	@Override
	public void setParameter(BeforeEvent event, @OptionalParameter String parameter) {
		ADR adr1 = null;
		if (parameter != null) {
			this.editADR.setText(StringConstantsFrontend.ADRCREATE_EDITADR);
			adr1 = adrDao.findById(parameter).orElse(null);
		}else{
			this.editADR.setText(StringConstantsFrontend.ADRCREATE_CREATEADR);
		}

		if (adr1 != null) {
			loadADR(adr1);
		}//else{
		//Notification errnot = Notification.show(StringConstantsFrontend.ADRCREATE_ERROR_ID_NOT_FOUND);
		//}
	}

	@Override
	public void beforeEnter(BeforeEnterEvent beforeEnterEvent) {
		if (adr == null || adr.getId() == null) return;
		adrDao.findById(this.adr.getId()).ifPresentOrElse(adr -> {
			if (!adr.canWrite(userDao.getCurrentUser()))
				beforeEnterEvent.rerouteTo(StringConstantsFrontend.LANDING_PAGE_PATH);
		}, () -> beforeEnterEvent.rerouteTo(StringConstantsFrontend.LANDING_PAGE_PATH));
	}

}

Answer: new Route(StringConstantsFrontend.USER_CONTROL_PATH, UserControlView.class),

<think>
The file defines several arrays of Route objects. After the opening brace of ADMIN_ACCESSIBLE_ROUTES, the first element should be a Route for the admin‑only user‑control view, which is referenced elsewhere (e.g., in GroupsInformationView). Therefore the next line is the Route construction for the user‑control path.
</think>
new Route(StringConstantsFrontend.USER_CONTROL_PATH, UserControlView.class),