diff --git a/Dockerfile b/Dockerfile
index 8a40812..4acecc6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,33 @@
-FROM eclipse-temurin:21-jre
-COPY target/*.jar app.jar
+FROM maven:3-eclipse-temurin-21-jammy as build
+
+# Install nodejs
+RUN apt-get update
+RUN apt-get install -y ca-certificates curl gnupg
+RUN mkdir -p /etc/apt/keyrings
+RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
+RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
+RUN apt-get update
+RUN apt-get install nodejs -y
+
+# Stop running as root at this point
+RUN useradd -m vaadin
+WORKDIR /usr/src/app/
+RUN chown vaadin:vaadin /usr/src/app/
+USER vaadin
+
+# Copy pom.xml and prefetch dependencies so a repeated build can continue from the next step with existing dependencies
+COPY --chown=vaadin pom.xml ./
+
+# Copy all needed project files to a folder
+COPY --chown=vaadin:vaadin src src
+
+# Build the production package, assuming that we validated the version before so no need for running tests again
+RUN mvn clean package -DskipTests -Pproduction --batch-mode
+
+# Running stage: the part that is used for running the application
+FROM eclipse-temurin:21-jre-jammy
+COPY --from=build /usr/src/app/target/*.jar /usr/app/app.jar
+RUN useradd -m vaadin
+USER vaadin
EXPOSE 8080
-ENTRYPOINT ["java", "-jar", "/app.jar"]
+CMD java -jar /usr/app/app.jar
diff --git a/src/main/frontend/index.html b/src/main/frontend/index.html
new file mode 100644
index 0000000..d36e593
--- /dev/null
+++ b/src/main/frontend/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/dev/mars3142/fhq/views/MainLayout.java b/src/main/java/dev/mars3142/fhq/views/MainLayout.java
index abd2a2a..f10d12a 100644
--- a/src/main/java/dev/mars3142/fhq/views/MainLayout.java
+++ b/src/main/java/dev/mars3142/fhq/views/MainLayout.java
@@ -27,7 +27,6 @@ import com.vaadin.flow.theme.lumo.LumoUtility.TextColor;
import com.vaadin.flow.theme.lumo.LumoUtility.Whitespace;
import com.vaadin.flow.theme.lumo.LumoUtility.Width;
import dev.mars3142.fhq.views.checkoutform.CheckoutFormView;
-import dev.mars3142.fhq.views.dashboard.DashboardView;
import dev.mars3142.fhq.views.myview.MyViewView;
import org.vaadin.lineawesome.LineAwesomeIcon;
@@ -102,8 +101,6 @@ public class MainLayout extends AppLayout {
private MenuItemInfo[] createMenuItems() {
return new MenuItemInfo[]{ //
- new MenuItemInfo("Dashboard", LineAwesomeIcon.CHART_AREA_SOLID.create(), DashboardView.class), //
-
new MenuItemInfo("Checkout Form", LineAwesomeIcon.CREDIT_CARD.create(), CheckoutFormView.class), //
new MenuItemInfo("My View", LineAwesomeIcon.PENCIL_RULER_SOLID.create(), MyViewView.class), //
diff --git a/src/main/java/dev/mars3142/fhq/views/checkoutform/CheckoutFormView.java b/src/main/java/dev/mars3142/fhq/views/checkoutform/CheckoutFormView.java
index 645e272..bcc9fbb 100644
--- a/src/main/java/dev/mars3142/fhq/views/checkoutform/CheckoutFormView.java
+++ b/src/main/java/dev/mars3142/fhq/views/checkoutform/CheckoutFormView.java
@@ -38,7 +38,7 @@ import java.util.LinkedHashSet;
import java.util.Set;
@PageTitle("Checkout Form")
-@Route(value = "checkout-form", layout = MainLayout.class)
+@Route(value = "", layout = MainLayout.class)
public class CheckoutFormView extends Div {
private static final Set states = new LinkedHashSet<>();
diff --git a/src/main/java/dev/mars3142/fhq/views/dashboard/DashboardView.java b/src/main/java/dev/mars3142/fhq/views/dashboard/DashboardView.java
deleted file mode 100644
index 8128dd1..0000000
--- a/src/main/java/dev/mars3142/fhq/views/dashboard/DashboardView.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package dev.mars3142.fhq.views.dashboard;
-
-
-import com.vaadin.flow.component.Component;
-import com.vaadin.flow.component.board.Board;
-import com.vaadin.flow.component.charts.Chart;
-import com.vaadin.flow.component.charts.model.*;
-import com.vaadin.flow.component.grid.ColumnTextAlign;
-import com.vaadin.flow.component.grid.Grid;
-import com.vaadin.flow.component.grid.GridVariant;
-import com.vaadin.flow.component.html.H2;
-import com.vaadin.flow.component.html.Main;
-import com.vaadin.flow.component.html.Span;
-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.component.select.Select;
-import com.vaadin.flow.data.renderer.ComponentRenderer;
-import com.vaadin.flow.router.PageTitle;
-import com.vaadin.flow.router.Route;
-import com.vaadin.flow.router.RouteAlias;
-import com.vaadin.flow.theme.lumo.LumoUtility.BoxSizing;
-import com.vaadin.flow.theme.lumo.LumoUtility.FontSize;
-import com.vaadin.flow.theme.lumo.LumoUtility.FontWeight;
-import com.vaadin.flow.theme.lumo.LumoUtility.Margin;
-import com.vaadin.flow.theme.lumo.LumoUtility.Padding;
-import com.vaadin.flow.theme.lumo.LumoUtility.TextColor;
-import dev.mars3142.fhq.views.MainLayout;
-import dev.mars3142.fhq.views.dashboard.ServiceHealth.Status;
-
-@PageTitle("Dashboard")
-@Route(value = "", layout = MainLayout.class)
-@RouteAlias(value = "", layout = MainLayout.class)
-public class DashboardView extends Main {
-
- public DashboardView() {
- addClassName("dashboard-view");
-
- Board board = new Board();
- board.addRow(createHighlight("Current users", "745", 33.7), createHighlight("View events", "54.6k", -112.45),
- createHighlight("Conversion rate", "18%", 3.9), createHighlight("Custom metric", "-123.45", 0.0));
- board.addRow(createViewEvents());
- board.addRow(createServiceHealth(), createResponseTimes());
- add(board);
- }
-
- private Component createHighlight(String title, String value, Double percentage) {
- VaadinIcon icon = VaadinIcon.ARROW_UP;
- String prefix = "";
- String theme = "badge";
-
- if (percentage == 0) {
- prefix = "±";
- } else if (percentage > 0) {
- prefix = "+";
- theme += " success";
- } else if (percentage < 0) {
- icon = VaadinIcon.ARROW_DOWN;
- theme += " error";
- }
-
- H2 h2 = new H2(title);
- h2.addClassNames(FontWeight.NORMAL, Margin.NONE, TextColor.SECONDARY, FontSize.XSMALL);
-
- Span span = new Span(value);
- span.addClassNames(FontWeight.SEMIBOLD, FontSize.XXXLARGE);
-
- Icon i = icon.create();
- i.addClassNames(BoxSizing.BORDER, Padding.XSMALL);
-
- Span badge = new Span(i, new Span(prefix + percentage.toString()));
- badge.getElement().getThemeList().add(theme);
-
- VerticalLayout layout = new VerticalLayout(h2, span, badge);
- layout.addClassName(Padding.LARGE);
- layout.setPadding(false);
- layout.setSpacing(false);
- return layout;
- }
-
- private Component createViewEvents() {
- // Header
- Select year = new Select();
- year.setItems("2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021");
- year.setValue("2021");
- year.setWidth("100px");
-
- HorizontalLayout header = createHeader("View events", "City/month");
- header.add(year);
-
- // Chart
- Chart chart = new Chart(ChartType.AREASPLINE);
- Configuration conf = chart.getConfiguration();
- conf.getChart().setStyledMode(true);
-
- XAxis xAxis = new XAxis();
- xAxis.setCategories("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
- conf.addxAxis(xAxis);
-
- conf.getyAxis().setTitle("Values");
-
- PlotOptionsAreaspline plotOptions = new PlotOptionsAreaspline();
- plotOptions.setPointPlacement(PointPlacement.ON);
- plotOptions.setMarker(new Marker(false));
- conf.addPlotOptions(plotOptions);
-
- conf.addSeries(new ListSeries("Berlin", 189, 191, 291, 396, 501, 403, 609, 712, 729, 942, 1044, 1247));
- conf.addSeries(new ListSeries("London", 138, 246, 248, 348, 352, 353, 463, 573, 778, 779, 885, 887));
- conf.addSeries(new ListSeries("New York", 65, 65, 166, 171, 293, 302, 308, 317, 427, 429, 535, 636));
- conf.addSeries(new ListSeries("Tokyo", 0, 11, 17, 123, 130, 142, 248, 349, 452, 454, 458, 462));
-
- // Add it all together
- VerticalLayout viewEvents = new VerticalLayout(header, chart);
- viewEvents.addClassName(Padding.LARGE);
- viewEvents.setPadding(false);
- viewEvents.setSpacing(false);
- viewEvents.getElement().getThemeList().add("spacing-l");
- return viewEvents;
- }
-
- private Component createServiceHealth() {
- // Header
- HorizontalLayout header = createHeader("Service health", "Input / output");
-
- // Grid
- Grid grid = new Grid();
- grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
- grid.setAllRowsVisible(true);
-
- grid.addColumn(new ComponentRenderer<>(serviceHealth -> {
- Span status = new Span();
- String statusText = getStatusDisplayName(serviceHealth);
- status.getElement().setAttribute("aria-label", "Status: " + statusText);
- status.getElement().setAttribute("title", "Status: " + statusText);
- status.getElement().getThemeList().add(getStatusTheme(serviceHealth));
- return status;
- })).setHeader("").setFlexGrow(0).setAutoWidth(true);
- grid.addColumn(ServiceHealth::getCity).setHeader("City").setFlexGrow(1);
- grid.addColumn(ServiceHealth::getInput).setHeader("Input").setAutoWidth(true).setTextAlign(ColumnTextAlign.END);
- grid.addColumn(ServiceHealth::getOutput).setHeader("Output").setAutoWidth(true)
- .setTextAlign(ColumnTextAlign.END);
-
- grid.setItems(new ServiceHealth(Status.EXCELLENT, "Münster", 324, 1540),
- new ServiceHealth(Status.OK, "Cluj-Napoca", 311, 1320),
- new ServiceHealth(Status.FAILING, "Ciudad Victoria", 300, 1219));
-
- // Add it all together
- VerticalLayout serviceHealth = new VerticalLayout(header, grid);
- serviceHealth.addClassName(Padding.LARGE);
- serviceHealth.setPadding(false);
- serviceHealth.setSpacing(false);
- serviceHealth.getElement().getThemeList().add("spacing-l");
- return serviceHealth;
- }
-
- private Component createResponseTimes() {
- HorizontalLayout header = createHeader("Response times", "Average across all systems");
-
- // Chart
- Chart chart = new Chart(ChartType.PIE);
- Configuration conf = chart.getConfiguration();
- conf.getChart().setStyledMode(true);
- chart.setThemeName("gradient");
-
- DataSeries series = new DataSeries();
- series.add(new DataSeriesItem("System 1", 12.5));
- series.add(new DataSeriesItem("System 2", 12.5));
- series.add(new DataSeriesItem("System 3", 12.5));
- series.add(new DataSeriesItem("System 4", 12.5));
- series.add(new DataSeriesItem("System 5", 12.5));
- series.add(new DataSeriesItem("System 6", 12.5));
- conf.addSeries(series);
-
- // Add it all together
- VerticalLayout serviceHealth = new VerticalLayout(header, chart);
- serviceHealth.addClassName(Padding.LARGE);
- serviceHealth.setPadding(false);
- serviceHealth.setSpacing(false);
- serviceHealth.getElement().getThemeList().add("spacing-l");
- return serviceHealth;
- }
-
- private HorizontalLayout createHeader(String title, String subtitle) {
- H2 h2 = new H2(title);
- h2.addClassNames(FontSize.XLARGE, Margin.NONE);
-
- Span span = new Span(subtitle);
- span.addClassNames(TextColor.SECONDARY, FontSize.XSMALL);
-
- VerticalLayout column = new VerticalLayout(h2, span);
- column.setPadding(false);
- column.setSpacing(false);
-
- HorizontalLayout header = new HorizontalLayout(column);
- header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
- header.setSpacing(false);
- header.setWidthFull();
- return header;
- }
-
- private String getStatusDisplayName(ServiceHealth serviceHealth) {
- Status status = serviceHealth.getStatus();
- if (status == Status.OK) {
- return "Ok";
- } else if (status == Status.FAILING) {
- return "Failing";
- } else if (status == Status.EXCELLENT) {
- return "Excellent";
- } else {
- return status.toString();
- }
- }
-
- private String getStatusTheme(ServiceHealth serviceHealth) {
- Status status = serviceHealth.getStatus();
- String theme = "badge primary small";
- if (status == Status.EXCELLENT) {
- theme += " success";
- } else if (status == Status.FAILING) {
- theme += " error";
- }
- return theme;
- }
-
-}
diff --git a/src/main/java/dev/mars3142/fhq/views/dashboard/ServiceHealth.java b/src/main/java/dev/mars3142/fhq/views/dashboard/ServiceHealth.java
deleted file mode 100644
index 659b976..0000000
--- a/src/main/java/dev/mars3142/fhq/views/dashboard/ServiceHealth.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package dev.mars3142.fhq.views.dashboard;
-
-/**
- * Simple DTO class for the inbox list to demonstrate complex object data
- */
-public class ServiceHealth {
-
- private Status status;
-
- private String city;
-
- private int input;
-
- private int output;
-
- private String theme;
-
- enum Status {
- EXCELLENT, OK, FAILING;
- }
-
- public ServiceHealth() {
-
- }
-
- public ServiceHealth(Status status, String city, int input, int output) {
- this.status = status;
- this.city = city;
- this.input = input;
- this.output = output;
- }
-
- public Status getStatus() {
- return status;
- }
-
- public void setStatus(Status status) {
- this.status = status;
- }
-
- public String getCity() {
- return city;
- }
-
- public void setCity(String city) {
- this.city = city;
- }
-
- public int getInput() {
- return input;
- }
-
- public void setInput(int input) {
- this.input = input;
- }
-
- public int getOutput() {
- return output;
- }
-
- public void setOutput(int output) {
- this.output = output;
- }
-
-}