Added Accounts page

This commit is contained in:
Andrey Terentev 2024-03-30 11:42:29 +07:00 committed by Andrey Terentev
parent 11ef2b51c9
commit bbd55ed8b3
4 changed files with 238 additions and 0 deletions

View File

@ -0,0 +1,190 @@
package ru.dragonestia.picker.cp.component;
import com.vaadin.flow.component.Unit;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.checkbox.Checkbox;
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.html.H3;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.theme.lumo.LumoIcon;
import ru.dragonestia.picker.api.model.account.ResponseAccount;
import ru.dragonestia.picker.api.repository.AccountRepository;
import ru.dragonestia.picker.cp.model.Permission;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
public class AccountList extends VerticalLayout implements RefreshableTable {
private final AccountRepository accountRepository;
private final TextField searchField;
private final Grid<ResponseAccount> grid;
private List<ResponseAccount> cachedAccounts = new ArrayList<>();
public AccountList(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
add(searchField = createSearchField());
add(grid = createGridAccounts());
refresh();
}
private TextField createSearchField() {
var field = new TextField("Search by account username");
field.setPrefixComponent(new Icon(VaadinIcon.SEARCH));
field.setClearButtonVisible(true);
field.setHelperText("Press Enter to search");
field.addValueChangeListener(event -> applySearch(event.getValue()));
field.setValueChangeMode(ValueChangeMode.EAGER);
field.setMinWidth(30, Unit.PERCENTAGE);
return field;
}
private Grid<ResponseAccount> createGridAccounts() {
var grid = new Grid<>(ResponseAccount.class, false);
grid.addColumn(ResponseAccount::getUsername).setHeader("Username")
.setComparator(Comparator.comparing(ResponseAccount::getUsername)).setSortable(true);
grid.addComponentColumn(this::createAccountManagementButtons).setFrozenToEnd(true)
.setTextAlign(ColumnTextAlign.END).setHeader(createToolItems());
grid.setMultiSort(true, Grid.MultiSortPriority.APPEND);
return grid;
}
private HorizontalLayout createAccountManagementButtons(ResponseAccount account) {
var layout = new HorizontalLayout(JustifyContentMode.END);
{
var button = new Button("Manage", VaadinIcon.COG_O.create());
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
button.addClickListener(event -> {
});
layout.add(button);
}
return layout;
}
private HorizontalLayout createToolItems() {
var layout = new HorizontalLayout(JustifyContentMode.END);
layout.add(createRegisterAccountButton());
layout.add(createRefreshButton());
return layout;
}
private Button createRegisterAccountButton() {
var button = new Button("Register account", VaadinIcon.PLUS.create());
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
button.addClickListener(event -> openAccountRegistrationDialog());
return button;
}
@Override
public void refresh() {
var list = new ArrayList<ResponseAccount>();
for (int i = 0; i < 5; i++) {
var acc = new ResponseAccount("test" + i, "", Set.of(), false);
list.add(acc);
}
cachedAccounts = list; // TODO: accountRepository.getAllAccounts();
applySearch(searchField.getValue());
}
public void applySearch(String input) {
var temp = input.trim();
grid.setItems(cachedAccounts.stream()
.filter(account -> account.getUsername().startsWith(temp))
.toList());
}
private void openAccountRegistrationDialog() {
var dialog = new Dialog();
dialog.setHeaderTitle("Register new account");
var closeButton = new Button(LumoIcon.CROSS.create(), e -> dialog.close());
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
dialog.getHeader().add(closeButton);
var layout = new VerticalLayout();
var fieldUsername = new TextField("Account username");
fieldUsername.setWidth(70, Unit.PERCENTAGE);
layout.add(fieldUsername);
var fieldPassword = new PasswordField("Password");
fieldPassword.setWidth(70, Unit.PERCENTAGE);
layout.add(fieldPassword);
var fieldConfirmPassword = new PasswordField("Confirm password");
fieldConfirmPassword.setWidth(70, Unit.PERCENTAGE);
layout.add(fieldConfirmPassword);
layout.add(new H3("Permissions"));
var permissionsList = new ArrayList<PermissionCheckBox>();
for (var permission: Permission.Enum.values()) {
var comp = new PermissionCheckBox(permission);
permissionsList.add(comp);
layout.add(comp);
}
{
var button = new Button("Register");
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
button.setWidth(100, Unit.PERCENTAGE);
button.addClickListener(event -> {
validateAndRegister(dialog, fieldUsername, fieldPassword, fieldConfirmPassword, permissionsList);
});
dialog.getFooter().add(button);
}
dialog.add(layout);
dialog.setMinWidth(50, Unit.PERCENTAGE);
dialog.setCloseOnEsc(true);
dialog.setCloseOnOutsideClick(true);
dialog.open();
}
private void validateAndRegister(Dialog dialog, TextField username, PasswordField passwordField, PasswordField confirm, List<PermissionCheckBox> permissionCheckBoxes) {
// TODO: validate and send request
dialog.close();
refresh();
}
private static class PermissionCheckBox extends Checkbox {
private final Permission.Enum option;
public PermissionCheckBox(Permission.Enum option) {
super(option.getDescription());
this.option = option;
}
public Permission.Enum getOption() {
return option;
}
}
}

View File

@ -1,6 +1,7 @@
package ru.dragonestia.picker.cp.model; package ru.dragonestia.picker.cp.model;
import com.github.javaparser.quality.NotNull; import com.github.javaparser.quality.NotNull;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
public class Permission implements GrantedAuthority { public class Permission implements GrantedAuthority {
@ -15,4 +16,19 @@ public class Permission implements GrantedAuthority {
public String getAuthority() { public String getAuthority() {
return authority; return authority;
} }
@Getter
public enum Enum {
// All from ru.dragonestia.picker.model.Permission (server)
// Except for USER and ADMIN
NODE_MANAGEMENT("Create and remove nodes"),
;
private final String description;
Enum(String description) {
this.description = description;
}
}
} }

View File

@ -0,0 +1,29 @@
package ru.dragonestia.picker.cp.page;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import jakarta.annotation.security.RolesAllowed;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import ru.dragonestia.picker.api.repository.AccountRepository;
import ru.dragonestia.picker.cp.component.AccountList;
import ru.dragonestia.picker.cp.service.SecurityService;
@RolesAllowed("ADMIN")
@PageTitle("Accounts")
@Route(value = "/admin/accounts", layout = MainLayout.class)
@RequiredArgsConstructor
public class AccountsPage extends VerticalLayout {
private final AccountRepository accountRepository;
@Autowired
public AccountsPage(SecurityService securityService) {
accountRepository = securityService.getAuthenticatedAccount().getClient().getAccountRepository();
add(new H2("Account management"));
add(new AccountList(accountRepository));
}
}

View File

@ -74,6 +74,9 @@ public class MainLayout extends AppLayout {
var nav = new SideNav(); var nav = new SideNav();
nav.addItem(new SideNavItem("Nodes list", NodesPage.class, VaadinIcon.FOLDER_O.create())); nav.addItem(new SideNavItem("Nodes list", NodesPage.class, VaadinIcon.FOLDER_O.create()));
nav.addItem(new SideNavItem("Search users", UserSearchPage.class, VaadinIcon.SEARCH.create())); nav.addItem(new SideNavItem("Search users", UserSearchPage.class, VaadinIcon.SEARCH.create()));
if (isAdmin) {
nav.addItem(new SideNavItem("Accounts", AccountsPage.class, VaadinIcon.USERS.create()));
}
nav.addItem(new SideNavItem("Documentation", "https://github.com/ScarletRedMan/RoomPicker", VaadinIcon.BOOK.create())); nav.addItem(new SideNavItem("Documentation", "https://github.com/ScarletRedMan/RoomPicker", VaadinIcon.BOOK.create()));
nav.addItem(new SideNavItem("Swagger UI", serverUrl + "/api-docs-ui", VaadinIcon.CURLY_BRACKETS.create())); nav.addItem(new SideNavItem("Swagger UI", serverUrl + "/api-docs-ui", VaadinIcon.CURLY_BRACKETS.create()));
return nav; return nav;