Delete qrs, add additionalDoors management, update entities to fit the schema
This commit is contained in:
		
							parent
							
								
									d2d0b98083
								
							
						
					
					
						commit
						8e70180266
					
				| @ -4,6 +4,7 @@ import com.vaadin.flow.component.Composite; | |||||||
| import com.vaadin.flow.component.Key; | import com.vaadin.flow.component.Key; | ||||||
| import com.vaadin.flow.component.button.Button; | import com.vaadin.flow.component.button.Button; | ||||||
| import com.vaadin.flow.component.button.ButtonVariant; | import com.vaadin.flow.component.button.ButtonVariant; | ||||||
|  | import com.vaadin.flow.component.checkbox.Checkbox; | ||||||
| import com.vaadin.flow.component.icon.VaadinIcon; | import com.vaadin.flow.component.icon.VaadinIcon; | ||||||
| import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | ||||||
| import com.vaadin.flow.component.orderedlayout.VerticalLayout; | import com.vaadin.flow.component.orderedlayout.VerticalLayout; | ||||||
| @ -49,12 +50,14 @@ public class ClientEditor extends Composite<VerticalLayout> { | |||||||
| 
 | 
 | ||||||
|     public ClientEditor() { |     public ClientEditor() { | ||||||
|         var email = new EmailField("E-mail"); |         var email = new EmailField("E-mail"); | ||||||
|  |         var emailIsConfirmed = new Checkbox("e-mail подтвержден"); | ||||||
| 
 | 
 | ||||||
|         var save = new Button("Сохранить", VaadinIcon.CHECK.create()); |         var save = new Button("Сохранить", VaadinIcon.CHECK.create()); | ||||||
|         var cancel = new Button("Отмена"); |         var cancel = new Button("Отмена"); | ||||||
|         var delete = new Button("Удалить", VaadinIcon.TRASH.create()); |         var delete = new Button("Удалить", VaadinIcon.TRASH.create()); | ||||||
| 
 | 
 | ||||||
|         binder.forField(email).bind("email"); |         binder.forField(email).bind("email"); | ||||||
|  |         binder.forField(emailIsConfirmed).bind("emailIsConfirmed"); | ||||||
| 
 | 
 | ||||||
|         save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); |         save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); | ||||||
|         save.addClickListener(e -> saveListener.onSave(currentClient)); |         save.addClickListener(e -> saveListener.onSave(currentClient)); | ||||||
| @ -65,6 +68,6 @@ public class ClientEditor extends Composite<VerticalLayout> { | |||||||
| 
 | 
 | ||||||
|         cancel.addClickListener(e -> cancelListener.onCancel()); |         cancel.addClickListener(e -> cancelListener.onCancel()); | ||||||
| 
 | 
 | ||||||
|         getContent().add(email, new HorizontalLayout(save, cancel, delete)); |         getContent().add(email, emailIsConfirmed, new HorizontalLayout(save, cancel, delete)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,4 +16,7 @@ public class ClientEntity { | |||||||
| 
 | 
 | ||||||
|     @Column |     @Column | ||||||
|     private String email; |     private String email; | ||||||
|  | 
 | ||||||
|  |     @Column | ||||||
|  |     private Boolean emailIsConfirmed; | ||||||
| } | } | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ public class ClientView extends VerticalLayout { | |||||||
| 
 | 
 | ||||||
|         var addButton = new Button("Добавить клиента", VaadinIcon.PLUS.create()); |         var addButton = new Button("Добавить клиента", VaadinIcon.PLUS.create()); | ||||||
|         grid = new Grid<>(ClientEntity.class); |         grid = new Grid<>(ClientEntity.class); | ||||||
|         grid.setColumns("id", "email"); |         grid.setColumns("id", "email", "emailIsConfirmed"); | ||||||
|         editor = new ClientEditor(); |         editor = new ClientEditor(); | ||||||
| 
 | 
 | ||||||
|         var actionsLayout = new HorizontalLayout(addButton); |         var actionsLayout = new HorizontalLayout(addButton); | ||||||
|  | |||||||
| @ -19,7 +19,6 @@ import ru.vyatsu.qr_access_admin.client.view.ClientView; | |||||||
| import ru.vyatsu.qr_access_admin.common.view.MainView; | import ru.vyatsu.qr_access_admin.common.view.MainView; | ||||||
| import ru.vyatsu.qr_access_admin.door.view.DoorView; | import ru.vyatsu.qr_access_admin.door.view.DoorView; | ||||||
| import ru.vyatsu.qr_access_admin.partner.view.PartnerView; | import ru.vyatsu.qr_access_admin.partner.view.PartnerView; | ||||||
| import ru.vyatsu.qr_access_admin.qr.view.QrView; |  | ||||||
| import ru.vyatsu.qr_access_admin.unit.view.UnitView; | import ru.vyatsu.qr_access_admin.unit.view.UnitView; | ||||||
| 
 | 
 | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
| @ -120,7 +119,6 @@ public class MainLayout extends AppLayout { | |||||||
|                 createTab("Домашняя страница", MainView.class), |                 createTab("Домашняя страница", MainView.class), | ||||||
|                 createTab("Устройства", UnitView.class), |                 createTab("Устройства", UnitView.class), | ||||||
|                 createTab("Двери", DoorView.class), |                 createTab("Двери", DoorView.class), | ||||||
|                 createTab("QR-коды", QrView.class), |  | ||||||
|                 createTab("Партнеры", PartnerView.class), |                 createTab("Партнеры", PartnerView.class), | ||||||
|                 createTab("Клиенты", ClientView.class) |                 createTab("Клиенты", ClientView.class) | ||||||
|         }; |         }; | ||||||
|  | |||||||
| @ -15,6 +15,7 @@ import com.vaadin.flow.data.binder.Binder; | |||||||
| import lombok.Getter; | import lombok.Getter; | ||||||
| import lombok.Setter; | import lombok.Setter; | ||||||
| import ru.vyatsu.qr_access_admin.door.entity.DoorEntity; | import ru.vyatsu.qr_access_admin.door.entity.DoorEntity; | ||||||
|  | import ru.vyatsu.qr_access_admin.door.entity.DoorRepository; | ||||||
| import ru.vyatsu.qr_access_admin.door.schedule.component.ScheduleEditor; | import ru.vyatsu.qr_access_admin.door.schedule.component.ScheduleEditor; | ||||||
| import ru.vyatsu.qr_access_admin.door.schedule.entity.ScheduleEntity; | import ru.vyatsu.qr_access_admin.door.schedule.entity.ScheduleEntity; | ||||||
| import ru.vyatsu.qr_access_admin.door.schedule.entity.ScheduleRepository; | import ru.vyatsu.qr_access_admin.door.schedule.entity.ScheduleRepository; | ||||||
| @ -22,14 +23,13 @@ import ru.vyatsu.qr_access_admin.unit.entity.UnitRepository; | |||||||
| import ru.vyatsu.qr_access_admin.unit.mapper.UnitEntityUnitComboBoxModelMapper; | import ru.vyatsu.qr_access_admin.unit.mapper.UnitEntityUnitComboBoxModelMapper; | ||||||
| import ru.vyatsu.qr_access_admin.unit.model.UnitComboBoxModel; | import ru.vyatsu.qr_access_admin.unit.model.UnitComboBoxModel; | ||||||
| 
 | 
 | ||||||
| import java.util.Collection; | import java.util.*; | ||||||
| import java.util.Map; |  | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| public class DoorEditor extends Composite<VerticalLayout> { | public class DoorEditor extends Composite<HorizontalLayout> { | ||||||
| 
 | 
 | ||||||
|     public interface SaveListener { |     public interface SaveListener { | ||||||
|         void onSave(DoorEntity door, Collection<ScheduleEntity> schedule); |         void onSave(DoorEditInfo editInfo); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public interface DeleteListener { |     public interface DeleteListener { | ||||||
| @ -44,6 +44,14 @@ public class DoorEditor extends Composite<VerticalLayout> { | |||||||
| 
 | 
 | ||||||
|     private final ScheduleEditor scheduleEditor; |     private final ScheduleEditor scheduleEditor; | ||||||
| 
 | 
 | ||||||
|  |     private final VerticalLayout additionalDoorsEditor = new VerticalLayout(); | ||||||
|  | 
 | ||||||
|  |     private final Map<String, DoorEntity> additionalDoors = new HashMap<>(); | ||||||
|  |     private final Map<String, DoorEntity> additionalDoorsWasLinked = new HashMap<>(); | ||||||
|  |     private final Collection<DoorEntity> createdAdditionalDoors = new ArrayList<>(); | ||||||
|  | 
 | ||||||
|  |     private final DoorRepository repository; | ||||||
|  | 
 | ||||||
|     @Getter |     @Getter | ||||||
|     @Setter |     @Setter | ||||||
|     private SaveListener saveListener; |     private SaveListener saveListener; | ||||||
| @ -61,13 +69,100 @@ public class DoorEditor extends Composite<VerticalLayout> { | |||||||
|         binder.setBean(door); |         binder.setBean(door); | ||||||
|         scheduleEditor.setCurrentDoorId(door.getId()); |         scheduleEditor.setCurrentDoorId(door.getId()); | ||||||
|         scheduleEditor.refreshScheduleView(); |         scheduleEditor.refreshScheduleView(); | ||||||
|  |         this.refreshAdditionalDoors(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public DoorEditor(UnitRepository unitRepository, ScheduleRepository scheduleRepository) { |     private void refreshAdditionalDoors() { | ||||||
|  |         additionalDoorsEditor.removeAll(); | ||||||
|  |         additionalDoors.clear(); | ||||||
|  |         createdAdditionalDoors.clear(); | ||||||
|  |         List<DoorEntity> existAdditionalDoors = repository.findAllByParentDoorIdsIsNotNull(); | ||||||
|  |         existAdditionalDoors | ||||||
|  |                 .stream() | ||||||
|  |                 .filter(ad -> currentDoor != null && new HashSet<>(Set.of(ad.getParentDoorIds().split(","))).contains(currentDoor.getId())) | ||||||
|  |                 .forEach(ad -> { | ||||||
|  |                     HorizontalLayout choose = new HorizontalLayout(); | ||||||
|  |                     ComboBox<DoorEntity> comboBox = new ComboBox<>("Выбрать из имеющихся"); | ||||||
|  |                     comboBox.setItems(existAdditionalDoors); | ||||||
|  |                     comboBox.setItemLabelGenerator(DoorEntity::getDescription); | ||||||
|  |                     comboBox.setValue(ad); | ||||||
|  |                     comboBox.setAllowCustomValue(true); | ||||||
|  |                     comboBox.addValueChangeListener(e -> { | ||||||
|  |                         DoorEntity chosenValue = e.getValue(); | ||||||
|  |                         DoorEntity oldValue = e.getOldValue(); | ||||||
|  |                         if (oldValue != null) { | ||||||
|  |                             additionalDoors.remove(oldValue.getId()); | ||||||
|  |                         } | ||||||
|  |                         if (chosenValue != null) { | ||||||
|  |                             additionalDoors.put(chosenValue.getId(), chosenValue); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                     comboBox.addCustomValueSetListener(e -> { | ||||||
|  |                         DoorEntity newDoor = new DoorEntity(); | ||||||
|  |                         newDoor.setCount(0); | ||||||
|  |                         newDoor.setDescription(e.getDetail()); | ||||||
|  |                         newDoor.setUnitId(currentDoor.getUnitId()); | ||||||
|  |                         createdAdditionalDoors.add(newDoor); | ||||||
|  |                         existAdditionalDoors.add(newDoor); | ||||||
|  |                         comboBox.setItems(existAdditionalDoors); | ||||||
|  |                         comboBox.setValue(newDoor); | ||||||
|  |                     }); | ||||||
|  |                     var deleteAdditionalDoor = new Button(VaadinIcon.TRASH.create()); | ||||||
|  |                     additionalDoors.put(ad.getId(), ad); | ||||||
|  |                     additionalDoorsWasLinked.put(ad.getId(), ad); | ||||||
|  |                     deleteAdditionalDoor.addClickListener(ev -> { | ||||||
|  |                         additionalDoorsEditor.remove(choose); | ||||||
|  |                         additionalDoors.remove(ad.getId()); | ||||||
|  |                     }); | ||||||
|  |                     choose.add(comboBox, deleteAdditionalDoor); | ||||||
|  |                     additionalDoorsEditor.add(choose); | ||||||
|  |                 }); | ||||||
|  |         var addAdditionalDoor = new Button("Привязать дверь", VaadinIcon.PLUS.create()); | ||||||
|  |         addAdditionalDoor.addClickListener(e -> { | ||||||
|  |             HorizontalLayout choose = new HorizontalLayout(); | ||||||
|  |             ComboBox<DoorEntity> comboBox = new ComboBox<>("Выбрать из имеющихся"); | ||||||
|  |             comboBox.setItems(existAdditionalDoors); | ||||||
|  |             comboBox.setItemLabelGenerator(DoorEntity::getDescription); | ||||||
|  |             comboBox.setAllowCustomValue(true); | ||||||
|  |             comboBox.addValueChangeListener(ce -> { | ||||||
|  |                 DoorEntity chosenValue = ce.getValue(); | ||||||
|  |                 DoorEntity oldValue = ce.getOldValue(); | ||||||
|  |                 if (oldValue != null) { | ||||||
|  |                     additionalDoors.remove(oldValue.getId()); | ||||||
|  |                 } | ||||||
|  |                 if (chosenValue != null) { | ||||||
|  |                     additionalDoors.put(chosenValue.getId(), chosenValue); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             comboBox.addCustomValueSetListener(ce -> { | ||||||
|  |                 DoorEntity newDoor = new DoorEntity(); | ||||||
|  |                 newDoor.setCount(0); | ||||||
|  |                 newDoor.setDescription(ce.getDetail()); | ||||||
|  |                 newDoor.setUnitId(currentDoor.getUnitId()); | ||||||
|  |                 createdAdditionalDoors.add(newDoor); | ||||||
|  |                 existAdditionalDoors.add(newDoor); | ||||||
|  |                 comboBox.setItems(existAdditionalDoors); | ||||||
|  |                 comboBox.setValue(newDoor); | ||||||
|  |             }); | ||||||
|  |             var deleteAdditionalDoor = new Button(VaadinIcon.TRASH.create()); | ||||||
|  |             deleteAdditionalDoor.addClickListener(ev -> { | ||||||
|  |                 additionalDoorsEditor.remove(choose); | ||||||
|  |                 if (comboBox.getValue() != null) { | ||||||
|  |                     additionalDoors.remove(comboBox.getValue().getId()); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             choose.add(comboBox, deleteAdditionalDoor); | ||||||
|  |             additionalDoorsEditor.addComponentAtIndex(additionalDoorsEditor.getComponentCount() - 1, choose); | ||||||
|  |         }); | ||||||
|  |         additionalDoorsEditor.add(addAdditionalDoor); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public DoorEditor(UnitRepository unitRepository, ScheduleRepository scheduleRepository, DoorRepository repository) { | ||||||
|  |         this.repository = repository; | ||||||
|         ComboBox<UnitComboBoxModel> unitField = new ComboBox<>("Устройство"); |         ComboBox<UnitComboBoxModel> unitField = new ComboBox<>("Устройство"); | ||||||
|         unitField.setRequired(true); |         unitField.setRequired(true); | ||||||
|         unitField.setPageSize(100); |         unitField.setPageSize(100); | ||||||
|         Map<String, UnitComboBoxModel> units = unitRepository.findAll() |         Map<String, UnitComboBoxModel> units = unitRepository.findAllByAdminEditableIsTrue() | ||||||
|                 .stream() |                 .stream() | ||||||
|                 .map(new UnitEntityUnitComboBoxModelMapper()::toModel) |                 .map(new UnitEntityUnitComboBoxModelMapper()::toModel) | ||||||
|                 .collect(Collectors.toMap(UnitComboBoxModel::clientId, unit -> unit)); |                 .collect(Collectors.toMap(UnitComboBoxModel::clientId, unit -> unit)); | ||||||
| @ -76,6 +171,7 @@ public class DoorEditor extends Composite<VerticalLayout> { | |||||||
|         IntegerField countField = new IntegerField("Количество мест"); |         IntegerField countField = new IntegerField("Количество мест"); | ||||||
|         TextField descriptionField = new TextField("Описание"); |         TextField descriptionField = new TextField("Описание"); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|         var save = new Button("Сохранить", VaadinIcon.CHECK.create()); |         var save = new Button("Сохранить", VaadinIcon.CHECK.create()); | ||||||
|         var cancel = new Button("Отмена"); |         var cancel = new Button("Отмена"); | ||||||
|         var delete = new Button("Удалить", VaadinIcon.TRASH.create()); |         var delete = new Button("Удалить", VaadinIcon.TRASH.create()); | ||||||
| @ -90,7 +186,7 @@ public class DoorEditor extends Composite<VerticalLayout> { | |||||||
| 
 | 
 | ||||||
|         scheduleEditor = new ScheduleEditor(scheduleRepository); |         scheduleEditor = new ScheduleEditor(scheduleRepository); | ||||||
| 
 | 
 | ||||||
|         save.addClickListener(e -> saveListener.onSave(currentDoor, scheduleEditor.getNewSchedule())); |         save.addClickListener(e -> saveListener.onSave(new DoorEditInfo(currentDoor, scheduleEditor.getNewSchedule(), additionalDoors, additionalDoorsWasLinked))); | ||||||
|         save.addClickShortcut(Key.ENTER); |         save.addClickShortcut(Key.ENTER); | ||||||
| 
 | 
 | ||||||
|         delete.addThemeVariants(ButtonVariant.LUMO_ERROR); |         delete.addThemeVariants(ButtonVariant.LUMO_ERROR); | ||||||
| @ -102,8 +198,19 @@ public class DoorEditor extends Composite<VerticalLayout> { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  |         this.refreshAdditionalDoors(); | ||||||
|  | 
 | ||||||
|         cancel.addClickListener(e -> cancelListener.onCancel()); |         cancel.addClickListener(e -> cancelListener.onCancel()); | ||||||
| 
 | 
 | ||||||
|         getContent().add(unitField, countField, descriptionField, scheduleEditor, new HorizontalLayout(save, cancel, delete)); |         VerticalLayout mainForm = new VerticalLayout(); | ||||||
|  |         mainForm.add(unitField, countField, descriptionField, scheduleEditor, new HorizontalLayout(save, cancel, delete)); | ||||||
|  | 
 | ||||||
|  |         getContent().add(mainForm, additionalDoorsEditor); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public record DoorEditInfo(DoorEntity mainDoor, Collection<ScheduleEntity> schedule, | ||||||
|  |                                Map<String, DoorEntity> additionalDoorsToSave, | ||||||
|  |                                Map<String, DoorEntity> additionalDoorsWasLinked) { | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -22,4 +22,7 @@ public class DoorEntity { | |||||||
| 
 | 
 | ||||||
|     @Column |     @Column | ||||||
|     private int count; |     private int count; | ||||||
|  | 
 | ||||||
|  |     @Column | ||||||
|  |     private String parentDoorIds; | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,6 +2,12 @@ package ru.vyatsu.qr_access_admin.door.entity; | |||||||
| 
 | 
 | ||||||
| import org.springframework.data.jpa.repository.JpaRepository; | import org.springframework.data.jpa.repository.JpaRepository; | ||||||
| 
 | 
 | ||||||
|  | import java.util.List; | ||||||
|  | 
 | ||||||
| public interface DoorRepository extends JpaRepository<DoorEntity, String> { | public interface DoorRepository extends JpaRepository<DoorEntity, String> { | ||||||
|  |     // TODO: запрашиваем все, чтобы не городить много SQL логики, так как id хранятся через запятую. Будем парсить и фильтровать в коде. | ||||||
|  |     List<DoorEntity> findAllByParentDoorIdsIsNotNull(); | ||||||
|  | 
 | ||||||
|  |     List<DoorEntity> findAllByParentDoorIdsIsNull(); | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,7 +19,10 @@ import ru.vyatsu.qr_access_admin.door.schedule.entity.ScheduleEntity; | |||||||
| import ru.vyatsu.qr_access_admin.door.schedule.entity.ScheduleRepository; | import ru.vyatsu.qr_access_admin.door.schedule.entity.ScheduleRepository; | ||||||
| import ru.vyatsu.qr_access_admin.unit.entity.UnitRepository; | import ru.vyatsu.qr_access_admin.unit.entity.UnitRepository; | ||||||
| 
 | 
 | ||||||
| import java.util.List; | import java.util.*; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  | 
 | ||||||
|  | import static org.apache.commons.lang3.StringUtils.isBlank; | ||||||
| 
 | 
 | ||||||
| @Route(value = "doors", layout = MainLayout.class) | @Route(value = "doors", layout = MainLayout.class) | ||||||
| @PageTitle("Двери") | @PageTitle("Двери") | ||||||
| @ -39,7 +42,8 @@ public class DoorView extends VerticalLayout { | |||||||
|         var addButton = new Button("Добавить дверь", VaadinIcon.PLUS.create()); |         var addButton = new Button("Добавить дверь", VaadinIcon.PLUS.create()); | ||||||
| 
 | 
 | ||||||
|         grid = new Grid<>(DoorEntity.class); |         grid = new Grid<>(DoorEntity.class); | ||||||
|         editor = new DoorEditor(unitRepository, scheduleRepository); |         grid.setColumns("description", "unitId", "count"); | ||||||
|  |         editor = new DoorEditor(unitRepository, scheduleRepository, repository); | ||||||
| 
 | 
 | ||||||
|         var actionsLayout = new HorizontalLayout(addButton); |         var actionsLayout = new HorizontalLayout(addButton); | ||||||
|         add(actionsLayout, grid, editor); |         add(actionsLayout, grid, editor); | ||||||
| @ -56,7 +60,7 @@ public class DoorView extends VerticalLayout { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void refreshDoorsGrid() { |     private void refreshDoorsGrid() { | ||||||
|         List<DoorEntity> entities = repository.findAll(); |         List<DoorEntity> entities = repository.findAllByParentDoorIdsIsNull(); | ||||||
| 
 | 
 | ||||||
|         grid.setItems(entities); |         grid.setItems(entities); | ||||||
|     } |     } | ||||||
| @ -73,12 +77,34 @@ public class DoorView extends VerticalLayout { | |||||||
|     private void configureEditor() { |     private void configureEditor() { | ||||||
|         editor.setVisible(false); |         editor.setVisible(false); | ||||||
| 
 | 
 | ||||||
|         editor.setSaveListener((door, schedule) -> { |         editor.setSaveListener(doorEditInfo -> { | ||||||
|             DoorEntity savedDoor = repository.save(door); |             DoorEntity savedDoor = repository.save(doorEditInfo.mainDoor()); | ||||||
|             for (ScheduleEntity se : schedule) { |             Map<String, DoorEntity> doorsToSave = new HashMap<>(); | ||||||
|  |             doorEditInfo.additionalDoorsToSave().values().forEach(ad -> { | ||||||
|  |                 if (isBlank(ad.getParentDoorIds())) { | ||||||
|  |                     ad.setParentDoorIds(savedDoor.getId()); | ||||||
|  |                 } else { | ||||||
|  |                     Set<String> parentDoorIds = Arrays.stream(ad.getParentDoorIds().split(",")) | ||||||
|  |                             .collect(Collectors.toSet()); | ||||||
|  |                     parentDoorIds.add(savedDoor.getId()); | ||||||
|  |                     ad.setParentDoorIds(String.join(",", parentDoorIds)); | ||||||
|  |                 } | ||||||
|  |                 doorsToSave.put(ad.getId(), ad); | ||||||
|  |             }); | ||||||
|  |             doorEditInfo.additionalDoorsWasLinked().forEach((id, door) -> { | ||||||
|  |                 if (!doorEditInfo.additionalDoorsToSave().containsKey(id)) { | ||||||
|  |                     Set<String> parentIds = Arrays.stream(door.getParentDoorIds().split(",")) | ||||||
|  |                             .collect(Collectors.toSet()); | ||||||
|  |                     parentIds.remove(savedDoor.getId()); | ||||||
|  |                     door.setParentDoorIds(String.join(",", parentIds)); | ||||||
|  |                     doorsToSave.put(id, door); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             repository.saveAll(doorsToSave.values()); | ||||||
|  |             for (ScheduleEntity se : doorEditInfo.schedule()) { | ||||||
|                 se.setDoorId(savedDoor.getId()); |                 se.setDoorId(savedDoor.getId()); | ||||||
|             } |             } | ||||||
|             scheduleRepository.saveAll(schedule); |             scheduleRepository.saveAll(doorEditInfo.schedule()); | ||||||
|             refreshDoorsGrid(); |             refreshDoorsGrid(); | ||||||
|             editDoor(null); |             editDoor(null); | ||||||
|         }); |         }); | ||||||
|  | |||||||
| @ -1,89 +0,0 @@ | |||||||
| package ru.vyatsu.qr_access_admin.qr.component; |  | ||||||
| 
 |  | ||||||
| import com.vaadin.flow.component.Composite; |  | ||||||
| import com.vaadin.flow.component.Key; |  | ||||||
| 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.datetimepicker.DateTimePicker; |  | ||||||
| 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.data.binder.BeanValidationBinder; |  | ||||||
| import com.vaadin.flow.data.binder.Binder; |  | ||||||
| import lombok.Getter; |  | ||||||
| import lombok.Setter; |  | ||||||
| import ru.vyatsu.qr_access_admin.door.entity.DoorEntity; |  | ||||||
| import ru.vyatsu.qr_access_admin.door.entity.DoorRepository; |  | ||||||
| import ru.vyatsu.qr_access_admin.qr.entity.QrEntity; |  | ||||||
| 
 |  | ||||||
| import java.util.Map; |  | ||||||
| import java.util.stream.Collectors; |  | ||||||
| 
 |  | ||||||
| public class QrEditor extends Composite<VerticalLayout> { |  | ||||||
| 
 |  | ||||||
|     public interface SaveListener { |  | ||||||
|         void onSave(QrEntity qr); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public interface DeleteListener { |  | ||||||
|         void onDelete(QrEntity qr); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public interface CancelListener { |  | ||||||
|         void onCancel(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private volatile QrEntity currentQr; |  | ||||||
| 
 |  | ||||||
|     @Getter |  | ||||||
|     @Setter |  | ||||||
|     private SaveListener saveListener; |  | ||||||
|     @Getter |  | ||||||
|     @Setter |  | ||||||
|     private DeleteListener deleteListener; |  | ||||||
|     @Getter |  | ||||||
|     @Setter |  | ||||||
|     private CancelListener cancelListener; |  | ||||||
| 
 |  | ||||||
|     private final Binder<QrEntity> binder = new BeanValidationBinder<>(QrEntity.class); |  | ||||||
| 
 |  | ||||||
|     public void setCurrentQr(QrEntity qr) { |  | ||||||
|         this.currentQr = qr; |  | ||||||
|         binder.setBean(qr); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public QrEditor(DoorRepository doorRepository) { |  | ||||||
|         ComboBox<DoorEntity> doorIdField = new ComboBox<>("Идентификатор двери"); |  | ||||||
|         doorIdField.setRequired(true); |  | ||||||
|         doorIdField.setItemLabelGenerator(DoorEntity::getId); |  | ||||||
|         Map<String, DoorEntity> doors = doorRepository.findAll() |  | ||||||
|                 .stream() |  | ||||||
|                 .collect(Collectors.toMap(DoorEntity::getId, e -> e)); |  | ||||||
| 
 |  | ||||||
|         doorIdField.setItems(doors.values()); |  | ||||||
|         var startDateTime = new DateTimePicker("Начало действия"); |  | ||||||
|         var endDateTime = new DateTimePicker("Актуален до"); |  | ||||||
| 
 |  | ||||||
|         var save = new Button("Сохранить", VaadinIcon.CHECK.create()); |  | ||||||
|         var cancel = new Button("Отмена"); |  | ||||||
|         var delete = new Button("Удалить", VaadinIcon.TRASH.create()); |  | ||||||
| 
 |  | ||||||
|         binder.forField(doorIdField) |  | ||||||
|                 .withConverter(DoorEntity::getId, doors::get, "Invalid value") |  | ||||||
|                 .bind("doorId"); |  | ||||||
|         binder.forField(startDateTime).bind("startDateTime"); |  | ||||||
|         binder.forField(endDateTime).bind("endDateTime"); |  | ||||||
| 
 |  | ||||||
|         save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); |  | ||||||
|         save.addClickListener(e -> saveListener.onSave(currentQr)); |  | ||||||
|         save.addClickShortcut(Key.ENTER); |  | ||||||
| 
 |  | ||||||
|         delete.addThemeVariants(ButtonVariant.LUMO_ERROR); |  | ||||||
|         delete.addClickListener(e -> deleteListener.onDelete(currentQr)); |  | ||||||
| 
 |  | ||||||
|         cancel.addClickListener(e -> cancelListener.onCancel()); |  | ||||||
| 
 |  | ||||||
|         getContent().add(doorIdField, startDateTime, endDateTime, new HorizontalLayout(save, cancel, delete)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,28 +0,0 @@ | |||||||
| package ru.vyatsu.qr_access_admin.qr.entity; |  | ||||||
| 
 |  | ||||||
| import jakarta.persistence.*; |  | ||||||
| import lombok.Getter; |  | ||||||
| import lombok.Setter; |  | ||||||
| 
 |  | ||||||
| import java.time.LocalDateTime; |  | ||||||
| 
 |  | ||||||
| @Entity |  | ||||||
| @Table(name = "qrs") |  | ||||||
| @Getter |  | ||||||
| @Setter |  | ||||||
| public class QrEntity { |  | ||||||
| 
 |  | ||||||
|     @Id |  | ||||||
|     @Column |  | ||||||
|     @GeneratedValue(strategy = GenerationType.UUID) |  | ||||||
|     private String keyCode; |  | ||||||
| 
 |  | ||||||
|     @Column |  | ||||||
|     private String doorId; |  | ||||||
| 
 |  | ||||||
|     @Column |  | ||||||
|     private LocalDateTime startDateTime; |  | ||||||
| 
 |  | ||||||
|     @Column |  | ||||||
|     private LocalDateTime endDateTime; |  | ||||||
| } |  | ||||||
| @ -1,6 +0,0 @@ | |||||||
| package ru.vyatsu.qr_access_admin.qr.entity; |  | ||||||
| 
 |  | ||||||
| import org.springframework.data.jpa.repository.JpaRepository; |  | ||||||
| 
 |  | ||||||
| public interface QrRepository extends JpaRepository<QrEntity, String> { |  | ||||||
| } |  | ||||||
| @ -1,78 +0,0 @@ | |||||||
| package ru.vyatsu.qr_access_admin.qr.view; |  | ||||||
| 
 |  | ||||||
| import com.vaadin.flow.component.button.Button; |  | ||||||
| import com.vaadin.flow.component.grid.Grid; |  | ||||||
| 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.router.PageTitle; |  | ||||||
| import com.vaadin.flow.router.Route; |  | ||||||
| import jakarta.annotation.security.PermitAll; |  | ||||||
| import ru.vyatsu.qr_access_admin.common.MainLayout; |  | ||||||
| import ru.vyatsu.qr_access_admin.door.entity.DoorRepository; |  | ||||||
| import ru.vyatsu.qr_access_admin.qr.component.QrEditor; |  | ||||||
| import ru.vyatsu.qr_access_admin.qr.entity.QrEntity; |  | ||||||
| import ru.vyatsu.qr_access_admin.qr.entity.QrRepository; |  | ||||||
| 
 |  | ||||||
| import java.util.List; |  | ||||||
| 
 |  | ||||||
| @Route(value = "qrs", layout = MainLayout.class) |  | ||||||
| @PageTitle("Коды") |  | ||||||
| @PermitAll |  | ||||||
| public class QrView extends VerticalLayout { |  | ||||||
|     private final QrRepository repository; |  | ||||||
|     private final Grid<QrEntity> grid; |  | ||||||
|     private final QrEditor editor; |  | ||||||
| 
 |  | ||||||
|     public QrView(QrRepository repository, DoorRepository doorRepository) { |  | ||||||
|         this.repository = repository; |  | ||||||
| 
 |  | ||||||
|         var addButton = new Button("Добавить код", VaadinIcon.PLUS.create()); |  | ||||||
|         grid = new Grid<>(QrEntity.class); |  | ||||||
|         editor = new QrEditor(doorRepository); |  | ||||||
| 
 |  | ||||||
|         var actionsLayout = new HorizontalLayout(addButton); |  | ||||||
|         add(actionsLayout, grid, editor); |  | ||||||
| 
 |  | ||||||
|         this.configureEditor(); |  | ||||||
| 
 |  | ||||||
|         addButton.addClickListener(e -> editQr(new QrEntity())); |  | ||||||
| 
 |  | ||||||
|         grid.setHeight("200px"); |  | ||||||
|         grid.asSingleSelect().addValueChangeListener(e -> editQr(e.getValue())); |  | ||||||
| 
 |  | ||||||
|         refreshQrGrid(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void refreshQrGrid() { |  | ||||||
|         List<QrEntity> entities = repository.findAll(); |  | ||||||
|         grid.setItems(entities); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void editQr(QrEntity qr) { |  | ||||||
|         if (qr == null) { |  | ||||||
|             editor.setVisible(false); |  | ||||||
|         } else { |  | ||||||
|             editor.setVisible(true); |  | ||||||
|             editor.setCurrentQr(qr); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void configureEditor() { |  | ||||||
|         editor.setVisible(false); |  | ||||||
| 
 |  | ||||||
|         editor.setSaveListener(qr -> { |  | ||||||
|             repository.save(qr); |  | ||||||
|             refreshQrGrid(); |  | ||||||
|             editQr(null); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         editor.setDeleteListener(qr -> { |  | ||||||
|             repository.deleteById(qr.getKeyCode()); |  | ||||||
|             refreshQrGrid(); |  | ||||||
|             editQr(null); |  | ||||||
|         }); |  | ||||||
| 
 |  | ||||||
|         editor.setCancelListener(() -> editQr(null)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,10 +1,10 @@ | |||||||
| package ru.vyatsu.qr_access_admin.unit.component; | package ru.vyatsu.qr_access_admin.unit.component; | ||||||
| 
 | 
 | ||||||
| import ru.vyatsu.qr_access_admin.unit.model.UnitModel; |  | ||||||
| import com.vaadin.flow.component.Composite; | import com.vaadin.flow.component.Composite; | ||||||
| import com.vaadin.flow.component.Key; | import com.vaadin.flow.component.Key; | ||||||
| import com.vaadin.flow.component.button.Button; | import com.vaadin.flow.component.button.Button; | ||||||
| import com.vaadin.flow.component.button.ButtonVariant; | import com.vaadin.flow.component.button.ButtonVariant; | ||||||
|  | import com.vaadin.flow.component.combobox.ComboBox; | ||||||
| import com.vaadin.flow.component.datetimepicker.DateTimePicker; | import com.vaadin.flow.component.datetimepicker.DateTimePicker; | ||||||
| import com.vaadin.flow.component.icon.VaadinIcon; | import com.vaadin.flow.component.icon.VaadinIcon; | ||||||
| import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | ||||||
| @ -14,6 +14,12 @@ import com.vaadin.flow.data.binder.BeanValidationBinder; | |||||||
| import com.vaadin.flow.data.binder.Binder; | import com.vaadin.flow.data.binder.Binder; | ||||||
| import lombok.Getter; | import lombok.Getter; | ||||||
| import lombok.Setter; | import lombok.Setter; | ||||||
|  | import ru.vyatsu.qr_access_admin.partner.entity.PartnerEntity; | ||||||
|  | import ru.vyatsu.qr_access_admin.partner.entity.PartnerRepository; | ||||||
|  | import ru.vyatsu.qr_access_admin.unit.model.UnitModel; | ||||||
|  | 
 | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| public class UnitEditor extends Composite<VerticalLayout> { | public class UnitEditor extends Composite<VerticalLayout> { | ||||||
|     public interface SaveListener { |     public interface SaveListener { | ||||||
| @ -47,7 +53,7 @@ public class UnitEditor extends Composite<VerticalLayout> { | |||||||
|         binder.setBean(unit); |         binder.setBean(unit); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public UnitEditor() { |     public UnitEditor(PartnerRepository partnerRepository) { | ||||||
|         var clientId = new TextField("Идентификатор устройства"); |         var clientId = new TextField("Идентификатор устройства"); | ||||||
|         clientId.setRequired(true); |         clientId.setRequired(true); | ||||||
|         var clientSecret = new TextField("Секрет устройства"); |         var clientSecret = new TextField("Секрет устройства"); | ||||||
| @ -56,6 +62,13 @@ public class UnitEditor extends Composite<VerticalLayout> { | |||||||
|         clientSecretExpiresAt.setRequiredIndicatorVisible(true); |         clientSecretExpiresAt.setRequiredIndicatorVisible(true); | ||||||
|         var clientName = new TextField("Человекочитаемое название устройства"); |         var clientName = new TextField("Человекочитаемое название устройства"); | ||||||
|         clientName.setRequired(true); |         clientName.setRequired(true); | ||||||
|  |         ComboBox<PartnerEntity> partnerField = new ComboBox<>("Партнер"); | ||||||
|  |         partnerField.setPageSize(100); | ||||||
|  |         Map<String, PartnerEntity> partners = partnerRepository.findAll() | ||||||
|  |                 .stream() | ||||||
|  |                 .collect(Collectors.toMap(PartnerEntity::getId, p -> p)); | ||||||
|  |         partnerField.setItemLabelGenerator(PartnerEntity::getName); | ||||||
|  |         partnerField.setItems(partners.values()); | ||||||
| 
 | 
 | ||||||
|         var save = new Button("Сохранить", VaadinIcon.CHECK.create()); |         var save = new Button("Сохранить", VaadinIcon.CHECK.create()); | ||||||
|         var cancel = new Button("Отмена"); |         var cancel = new Button("Отмена"); | ||||||
| @ -65,6 +78,9 @@ public class UnitEditor extends Composite<VerticalLayout> { | |||||||
|         binder.forField(clientSecret).bind("clientSecret"); |         binder.forField(clientSecret).bind("clientSecret"); | ||||||
|         binder.forField(clientSecretExpiresAt).bind("clientSecretExpiresAt"); |         binder.forField(clientSecretExpiresAt).bind("clientSecretExpiresAt"); | ||||||
|         binder.forField(clientName).bind("clientName"); |         binder.forField(clientName).bind("clientName"); | ||||||
|  |         binder.forField(partnerField) | ||||||
|  |                 .withConverter(PartnerEntity::getId, partners::get, "Invalid value") | ||||||
|  |                 .bind("partnerId"); | ||||||
| 
 | 
 | ||||||
|         save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); |         save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); | ||||||
|         save.addClickListener(e -> saveListener.onSave(currentUnit)); |         save.addClickListener(e -> saveListener.onSave(currentUnit)); | ||||||
| @ -75,6 +91,6 @@ public class UnitEditor extends Composite<VerticalLayout> { | |||||||
| 
 | 
 | ||||||
|         cancel.addClickListener(e -> cancelListener.onCancel()); |         cancel.addClickListener(e -> cancelListener.onCancel()); | ||||||
| 
 | 
 | ||||||
|         getContent().add(clientId, clientSecret, clientSecretExpiresAt, clientName, new HorizontalLayout(save, cancel, delete)); |         getContent().add(clientId, clientSecret, clientSecretExpiresAt, clientName, partnerField, new HorizontalLayout(save, cancel, delete)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -49,4 +49,6 @@ public class UnitEntity { | |||||||
|     private String tokenSettings; |     private String tokenSettings; | ||||||
|     @Column |     @Column | ||||||
|     private Boolean adminEditable; |     private Boolean adminEditable; | ||||||
|  |     @Column | ||||||
|  |     private String partnerId; | ||||||
| } | } | ||||||
|  | |||||||
| @ -33,6 +33,7 @@ public class UnitEntityUnitModelMapper { | |||||||
|         entity.setRedirectUris(""); |         entity.setRedirectUris(""); | ||||||
|         entity.setPostLogoutRedirectUris(""); |         entity.setPostLogoutRedirectUris(""); | ||||||
|         entity.setAdminEditable(true); |         entity.setAdminEditable(true); | ||||||
|  |         entity.setPartnerId(model.getPartnerId()); | ||||||
|         return entity; |         return entity; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -44,6 +45,7 @@ public class UnitEntityUnitModelMapper { | |||||||
|         unitModel.setClientName(entity.getClientName()); |         unitModel.setClientName(entity.getClientName()); | ||||||
|         unitModel.setClientSecretExpiresAt(entity.getClientSecretExpiresAt()); |         unitModel.setClientSecretExpiresAt(entity.getClientSecretExpiresAt()); | ||||||
|         unitModel.setAdminEditable(entity.getAdminEditable()); |         unitModel.setAdminEditable(entity.getAdminEditable()); | ||||||
|  |         unitModel.setPartnerId(entity.getPartnerId()); | ||||||
|         return unitModel; |         return unitModel; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -28,4 +28,6 @@ public class UnitModel { | |||||||
|     private String clientName; |     private String clientName; | ||||||
| 
 | 
 | ||||||
|     private boolean adminEditable; |     private boolean adminEditable; | ||||||
|  | 
 | ||||||
|  |     private String partnerId; | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ import com.vaadin.flow.router.PageTitle; | |||||||
| import com.vaadin.flow.router.Route; | import com.vaadin.flow.router.Route; | ||||||
| import jakarta.annotation.security.PermitAll; | import jakarta.annotation.security.PermitAll; | ||||||
| import ru.vyatsu.qr_access_admin.common.MainLayout; | import ru.vyatsu.qr_access_admin.common.MainLayout; | ||||||
|  | import ru.vyatsu.qr_access_admin.partner.entity.PartnerRepository; | ||||||
| import ru.vyatsu.qr_access_admin.unit.component.UnitEditor; | import ru.vyatsu.qr_access_admin.unit.component.UnitEditor; | ||||||
| import ru.vyatsu.qr_access_admin.unit.entity.UnitEntity; | import ru.vyatsu.qr_access_admin.unit.entity.UnitEntity; | ||||||
| import ru.vyatsu.qr_access_admin.unit.entity.UnitRepository; | import ru.vyatsu.qr_access_admin.unit.entity.UnitRepository; | ||||||
| @ -27,14 +28,14 @@ public class UnitView extends VerticalLayout { | |||||||
|     private final UnitEditor editor; |     private final UnitEditor editor; | ||||||
|     private final UnitEntityUnitModelMapper entityModelMapper; |     private final UnitEntityUnitModelMapper entityModelMapper; | ||||||
| 
 | 
 | ||||||
|     public UnitView(UnitRepository unitRepository, UnitEntityUnitModelMapper entityModelMapper) { |     public UnitView(UnitRepository unitRepository, UnitEntityUnitModelMapper entityModelMapper, PartnerRepository partnerRepository) { | ||||||
|         this.unitRepository = unitRepository; |         this.unitRepository = unitRepository; | ||||||
|         this.entityModelMapper = entityModelMapper; |         this.entityModelMapper = entityModelMapper; | ||||||
| 
 | 
 | ||||||
|         var addButton = new Button("Добавить устройство", VaadinIcon.PLUS.create()); |         var addButton = new Button("Добавить устройство", VaadinIcon.PLUS.create()); | ||||||
|         grid = new Grid<>(UnitModel.class); |         grid = new Grid<>(UnitModel.class); | ||||||
|         grid.setColumns("clientId", "clientName", "clientSecretExpiresAt"); |         grid.setColumns("clientId", "clientName", "clientSecretExpiresAt"); | ||||||
|         editor = new UnitEditor(); |         editor = new UnitEditor(partnerRepository); | ||||||
| 
 | 
 | ||||||
|         var actionsLayout = new HorizontalLayout(addButton); |         var actionsLayout = new HorizontalLayout(addButton); | ||||||
|         add(actionsLayout, grid, editor); |         add(actionsLayout, grid, editor); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user