Erweiterungen
This commit is contained in:
@@ -35,6 +35,9 @@ public abstract class BaseTask {
|
|||||||
@Field("task_order")
|
@Field("task_order")
|
||||||
private Integer taskOrder = 0;
|
private Integer taskOrder = 0;
|
||||||
|
|
||||||
|
@Field("description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
@Field("completed")
|
@Field("completed")
|
||||||
private boolean completed = false;
|
private boolean completed = false;
|
||||||
|
|
||||||
|
|||||||
@@ -391,9 +391,10 @@ public class AddJobView extends Main {
|
|||||||
.setWeekdaysShort(java.util.Arrays.asList(
|
.setWeekdaysShort(java.util.Arrays.asList(
|
||||||
"So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")));
|
"So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")));
|
||||||
|
|
||||||
// Submit button
|
// Submit button - initially disabled until all required fields are valid
|
||||||
submitButton = new Button("Auftrag anlegen", event -> submit());
|
submitButton = new Button("Auftrag anlegen", event -> submit());
|
||||||
submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
submitButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
|
||||||
|
submitButton.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Testdaten entfernt
|
// Testdaten entfernt
|
||||||
@@ -951,6 +952,16 @@ public class AddJobView extends Main {
|
|||||||
private void triggerValidation() {
|
private void triggerValidation() {
|
||||||
// Create a temporary job object to trigger validation
|
// Create a temporary job object to trigger validation
|
||||||
binder.validate();
|
binder.validate();
|
||||||
|
|
||||||
|
// Update submit button state based on all validation checks
|
||||||
|
if (submitButton != null) {
|
||||||
|
boolean hasErrors = hasAddressValidationErrors()
|
||||||
|
|| hasAppointmentValidationErrors()
|
||||||
|
|| hasCargoValidationErrors()
|
||||||
|
|| hasPriceValidationErrors()
|
||||||
|
|| hasTasksValidationErrors();
|
||||||
|
submitButton.setEnabled(!hasErrors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFieldStyling(TextField field) {
|
private void updateFieldStyling(TextField field) {
|
||||||
@@ -992,6 +1003,9 @@ public class AddJobView extends Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateTabLabel(com.vaadin.flow.component.tabs.Tab tab, String baseLabel, boolean hasErrors) {
|
private void updateTabLabel(com.vaadin.flow.component.tabs.Tab tab, String baseLabel, boolean hasErrors) {
|
||||||
|
if (tab == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (hasErrors) {
|
if (hasErrors) {
|
||||||
tab.setLabel(baseLabel + " ⚠️");
|
tab.setLabel(baseLabel + " ⚠️");
|
||||||
} else {
|
} else {
|
||||||
@@ -1043,7 +1057,28 @@ public class AddJobView extends Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasTasksValidationErrors() {
|
private boolean hasTasksValidationErrors() {
|
||||||
// Tasks are optional, so no validation errors for tasks
|
for (BaseTask task : tasksState) {
|
||||||
|
// Check if any ConfirmationTask has an empty description (required field)
|
||||||
|
if (task instanceof ConfirmationTask) {
|
||||||
|
String description = task.getDescription();
|
||||||
|
if (description == null || description.trim().isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if any TodoListTask has at least one non-empty todo item
|
||||||
|
if (task instanceof TodoListTask todoListTask) {
|
||||||
|
List<String> todoItems = todoListTask.getTodoItems();
|
||||||
|
if (todoItems == null || todoItems.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Check if at least one todo item is non-empty
|
||||||
|
boolean hasValidTodoItem = todoItems.stream()
|
||||||
|
.anyMatch(item -> item != null && !item.trim().isEmpty());
|
||||||
|
if (!hasValidTodoItem) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1275,7 +1310,8 @@ public class AddJobView extends Main {
|
|||||||
cargoItemsState.remove(idx);
|
cargoItemsState.remove(idx);
|
||||||
}
|
}
|
||||||
cargoList.remove(row);
|
cargoList.remove(row);
|
||||||
updateTabLabels(); // Update tab validation when cargo item is removed
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
});
|
});
|
||||||
|
|
||||||
row.add(desc, qty, weight, len, wid, hei, remove);
|
row.add(desc, qty, weight, len, wid, hei, remove);
|
||||||
@@ -1317,32 +1353,38 @@ public class AddJobView extends Main {
|
|||||||
desc.addValueChangeListener(ev -> {
|
desc.addValueChangeListener(ev -> {
|
||||||
item.setDescription(ev.getValue());
|
item.setDescription(ev.getValue());
|
||||||
validateField.accept(desc);
|
validateField.accept(desc);
|
||||||
updateTabLabels(); // Update tab validation when cargo description changes
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
});
|
});
|
||||||
qty.addValueChangeListener(ev -> {
|
qty.addValueChangeListener(ev -> {
|
||||||
item.setQuantity(ev.getValue());
|
item.setQuantity(ev.getValue());
|
||||||
validateField.accept(qty);
|
validateField.accept(qty);
|
||||||
updateTabLabels(); // Update tab validation when cargo quantity changes
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
});
|
});
|
||||||
weight.addValueChangeListener(ev -> {
|
weight.addValueChangeListener(ev -> {
|
||||||
item.setWeightKg(ev.getValue());
|
item.setWeightKg(ev.getValue());
|
||||||
validateField.accept(weight);
|
validateField.accept(weight);
|
||||||
updateTabLabels(); // Update tab validation when cargo weight changes
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
});
|
});
|
||||||
len.addValueChangeListener(ev -> {
|
len.addValueChangeListener(ev -> {
|
||||||
item.setLengthMm(ev.getValue());
|
item.setLengthMm(ev.getValue());
|
||||||
validateField.accept(len);
|
validateField.accept(len);
|
||||||
updateTabLabels(); // Update tab validation when cargo length changes
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
});
|
});
|
||||||
wid.addValueChangeListener(ev -> {
|
wid.addValueChangeListener(ev -> {
|
||||||
item.setWidthMm(ev.getValue());
|
item.setWidthMm(ev.getValue());
|
||||||
validateField.accept(wid);
|
validateField.accept(wid);
|
||||||
updateTabLabels(); // Update tab validation when cargo width changes
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
});
|
});
|
||||||
hei.addValueChangeListener(ev -> {
|
hei.addValueChangeListener(ev -> {
|
||||||
item.setHeightMm(ev.getValue());
|
item.setHeightMm(ev.getValue());
|
||||||
validateField.accept(hei);
|
validateField.accept(hei);
|
||||||
updateTabLabels(); // Update tab validation when cargo height changes
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initial validation
|
// Initial validation
|
||||||
@@ -1679,12 +1721,16 @@ public class AddJobView extends Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTaskConfiguration(configContainer, newTask);
|
updateTaskConfiguration(configContainer, newTask);
|
||||||
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set initial configuration
|
// Set initial configuration
|
||||||
taskTypeCombo.setValue(TaskType.CONFIRMATION);
|
taskTypeCombo.setValue(TaskType.CONFIRMATION);
|
||||||
updateTaskConfiguration(configContainer, currentTask[0]);
|
updateTaskConfiguration(configContainer, currentTask[0]);
|
||||||
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
|
|
||||||
tasksList.add(taskContainer);
|
tasksList.add(taskContainer);
|
||||||
}
|
}
|
||||||
@@ -1717,6 +1763,33 @@ public class AddJobView extends Main {
|
|||||||
|
|
||||||
switch (taskType) {
|
switch (taskType) {
|
||||||
case CONFIRMATION:
|
case CONFIRMATION:
|
||||||
|
// Description field (required)
|
||||||
|
TextField descriptionField = new TextField("Beschreibung");
|
||||||
|
descriptionField.setPlaceholder("Beschreibung der Aufgabe...");
|
||||||
|
descriptionField.setWidthFull();
|
||||||
|
descriptionField.setRequiredIndicatorVisible(true);
|
||||||
|
descriptionField.setValue(task.getDescription() != null ? task.getDescription() : "");
|
||||||
|
descriptionField.addValueChangeListener(ev -> {
|
||||||
|
task.setDescription(ev.getValue());
|
||||||
|
// Update field styling based on value
|
||||||
|
boolean isEmpty = ev.getValue() == null || ev.getValue().trim().isEmpty();
|
||||||
|
if (isEmpty) {
|
||||||
|
descriptionField.getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)");
|
||||||
|
descriptionField.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)");
|
||||||
|
} else {
|
||||||
|
descriptionField.getStyle().remove("--vaadin-input-field-background");
|
||||||
|
descriptionField.getStyle().remove("--vaadin-input-field-border-color");
|
||||||
|
}
|
||||||
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
|
});
|
||||||
|
// Initial styling for empty field
|
||||||
|
if (task.getDescription() == null || task.getDescription().trim().isEmpty()) {
|
||||||
|
descriptionField.getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)");
|
||||||
|
descriptionField.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button text field
|
||||||
TextField buttonTextField = new TextField("Button-Text");
|
TextField buttonTextField = new TextField("Button-Text");
|
||||||
buttonTextField.setPlaceholder("z.B. 'Bestätigen', 'Abgeschlossen'");
|
buttonTextField.setPlaceholder("z.B. 'Bestätigen', 'Abgeschlossen'");
|
||||||
buttonTextField.setWidthFull();
|
buttonTextField.setWidthFull();
|
||||||
@@ -1725,10 +1798,12 @@ public class AddJobView extends Main {
|
|||||||
buttonTextField.addValueChangeListener(ev -> {
|
buttonTextField.addValueChangeListener(ev -> {
|
||||||
// Find the current ConfirmationTask in tasksState and update it
|
// Find the current ConfirmationTask in tasksState and update it
|
||||||
for (BaseTask stateTask : tasksState) {
|
for (BaseTask stateTask : tasksState) {
|
||||||
|
if (stateTask instanceof ConfirmationTask) {
|
||||||
((ConfirmationTask) stateTask).setButtonText(ev.getValue());
|
((ConfirmationTask) stateTask).setButtonText(ev.getValue());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
configContainer.add(buttonTextField);
|
configContainer.add(descriptionField, buttonTextField);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SIGNATURE:
|
case SIGNATURE:
|
||||||
@@ -1753,6 +1828,18 @@ public class AddJobView extends Main {
|
|||||||
todoList.setPadding(false);
|
todoList.setPadding(false);
|
||||||
todoList.setSpacing(true);
|
todoList.setSpacing(true);
|
||||||
|
|
||||||
|
// Helper to update todo field styling based on value
|
||||||
|
java.util.function.Consumer<TextField> updateTodoFieldStyling = (field) -> {
|
||||||
|
boolean isEmpty = field.getValue() == null || field.getValue().trim().isEmpty();
|
||||||
|
if (isEmpty) {
|
||||||
|
field.getStyle().set("--vaadin-input-field-background", "rgba(255, 0, 0, 0.1)");
|
||||||
|
field.getStyle().set("--vaadin-input-field-border-color", "rgba(255, 0, 0, 0.3)");
|
||||||
|
} else {
|
||||||
|
field.getStyle().remove("--vaadin-input-field-background");
|
||||||
|
field.getStyle().remove("--vaadin-input-field-border-color");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
java.util.function.Consumer<Void> addTodoItem = (v) -> {
|
java.util.function.Consumer<Void> addTodoItem = (v) -> {
|
||||||
HorizontalLayout todoRow = new HorizontalLayout();
|
HorizontalLayout todoRow = new HorizontalLayout();
|
||||||
todoRow.setWidthFull();
|
todoRow.setWidthFull();
|
||||||
@@ -1761,6 +1848,9 @@ public class AddJobView extends Main {
|
|||||||
TextField todoField = new TextField();
|
TextField todoField = new TextField();
|
||||||
todoField.setPlaceholder("To-Do Punkt");
|
todoField.setPlaceholder("To-Do Punkt");
|
||||||
todoField.setWidth("100%");
|
todoField.setWidth("100%");
|
||||||
|
todoField.setRequiredIndicatorVisible(true);
|
||||||
|
// Initial red styling for empty field
|
||||||
|
updateTodoFieldStyling.accept(todoField);
|
||||||
|
|
||||||
Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||||
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||||
@@ -1773,7 +1863,10 @@ public class AddJobView extends Main {
|
|||||||
todoRow.setFlexGrow(1, todoField);
|
todoRow.setFlexGrow(1, todoField);
|
||||||
todoList.add(todoRow);
|
todoList.add(todoRow);
|
||||||
|
|
||||||
todoField.addValueChangeListener(ev -> updateTodoItems(todoList, task));
|
todoField.addValueChangeListener(ev -> {
|
||||||
|
updateTodoFieldStyling.accept(todoField);
|
||||||
|
updateTodoItems(todoList, task);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Button addTodoBtn = new Button("To-Do Punkt hinzufügen", new Icon(VaadinIcon.PLUS));
|
Button addTodoBtn = new Button("To-Do Punkt hinzufügen", new Icon(VaadinIcon.PLUS));
|
||||||
@@ -1793,7 +1886,10 @@ public class AddJobView extends Main {
|
|||||||
TextField todoField = new TextField();
|
TextField todoField = new TextField();
|
||||||
todoField.setPlaceholder("To-Do Punkt");
|
todoField.setPlaceholder("To-Do Punkt");
|
||||||
todoField.setWidth("100%");
|
todoField.setWidth("100%");
|
||||||
|
todoField.setRequiredIndicatorVisible(true);
|
||||||
todoField.setValue(todoText != null ? todoText : ""); // Set the saved text
|
todoField.setValue(todoText != null ? todoText : ""); // Set the saved text
|
||||||
|
// Apply styling based on value
|
||||||
|
updateTodoFieldStyling.accept(todoField);
|
||||||
|
|
||||||
Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
Button removeTodo = new Button(new Icon(VaadinIcon.CLOSE_SMALL));
|
||||||
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
removeTodo.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
|
||||||
@@ -1806,7 +1902,10 @@ public class AddJobView extends Main {
|
|||||||
todoRow.setFlexGrow(1, todoField);
|
todoRow.setFlexGrow(1, todoField);
|
||||||
todoList.add(todoRow);
|
todoList.add(todoRow);
|
||||||
|
|
||||||
todoField.addValueChangeListener(ev -> updateTodoItems(todoList, task));
|
todoField.addValueChangeListener(ev -> {
|
||||||
|
updateTodoFieldStyling.accept(todoField);
|
||||||
|
updateTodoItems(todoList, task);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add initial empty todo item if no existing items
|
// Add initial empty todo item if no existing items
|
||||||
@@ -1894,6 +1993,10 @@ public class AddJobView extends Main {
|
|||||||
if (task instanceof TodoListTask) {
|
if (task instanceof TodoListTask) {
|
||||||
((TodoListTask) task).setTodoItems(todoItems);
|
((TodoListTask) task).setTodoItems(todoItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trigger validation to update submit button state
|
||||||
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2193,6 +2296,8 @@ public class AddJobView extends Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateTaskConfiguration(configContainer, newTask);
|
updateTaskConfiguration(configContainer, newTask);
|
||||||
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2201,6 +2306,8 @@ public class AddJobView extends Main {
|
|||||||
if (taskType != null) {
|
if (taskType != null) {
|
||||||
taskTypeCombo.setValue(taskType);
|
taskTypeCombo.setValue(taskType);
|
||||||
updateTaskConfiguration(configContainer, task);
|
updateTaskConfiguration(configContainer, task);
|
||||||
|
triggerValidation();
|
||||||
|
updateTabLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
tasksList.add(taskContainer);
|
tasksList.add(taskContainer);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import com.vaadin.flow.router.Route;
|
|||||||
import de.assecutor.votianlt.security.SecurityService;
|
import de.assecutor.votianlt.security.SecurityService;
|
||||||
import de.assecutor.votianlt.pages.base.ui.view.MainLayout;
|
import de.assecutor.votianlt.pages.base.ui.view.MainLayout;
|
||||||
import jakarta.annotation.security.RolesAllowed;
|
import jakarta.annotation.security.RolesAllowed;
|
||||||
import com.vaadin.flow.component.UI;
|
|
||||||
|
|
||||||
@Route(value = "dashboard", layout = MainLayout.class)
|
@Route(value = "dashboard", layout = MainLayout.class)
|
||||||
@PageTitle("VotianLT - Dashboard")
|
@PageTitle("VotianLT - Dashboard")
|
||||||
@@ -96,12 +95,13 @@ public class AuthenticatedStartView extends VerticalLayout {
|
|||||||
systemIntro.getStyle().set("max-width", "800px");
|
systemIntro.getStyle().set("max-width", "800px");
|
||||||
systemIntro.getStyle().set("margin-bottom", "var(--lumo-space-xl)");
|
systemIntro.getStyle().set("margin-bottom", "var(--lumo-space-xl)");
|
||||||
|
|
||||||
// Features Grid
|
// Features Grid - using Div with CSS flexbox for proper centering
|
||||||
HorizontalLayout featuresGrid = new HorizontalLayout();
|
Div featuresGrid = new Div();
|
||||||
featuresGrid.setSpacing(true);
|
featuresGrid.getStyle().set("display", "flex");
|
||||||
featuresGrid.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
|
||||||
featuresGrid.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.START);
|
|
||||||
featuresGrid.getStyle().set("flex-wrap", "wrap");
|
featuresGrid.getStyle().set("flex-wrap", "wrap");
|
||||||
|
featuresGrid.getStyle().set("justify-content", "center");
|
||||||
|
featuresGrid.getStyle().set("align-items", "flex-start");
|
||||||
|
featuresGrid.getStyle().set("gap", "var(--lumo-space-l)");
|
||||||
featuresGrid.getStyle().set("width", "100%");
|
featuresGrid.getStyle().set("width", "100%");
|
||||||
|
|
||||||
// Feature Cards
|
// Feature Cards
|
||||||
@@ -126,6 +126,10 @@ public class AuthenticatedStartView extends VerticalLayout {
|
|||||||
card.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
card.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
|
||||||
card.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)");
|
card.getStyle().set("box-shadow", "var(--lumo-box-shadow-s)");
|
||||||
card.setWidth("300px");
|
card.setWidth("300px");
|
||||||
|
card.setMinWidth("300px");
|
||||||
|
card.setMaxWidth("300px");
|
||||||
|
card.getStyle().set("flex-grow", "0");
|
||||||
|
card.getStyle().set("flex-shrink", "0");
|
||||||
|
|
||||||
Icon icon = iconType.create();
|
Icon icon = iconType.create();
|
||||||
icon.setSize("48px");
|
icon.setSize("48px");
|
||||||
@@ -177,6 +181,7 @@ public class AuthenticatedStartView extends VerticalLayout {
|
|||||||
footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
footer.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
footer.getStyle().set("background-color", "var(--lumo-contrast-10pct)");
|
footer.getStyle().set("background-color", "var(--lumo-contrast-10pct)");
|
||||||
footer.getStyle().set("border-top", "1px solid var(--lumo-contrast-20pct)");
|
footer.getStyle().set("border-top", "1px solid var(--lumo-contrast-20pct)");
|
||||||
|
footer.getStyle().set("margin-top", "auto");
|
||||||
|
|
||||||
HorizontalLayout footerContent = new HorizontalLayout();
|
HorizontalLayout footerContent = new HorizontalLayout();
|
||||||
footerContent.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
footerContent.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
|
|||||||
@@ -221,6 +221,7 @@ public class StartView extends VerticalLayout implements BeforeEnterObserver {
|
|||||||
HorizontalLayout featuresGrid = new HorizontalLayout();
|
HorizontalLayout featuresGrid = new HorizontalLayout();
|
||||||
featuresGrid.setWidthFull();
|
featuresGrid.setWidthFull();
|
||||||
featuresGrid.setSpacing(true);
|
featuresGrid.setSpacing(true);
|
||||||
|
featuresGrid.setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
|
||||||
featuresGrid.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.START);
|
featuresGrid.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.START);
|
||||||
|
|
||||||
// Feature Cards
|
// Feature Cards
|
||||||
|
|||||||
Reference in New Issue
Block a user