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; - } - -}