diff --git a/src/main/java/de/assecutor/votianlt/model/AppDevice.java b/src/main/java/de/assecutor/votianlt/model/AppDevice.java new file mode 100644 index 0000000..21c9241 --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/model/AppDevice.java @@ -0,0 +1,37 @@ +package de.assecutor.votianlt.model; + +import lombok.Data; +import org.bson.types.ObjectId; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +import java.time.LocalDateTime; + +@Data +@Document(collection = "app_device") +public class AppDevice { + + @Id + private ObjectId id; + + @Field("name") + private String name; + + @Field("erstellt_am") + private LocalDateTime erstelltAm; + + @Field("erstellt_von") + private ObjectId erstelltVon; + + @Field("aktualisiert_am") + private LocalDateTime aktualisiertAm; + + @Field("aktualisiert_von") + private ObjectId aktualisiertVon; + + public AppDevice() { + this.erstelltAm = LocalDateTime.now(); + this.aktualisiertAm = LocalDateTime.now(); + } +} diff --git a/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java b/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java index 8e61838..eb92171 100644 --- a/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java +++ b/src/main/java/de/assecutor/votianlt/pages/base/ui/view/MainLayout.java @@ -80,7 +80,7 @@ public final class MainLayout extends AppLayout { SideNavItem jobs = new SideNavItem("Aufträge", "jobs", new Icon(VaadinIcon.LIST)); SideNavItem customers = new SideNavItem("Kunden", "customers", new Icon(VaadinIcon.USERS)); SideNavItem appUsers = new SideNavItem("App-Nutzer", "app-user", new Icon(VaadinIcon.USERS)); - SideNavItem devices = new SideNavItem("Endgeräte", "4", new Icon(VaadinIcon.COG)); + SideNavItem devices = new SideNavItem("Endgeräte", "app-devices", new Icon(VaadinIcon.MOBILE)); SideNavItem invoices = new SideNavItem("Rechnungen", "5", new Icon(VaadinIcon.COG)); SideNavItem statistics = new SideNavItem("Statistik", "6", new Icon(VaadinIcon.COG)); diff --git a/src/main/java/de/assecutor/votianlt/pages/service/AppDeviceService.java b/src/main/java/de/assecutor/votianlt/pages/service/AppDeviceService.java new file mode 100644 index 0000000..d4529b3 --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/pages/service/AppDeviceService.java @@ -0,0 +1,57 @@ +package de.assecutor.votianlt.pages.service; + +import de.assecutor.votianlt.model.AppDevice; +import de.assecutor.votianlt.repository.AppDeviceRepository; +import de.assecutor.votianlt.security.SecurityService; +import org.bson.types.ObjectId; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +public class AppDeviceService { + + private final AppDeviceRepository appDeviceRepository; + private final SecurityService securityService; + + @Autowired + public AppDeviceService(AppDeviceRepository appDeviceRepository, SecurityService securityService) { + this.appDeviceRepository = appDeviceRepository; + this.securityService = securityService; + } + + public AppDevice createAppDevice(AppDevice appDevice) { + // Set creation and update metadata + appDevice.setErstelltAm(java.time.LocalDateTime.now()); + appDevice.setAktualisiertAm(java.time.LocalDateTime.now()); + + // Set creator and updater - current user ID is required + ObjectId currentUserId = securityService.getCurrentUserId(); + appDevice.setErstelltVon(currentUserId); + appDevice.setAktualisiertVon(currentUserId); + + return appDeviceRepository.save(appDevice); + } + + public List findByCurrentUser() { + ObjectId currentUserId = securityService.getCurrentUserId(); + return appDeviceRepository.findByErstelltVon(currentUserId); + } + + public AppDevice findById(ObjectId id) { + return appDeviceRepository.findById(id).orElse(null); + } + + public AppDevice updateAppDevice(AppDevice appDevice) { + appDevice.setAktualisiertAm(java.time.LocalDateTime.now()); + appDevice.setAktualisiertVon(securityService.getCurrentUserId()); + return appDeviceRepository.save(appDevice); + } + + public void deleteById(ObjectId id) { + appDeviceRepository.deleteById(id); + } +} diff --git a/src/main/java/de/assecutor/votianlt/pages/service/AppUserService.java b/src/main/java/de/assecutor/votianlt/pages/service/AppUserService.java index 02f3dd0..61838f3 100644 --- a/src/main/java/de/assecutor/votianlt/pages/service/AppUserService.java +++ b/src/main/java/de/assecutor/votianlt/pages/service/AppUserService.java @@ -40,5 +40,22 @@ public class AppUserService { return appUserRepository.findAll(); } + public List findByCurrentUser() { + ObjectId currentUserId = securityService.getCurrentUserId(); + return appUserRepository.findByErstelltVon(currentUserId); + } + public AppUser findById(ObjectId id) { + return appUserRepository.findById(id).orElse(null); + } + + public AppUser updateAppUser(AppUser appUser) { + appUser.setAktualisiertAm(java.time.LocalDateTime.now()); + appUser.setAktualisiertVon(securityService.getCurrentUserId()); + return appUserRepository.save(appUser); + } + + public void deleteById(ObjectId id) { + appUserRepository.deleteById(id); + } } diff --git a/src/main/java/de/assecutor/votianlt/pages/view/AppDevicesView.java b/src/main/java/de/assecutor/votianlt/pages/view/AppDevicesView.java new file mode 100644 index 0000000..4b02d2d --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/pages/view/AppDevicesView.java @@ -0,0 +1,85 @@ +package de.assecutor.votianlt.pages.view; + +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonVariant; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.html.H2; +import com.vaadin.flow.component.icon.Icon; +import com.vaadin.flow.component.icon.VaadinIcon; +import com.vaadin.flow.component.orderedlayout.FlexComponent; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.PageTitle; +import com.vaadin.flow.router.Route; +import de.assecutor.votianlt.model.AppDevice; +import de.assecutor.votianlt.pages.service.AppDeviceService; +import jakarta.annotation.security.RolesAllowed; +import org.springframework.beans.factory.annotation.Autowired; + +@PageTitle("Endgeräte") +@Route(value = "app-devices", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class) +@RolesAllowed({"USER","ADMIN"}) +public class AppDevicesView extends VerticalLayout { + + private final AppDeviceService appDeviceService; + private final Grid appDeviceGrid; + + @Autowired + public AppDevicesView(AppDeviceService appDeviceService) { + this.appDeviceService = appDeviceService; + + setSizeFull(); + setPadding(true); + setSpacing(true); + + // Header mit Titel und Button + HorizontalLayout header = new HorizontalLayout(); + header.setWidthFull(); + header.setAlignItems(FlexComponent.Alignment.CENTER); + + H2 title = new H2("Endgeräte"); + title.getStyle().set("margin", "0"); + + Button addButton = new Button("Neues Endgerät anlegen", new Icon(VaadinIcon.PLUS)); + addButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + addButton.addClickListener(e -> navigateToAddAppDevice()); + + header.add(title, addButton); + header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN); + add(header); + + // Grid für Endgeräte + appDeviceGrid = new Grid<>(AppDevice.class, false); + appDeviceGrid.setSizeFull(); + + // Grid-Spalten konfigurieren + appDeviceGrid.addColumn(AppDevice::getName).setHeader("Gerätename").setAutoWidth(true); + appDeviceGrid.addColumn(AppDevice::getErstelltAm).setHeader("Erstellt am").setAutoWidth(true); + + // Make grid rows clickable + appDeviceGrid.setSelectionMode(Grid.SelectionMode.SINGLE); + appDeviceGrid.getStyle().set("cursor", "pointer"); + + // Add click listener to navigate to edit view + appDeviceGrid.addItemClickListener(event -> { + AppDevice appDevice = event.getItem(); + if (appDevice != null && appDevice.getId() != null) { + getUI().ifPresent(ui -> ui.navigate("edit-app-device/" + appDevice.getId().toHexString())); + } + }); + + add(appDeviceGrid); + + // Daten laden + loadAppDevices(); + } + + private void loadAppDevices() { + var appDevices = appDeviceService.findByCurrentUser(); + appDeviceGrid.setItems(appDevices); + } + + private void navigateToAddAppDevice() { + getUI().ifPresent(ui -> ui.navigate("add-app-device")); + } +} diff --git a/src/main/java/de/assecutor/votianlt/pages/view/AppUserView.java b/src/main/java/de/assecutor/votianlt/pages/view/AppUserView.java index 8f53904..b5cb60c 100644 --- a/src/main/java/de/assecutor/votianlt/pages/view/AppUserView.java +++ b/src/main/java/de/assecutor/votianlt/pages/view/AppUserView.java @@ -61,6 +61,18 @@ public class AppUserView extends VerticalLayout { appUserGrid.addColumn(AppUser::getEmail).setHeader("E-Mail").setAutoWidth(true); appUserGrid.addColumn(AppUser::getGeraet).setHeader("Gerät").setAutoWidth(true); + // Make grid rows clickable + appUserGrid.setSelectionMode(Grid.SelectionMode.SINGLE); + appUserGrid.getStyle().set("cursor", "pointer"); + + // Add click listener to navigate to edit view + appUserGrid.addItemClickListener(event -> { + AppUser appUser = event.getItem(); + if (appUser != null && appUser.getId() != null) { + getUI().ifPresent(ui -> ui.navigate("edit-app-user/" + appUser.getId().toHexString())); + } + }); + add(appUserGrid); // Daten laden @@ -68,7 +80,7 @@ public class AppUserView extends VerticalLayout { } private void loadAppUsers() { - var appUsers = appUserService.findAll(); + var appUsers = appUserService.findByCurrentUser(); appUserGrid.setItems(appUsers); } diff --git a/src/main/java/de/assecutor/votianlt/pages/view/EditAppUserView.java b/src/main/java/de/assecutor/votianlt/pages/view/EditAppUserView.java new file mode 100644 index 0000000..9fc495b --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/pages/view/EditAppUserView.java @@ -0,0 +1,212 @@ +package de.assecutor.votianlt.pages.view; + +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonVariant; +import com.vaadin.flow.component.combobox.ComboBox; +import com.vaadin.flow.component.formlayout.FormLayout; +import com.vaadin.flow.component.html.H2; +import com.vaadin.flow.component.icon.Icon; +import com.vaadin.flow.component.icon.VaadinIcon; +import com.vaadin.flow.component.notification.Notification; +import com.vaadin.flow.component.orderedlayout.FlexComponent; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.component.textfield.TextField; +import com.vaadin.flow.data.binder.Binder; +import com.vaadin.flow.data.binder.ValidationException; +import com.vaadin.flow.router.BeforeEvent; +import com.vaadin.flow.router.HasUrlParameter; +import com.vaadin.flow.router.PageTitle; +import com.vaadin.flow.router.Route; +import de.assecutor.votianlt.model.AppUser; +import de.assecutor.votianlt.pages.service.AppUserService; +import jakarta.annotation.security.RolesAllowed; +import org.bson.types.ObjectId; +import org.springframework.beans.factory.annotation.Autowired; + +@PageTitle("App-Nutzer bearbeiten") +@Route(value = "edit-app-user", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class) +@RolesAllowed({"USER","ADMIN"}) +public class EditAppUserView extends VerticalLayout implements HasUrlParameter { + + private final AppUserService appUserService; + private AppUser appUser; + private final Binder binder = new Binder<>(AppUser.class); + + // Form fields + private final TextField designationField = new TextField("Bezeichnung (HH H 000)"); + private final TextField firstnameField = new TextField("Vorname"); + private final TextField lastnameField = new TextField("Nachname"); + private final TextField phoneField = new TextField("Telefon (Mobil)"); + private final TextField appCodeField = new TextField("App-Code"); + private final TextField emailField = new TextField("E-Mail-Adresse"); + private final ComboBox deviceField = new ComboBox<>("kein Gerät"); + + @Autowired + public EditAppUserView(AppUserService appUserService) { + this.appUserService = appUserService; + setSizeFull(); + setPadding(true); + setSpacing(true); + + // Center content vertically + setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER); + setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER); + + // Create main content container + VerticalLayout contentContainer = new VerticalLayout(); + contentContainer.setWidth("600px"); + contentContainer.setMaxWidth("90%"); + contentContainer.setSpacing(true); + contentContainer.setPadding(true); + contentContainer.getStyle().set("background", "var(--lumo-contrast-5pct)"); + contentContainer.getStyle().set("border-radius", "var(--lumo-border-radius)"); + contentContainer.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)"); + + // Header with title and back button + HorizontalLayout header = new HorizontalLayout(); + header.setAlignItems(FlexComponent.Alignment.CENTER); + header.setSpacing(true); + + H2 title = new H2("App-Nutzer bearbeiten"); + title.getStyle().set("margin", "0"); + + Button backButton = new Button("Zurück", new Icon(VaadinIcon.ARROW_LEFT)); + backButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY); + backButton.addClickListener(e -> navigateBack()); + + header.add(title, backButton); + header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN); + contentContainer.add(header); + + // Form layout + FormLayout formLayout = new FormLayout(); + formLayout.setResponsiveSteps( + new FormLayout.ResponsiveStep("0", 1) + ); + + // Configure fields + designationField.setPlaceholder("(HH H 000)"); + designationField.setWidthFull(); + + firstnameField.setWidthFull(); + lastnameField.setWidthFull(); + + // Create horizontal layout for firstname and lastname + HorizontalLayout nameLayout = new HorizontalLayout(); + nameLayout.setWidthFull(); + nameLayout.setSpacing(true); + nameLayout.add(firstnameField, lastnameField); + + phoneField.setWidthFull(); + appCodeField.setWidthFull(); + emailField.setWidthFull(); + + // Configure device dropdown + deviceField.setItems("kein Gerät", "iPhone", "Android", "Tablet", "Desktop"); + deviceField.setWidthFull(); + + // Add fields to form + formLayout.add(designationField); + formLayout.add(nameLayout); + formLayout.add(phoneField); + formLayout.add(appCodeField); + formLayout.add(emailField); + formLayout.add(deviceField); + + contentContainer.add(formLayout); + + // Buttons + HorizontalLayout buttonLayout = new HorizontalLayout(); + buttonLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER); + buttonLayout.setWidthFull(); + + Button saveButton = new Button("Speichern", e -> saveAppUser()); + saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + + Button deleteButton = new Button("Löschen", e -> deleteAppUser()); + deleteButton.addThemeVariants(ButtonVariant.LUMO_ERROR); + + buttonLayout.add(saveButton, deleteButton); + buttonLayout.setSpacing(true); + contentContainer.add(buttonLayout); + + // Add content container to main layout + add(contentContainer); + + // Setup binder + setupBinder(); + } + + private void setupBinder() { + // Bind fields to AppUser properties + binder.forField(designationField).bind(AppUser::getBezeichnung, AppUser::setBezeichnung); + binder.forField(firstnameField).bind(AppUser::getVorname, AppUser::setVorname); + binder.forField(lastnameField).bind(AppUser::getNachname, AppUser::setNachname); + binder.forField(phoneField).bind(AppUser::getTelefon, AppUser::setTelefon); + binder.forField(appCodeField).bind(AppUser::getAppCode, AppUser::setAppCode); + binder.forField(emailField).bind(AppUser::getEmail, AppUser::setEmail); + binder.forField(deviceField).bind(AppUser::getGeraet, AppUser::setGeraet); + } + + @Override + public void setParameter(BeforeEvent event, String parameter) { + try { + ObjectId appUserId = new ObjectId(parameter); + appUser = appUserService.findById(appUserId); + + if (appUser == null) { + Notification.show("App-Nutzer nicht gefunden", 3000, Notification.Position.MIDDLE); + navigateBack(); + return; + } + + // Load app user data into form + binder.readBean(appUser); + + } catch (IllegalArgumentException e) { + Notification.show("Ungültige App-Nutzer-ID", 3000, Notification.Position.MIDDLE); + navigateBack(); + } + } + + private void saveAppUser() { + try { + binder.writeBean(appUser); + appUserService.updateAppUser(appUser); + Notification.show("App-Nutzer erfolgreich gespeichert", 3000, Notification.Position.MIDDLE); + navigateBack(); + } catch (ValidationException e) { + Notification.show("Bitte überprüfen Sie die Eingaben", 3000, Notification.Position.MIDDLE); + } + } + + private void deleteAppUser() { + // Show confirmation dialog + com.vaadin.flow.component.dialog.Dialog confirmDialog = new com.vaadin.flow.component.dialog.Dialog(); + confirmDialog.add("Möchten Sie diesen App-Nutzer wirklich löschen?"); + + HorizontalLayout buttonLayout = new HorizontalLayout(); + Button confirmDeleteButton = new Button("Ja, löschen", e -> { + if (appUser != null && appUser.getId() != null) { + appUserService.deleteById(appUser.getId()); + Notification.show("App-Nutzer erfolgreich gelöscht", 3000, Notification.Position.MIDDLE); + confirmDialog.close(); + navigateBack(); + } + }); + confirmDeleteButton.addThemeVariants(ButtonVariant.LUMO_ERROR); + + Button cancelDeleteButton = new Button("Abbrechen", e -> confirmDialog.close()); + + buttonLayout.add(confirmDeleteButton, cancelDeleteButton); + buttonLayout.setSpacing(true); + confirmDialog.add(buttonLayout); + + confirmDialog.open(); + } + + private void navigateBack() { + getUI().ifPresent(ui -> ui.navigate("app-user")); + } +} diff --git a/src/main/java/de/assecutor/votianlt/repository/AppDeviceRepository.java b/src/main/java/de/assecutor/votianlt/repository/AppDeviceRepository.java new file mode 100644 index 0000000..a3a7229 --- /dev/null +++ b/src/main/java/de/assecutor/votianlt/repository/AppDeviceRepository.java @@ -0,0 +1,15 @@ +package de.assecutor.votianlt.repository; + +import de.assecutor.votianlt.model.AppDevice; +import org.bson.types.ObjectId; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface AppDeviceRepository extends MongoRepository { + + // Find all devices created by a specific user + List findByErstelltVon(ObjectId erstelltVon); +} diff --git a/src/main/java/de/assecutor/votianlt/repository/AppUserRepository.java b/src/main/java/de/assecutor/votianlt/repository/AppUserRepository.java index 205b690..6bbe3c4 100644 --- a/src/main/java/de/assecutor/votianlt/repository/AppUserRepository.java +++ b/src/main/java/de/assecutor/votianlt/repository/AppUserRepository.java @@ -5,9 +5,14 @@ import org.bson.types.ObjectId; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface AppUserRepository extends MongoRepository { + // Find all AppUsers created by a specific user + List findByErstelltVon(ObjectId erstelltVon); + // Custom query methods can be added here if needed // For example: // List findByEmail(String email);