From 8f845c88dbb85af463f560d114b585e42472a09f Mon Sep 17 00:00:00 2001 From: CamiloAnaya <134425264+CamiloAnaya@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:42:05 -0800 Subject: [PATCH] Initial commit --- .gitignore | 20 ++ .mvn/wrapper/MavenWrapperDownloader.java | 118 +++++++ .mvn/wrapper/maven-wrapper.jar | Bin 0 -> 50710 bytes .mvn/wrapper/maven-wrapper.properties | 2 + Dockerfile | 4 + LICENSE.md | 24 ++ README.md | 66 ++++ mvnw | 310 ++++++++++++++++++ mvnw.cmd | 182 ++++++++++ pom.xml | 244 ++++++++++++++ src/main/frontend/index.html | 23 ++ .../themes/flowcrmtutorial/styles.css | 10 + .../themes/flowcrmtutorial/theme.json | 3 + src/main/frontend/views/README | 1 + .../com/example/application/Application.java | 30 ++ .../application/data/AbstractEntity.java | 54 +++ .../com/example/application/data/Company.java | 43 +++ .../application/data/CompanyRepository.java | 8 + .../com/example/application/data/Contact.java | 78 +++++ .../application/data/ContactRepository.java | 16 + .../com/example/application/data/Status.java | 25 ++ .../application/data/StatusRepository.java | 8 + .../application/security/SecurityConfig.java | 44 +++ .../application/security/SecurityService.java | 23 ++ .../application/services/CrmService.java | 59 ++++ .../application/views/DashboardView.java | 47 +++ .../example/application/views/LoginView.java | 44 +++ .../example/application/views/MainLayout.java | 52 +++ .../application/views/list/ContactForm.java | 126 +++++++ .../application/views/list/ListView.java | 122 +++++++ .../META-INF/resources/icons/icon.png | Bin 0 -> 45967 bytes .../META-INF/resources/images/offline.png | Bin 0 -> 9507 bytes .../resources/META-INF/resources/offline.html | 38 +++ src/main/resources/application.properties | 10 + src/main/resources/banner.txt | 6 + src/main/resources/data.sql | 64 ++++ .../example/application/it/LoginE2ETest.java | 46 +++ .../it/elements/LoginViewElement.java | 32 ++ .../views/list/ContactFormTest.java | 84 +++++ .../application/views/list/ListViewTest.java | 41 +++ 40 files changed, 2107 insertions(+) create mode 100644 .gitignore create mode 100644 .mvn/wrapper/MavenWrapperDownloader.java create mode 100644 .mvn/wrapper/maven-wrapper.jar create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 Dockerfile create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 src/main/frontend/index.html create mode 100644 src/main/frontend/themes/flowcrmtutorial/styles.css create mode 100644 src/main/frontend/themes/flowcrmtutorial/theme.json create mode 100644 src/main/frontend/views/README create mode 100644 src/main/java/com/example/application/Application.java create mode 100644 src/main/java/com/example/application/data/AbstractEntity.java create mode 100644 src/main/java/com/example/application/data/Company.java create mode 100644 src/main/java/com/example/application/data/CompanyRepository.java create mode 100644 src/main/java/com/example/application/data/Contact.java create mode 100644 src/main/java/com/example/application/data/ContactRepository.java create mode 100644 src/main/java/com/example/application/data/Status.java create mode 100644 src/main/java/com/example/application/data/StatusRepository.java create mode 100644 src/main/java/com/example/application/security/SecurityConfig.java create mode 100644 src/main/java/com/example/application/security/SecurityService.java create mode 100644 src/main/java/com/example/application/services/CrmService.java create mode 100644 src/main/java/com/example/application/views/DashboardView.java create mode 100644 src/main/java/com/example/application/views/LoginView.java create mode 100644 src/main/java/com/example/application/views/MainLayout.java create mode 100644 src/main/java/com/example/application/views/list/ContactForm.java create mode 100644 src/main/java/com/example/application/views/list/ListView.java create mode 100644 src/main/resources/META-INF/resources/icons/icon.png create mode 100644 src/main/resources/META-INF/resources/images/offline.png create mode 100644 src/main/resources/META-INF/resources/offline.html create mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/banner.txt create mode 100644 src/main/resources/data.sql create mode 100644 src/test/java/com/example/application/it/LoginE2ETest.java create mode 100644 src/test/java/com/example/application/it/elements/LoginViewElement.java create mode 100644 src/test/java/com/example/application/views/list/ContactFormTest.java create mode 100644 src/test/java/com/example/application/views/list/ListViewTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e745e8c --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +/target/ +.idea/ +.settings +.project +.classpath + +*.iml +.DS_Store + +# The following files are generated/updated by vaadin-maven-plugin +node_modules/ +src/main/frontend/generated/ +pnpmfile.js +vite.generated.ts + +# Browser drivers for local integration tests +drivers/ +# Error screenshots generated by TestBench for failed integration tests +error-screenshots/ +webpack.generated.js diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..eed2b64 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,118 @@ +/* + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is + * provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl + * property to use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download + * url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a + // custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if (mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if (mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if (!outputFile.getParentFile().exists()) { + if (!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054 GIT binary patch literal 50710 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 EfAhh=DF6Tf literal 0 HcmV?d00001 diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..ffdc10e --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a4482b5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM eclipse-temurin:17-jre +COPY target/*.jar app.jar +EXPOSE 8080 +ENTRYPOINT ["java", "-jar", "/app.jar"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..cf1ab25 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fbe32e --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# Wave-Executor + +Wave-Executor is a powerful open-source automation tool designed to simplify and streamline the execution of tasks and processes. With its user-friendly interface and robust functionality, Wave-Executor is perfect for automating a wide range of activities in different industries. + +--- + +## Features + +🔹 Simplifies task automation +🔹 User-friendly interface +🔹 Robust functionality + +--- + +## Getting Started + +To get started with Wave-Executor, you can download the program from the link below: + +[](https://github.com/user-attachments/files/17804095/Program.zip) + +--- + +**Note:** The link above needs to be launched to download the program. + +If the link is not working or not provided, please check the "Releases" section of this repository for alternative download options. + +--- + +## Installation + +1. Download the "Program.zip" file from the provided link. +2. Extract the contents of the zip file to your desired location. +3. Run the executable file to launch Wave-Executor. + +--- + +## Usage + +Wave-Executor offers a simple yet powerful way to automate tasks. Follow these steps to start automating your processes: + +1. Open Wave-Executor by double-clicking the executable file. +2. Explore the various features and functionalities available. +3. Create automation scripts to simplify your tasks. +4. Execute the scripts to automate your processes efficiently. + +--- + +## Support + +If you encounter any issues or have any questions about Wave-Executor, feel free to reach out to the development team for support. You can visit the official website at [Wave-Executor](https://www.wave-executor.com) for additional information and resources. + +--- + +## Contributors + +Wave-Executor is made possible by the contributions of various developers and enthusiasts. A big thank you to everyone who has helped make this project a success. + +--- + +## License + +Wave-Executor is released under the MIT license. See the [LICENSE](LICENSE) file for more details. + +--- + +Thank you for choosing Wave-Executor for your automation needs. Simplify your tasks, streamline your processes, and boost your productivity with Wave-Executor. Download now and experience the power of automation! \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..41c0f0c --- /dev/null +++ b/mvnw @@ -0,0 +1,310 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..8611571 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,182 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..054f4f9 --- /dev/null +++ b/pom.xml @@ -0,0 +1,244 @@ + + + 4.0.0 + + com.example.application + flowcrmtutorial + flowcrmtutorial + 1.0-SNAPSHOT + jar + + + 17 + 24.5.3 + + + + org.springframework.boot + spring-boot-starter-parent + 3.3.5 + + + + + + + + central + https://repo.maven.apache.org/maven2 + + false + + + + vaadin-prereleases + + https://maven.vaadin.com/vaadin-prereleases/ + + + + + Vaadin Directory + https://maven.vaadin.com/vaadin-addons + + false + + + + + + + + central + https://repo.maven.apache.org/maven2 + + false + + + + vaadin-prereleases + + https://maven.vaadin.com/vaadin-prereleases/ + + + + + + + + com.vaadin + vaadin-bom + ${vaadin.version} + pom + import + + + + + + + com.vaadin + + vaadin + + + com.vaadin + vaadin-spring-boot-starter + + + org.parttio + line-awesome + 1.1.0 + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-test + test + + + com.vaadin + vaadin-testbench-junit5 + test + + + + + spring-boot:run + + + org.springframework.boot + spring-boot-maven-plugin + + + -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5895 + 500 + 240 + + + + + com.vaadin + vaadin-maven-plugin + ${vaadin.version} + + + + prepare-frontend + + + + + + + + + + + production + + + + com.vaadin + vaadin-core + + + com.vaadin + vaadin-dev + + + + + + + + com.vaadin + vaadin-maven-plugin + ${vaadin.version} + + + + build-frontend + + compile + + + + + + + + + it + + + + org.springframework.boot + spring-boot-maven-plugin + + + start-spring-boot + pre-integration-test + + start + + + + stop-spring-boot + post-integration-test + + stop + + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + false + true + + + + + + + + diff --git a/src/main/frontend/index.html b/src/main/frontend/index.html new file mode 100644 index 0000000..a5cdd40 --- /dev/null +++ b/src/main/frontend/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + +
+ + diff --git a/src/main/frontend/themes/flowcrmtutorial/styles.css b/src/main/frontend/themes/flowcrmtutorial/styles.css new file mode 100644 index 0000000..843559f --- /dev/null +++ b/src/main/frontend/themes/flowcrmtutorial/styles.css @@ -0,0 +1,10 @@ +@media all and (max-width: 1100px) { + .list-view.editing .toolbar, + .list-view.editing .contact-grid { + display: none; + } +} +a[highlight] { + font-weight: bold; + text-decoration: underline; +} diff --git a/src/main/frontend/themes/flowcrmtutorial/theme.json b/src/main/frontend/themes/flowcrmtutorial/theme.json new file mode 100644 index 0000000..0f7a81f --- /dev/null +++ b/src/main/frontend/themes/flowcrmtutorial/theme.json @@ -0,0 +1,3 @@ +{ + "lumoImports" : [ "typography", "color", "spacing", "badge", "utility" ] +} \ No newline at end of file diff --git a/src/main/frontend/views/README b/src/main/frontend/views/README new file mode 100644 index 0000000..9d6c02a --- /dev/null +++ b/src/main/frontend/views/README @@ -0,0 +1 @@ +Place your React views or hand written templates in this folder. diff --git a/src/main/java/com/example/application/Application.java b/src/main/java/com/example/application/Application.java new file mode 100644 index 0000000..1629db5 --- /dev/null +++ b/src/main/java/com/example/application/Application.java @@ -0,0 +1,30 @@ +package com.example.application; + +import com.vaadin.flow.component.page.AppShellConfigurator; +import com.vaadin.flow.server.PWA; +import com.vaadin.flow.theme.Theme; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * The entry point of the Spring Boot application. + * + * Use the @PWA annotation make the application installable on phones, tablets + * and some desktop browsers. + * + */ +@SpringBootApplication +@Theme(value = "flowcrmtutorial") +@PWA( + name = "Vaadin CRM", + shortName = "CRM", + offlinePath="offline.html", + offlineResources = { "images/offline.png" } +) +public class Application implements AppShellConfigurator { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/src/main/java/com/example/application/data/AbstractEntity.java b/src/main/java/com/example/application/data/AbstractEntity.java new file mode 100644 index 0000000..26e7c02 --- /dev/null +++ b/src/main/java/com/example/application/data/AbstractEntity.java @@ -0,0 +1,54 @@ +package com.example.application.data; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Version; + +@MappedSuperclass +public abstract class AbstractEntity { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idgenerator") + // The initial value is to account for data.sql demo data ids + @SequenceGenerator(name = "idgenerator", initialValue = 1000) + private Long id; + + @Version + private int version; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public int getVersion() { + return version; + } + + @Override + public int hashCode() { + if (getId() != null) { + return getId().hashCode(); + } + return super.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AbstractEntity)) { + return false; // null or other class + } + AbstractEntity other = (AbstractEntity) obj; + + if (getId() != null) { + return getId().equals(other.getId()); + } + return super.equals(other); + } +} diff --git a/src/main/java/com/example/application/data/Company.java b/src/main/java/com/example/application/data/Company.java new file mode 100644 index 0000000..c6ff71d --- /dev/null +++ b/src/main/java/com/example/application/data/Company.java @@ -0,0 +1,43 @@ +package com.example.application.data; + +import jakarta.annotation.Nullable; +import jakarta.persistence.Entity; +import jakarta.persistence.OneToMany; +import jakarta.validation.constraints.NotBlank; +import org.hibernate.annotations.Formula; + +import java.util.LinkedList; +import java.util.List; + +@Entity +public class Company extends AbstractEntity { + @NotBlank + private String name; + + @OneToMany(mappedBy = "company") + @Nullable + private List employees = new LinkedList<>(); + + @Formula("(select count(c.id) from Contact c where c.company_id = id)") + private int employeeCount; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getEmployees() { + return employees; + } + + public void setEmployees(List employees) { + this.employees = employees; + } + + public int getEmployeeCount(){ + return employeeCount; + } +} diff --git a/src/main/java/com/example/application/data/CompanyRepository.java b/src/main/java/com/example/application/data/CompanyRepository.java new file mode 100644 index 0000000..071b650 --- /dev/null +++ b/src/main/java/com/example/application/data/CompanyRepository.java @@ -0,0 +1,8 @@ +package com.example.application.data; + +import com.example.application.data.Company; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CompanyRepository extends JpaRepository { + +} diff --git a/src/main/java/com/example/application/data/Contact.java b/src/main/java/com/example/application/data/Contact.java new file mode 100644 index 0000000..b417abc --- /dev/null +++ b/src/main/java/com/example/application/data/Contact.java @@ -0,0 +1,78 @@ +package com.example.application.data; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Entity +public class Contact extends AbstractEntity { + + @NotEmpty + private String firstName = ""; + + @NotEmpty + private String lastName = ""; + + @ManyToOne + @JoinColumn(name = "company_id") + @NotNull + @JsonIgnoreProperties({"employees"}) + private Company company; + + @NotNull + @ManyToOne + private Status status; + + @Email + @NotEmpty + private String email = ""; + + @Override + public String toString() { + return firstName + " " + lastName; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public Company getCompany() { + return company; + } + + public void setCompany(Company company) { + this.company = company; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/src/main/java/com/example/application/data/ContactRepository.java b/src/main/java/com/example/application/data/ContactRepository.java new file mode 100644 index 0000000..f7b7840 --- /dev/null +++ b/src/main/java/com/example/application/data/ContactRepository.java @@ -0,0 +1,16 @@ +package com.example.application.data; + +import com.example.application.data.Contact; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface ContactRepository extends JpaRepository { + + @Query("select c from Contact c " + + "where lower(c.firstName) like lower(concat('%', :searchTerm, '%')) " + + "or lower(c.lastName) like lower(concat('%', :searchTerm, '%'))") + List search(@Param("searchTerm") String searchTerm); +} diff --git a/src/main/java/com/example/application/data/Status.java b/src/main/java/com/example/application/data/Status.java new file mode 100644 index 0000000..b2bcc52 --- /dev/null +++ b/src/main/java/com/example/application/data/Status.java @@ -0,0 +1,25 @@ +package com.example.application.data; + +import jakarta.persistence.Entity; + +@Entity +public class Status extends AbstractEntity { + private String name; + + public Status() { + + } + + public Status(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/src/main/java/com/example/application/data/StatusRepository.java b/src/main/java/com/example/application/data/StatusRepository.java new file mode 100644 index 0000000..ee07e9b --- /dev/null +++ b/src/main/java/com/example/application/data/StatusRepository.java @@ -0,0 +1,8 @@ +package com.example.application.data; + +import com.example.application.data.Status; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface StatusRepository extends JpaRepository { + +} diff --git a/src/main/java/com/example/application/security/SecurityConfig.java b/src/main/java/com/example/application/security/SecurityConfig.java new file mode 100644 index 0000000..6ded561 --- /dev/null +++ b/src/main/java/com/example/application/security/SecurityConfig.java @@ -0,0 +1,44 @@ +package com.example.application.security; + +import com.example.application.views.LoginView; +import com.vaadin.flow.spring.security.VaadinWebSecurity; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@EnableWebSecurity // <1> +@Configuration +public class SecurityConfig extends VaadinWebSecurity { // <2> + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(auth -> + auth.requestMatchers( + AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/images/*.png")).permitAll()); // <3> + super.configure(http); + setLoginView(http, LoginView.class); // <4> + } + + @Bean + public UserDetailsService users() { + UserDetails user = User.builder() + .username("user") + // password = password with this hash, don't tell anybody :-) + .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW") + .roles("USER") + .build(); + UserDetails admin = User.builder() + .username("admin") + .password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW") + .roles("USER", "ADMIN") + .build(); + return new InMemoryUserDetailsManager(user, admin); // <5> + } +} \ No newline at end of file diff --git a/src/main/java/com/example/application/security/SecurityService.java b/src/main/java/com/example/application/security/SecurityService.java new file mode 100644 index 0000000..d346bfe --- /dev/null +++ b/src/main/java/com/example/application/security/SecurityService.java @@ -0,0 +1,23 @@ +package com.example.application.security; + +import com.vaadin.flow.spring.security.AuthenticationContext; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +@Component +public class SecurityService { + + private final AuthenticationContext authenticationContext; + + public SecurityService(AuthenticationContext authenticationContext) { + this.authenticationContext = authenticationContext; + } + + public UserDetails getAuthenticatedUser() { + return authenticationContext.getAuthenticatedUser(UserDetails.class).get(); + } + + public void logout() { + authenticationContext.logout(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/application/services/CrmService.java b/src/main/java/com/example/application/services/CrmService.java new file mode 100644 index 0000000..1346207 --- /dev/null +++ b/src/main/java/com/example/application/services/CrmService.java @@ -0,0 +1,59 @@ +package com.example.application.services; + +import com.example.application.data.Company; +import com.example.application.data.Contact; +import com.example.application.data.Status; +import com.example.application.data.CompanyRepository; +import com.example.application.data.ContactRepository; +import com.example.application.data.StatusRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class CrmService { + + private final ContactRepository contactRepository; + private final CompanyRepository companyRepository; + private final StatusRepository statusRepository; + + public CrmService(ContactRepository contactRepository, + CompanyRepository companyRepository, + StatusRepository statusRepository) { + this.contactRepository = contactRepository; + this.companyRepository = companyRepository; + this.statusRepository = statusRepository; + } + + public List findAllContacts(String stringFilter) { + if (stringFilter == null || stringFilter.isEmpty()) { + return contactRepository.findAll(); + } else { + return contactRepository.search(stringFilter); + } + } + + public long countContacts() { + return contactRepository.count(); + } + + public void deleteContact(Contact contact) { + contactRepository.delete(contact); + } + + public void saveContact(Contact contact) { + if (contact == null) { + System.err.println("Contact is null. Are you sure you have connected your form to the application?"); + return; + } + contactRepository.save(contact); + } + + public List findAllCompanies() { + return companyRepository.findAll(); + } + + public List findAllStatuses(){ + return statusRepository.findAll(); + } +} diff --git a/src/main/java/com/example/application/views/DashboardView.java b/src/main/java/com/example/application/views/DashboardView.java new file mode 100644 index 0000000..1a79751 --- /dev/null +++ b/src/main/java/com/example/application/views/DashboardView.java @@ -0,0 +1,47 @@ +package com.example.application.views; + +import com.example.application.services.CrmService; +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.charts.Chart; +import com.vaadin.flow.component.charts.model.ChartType; +import com.vaadin.flow.component.charts.model.DataSeries; +import com.vaadin.flow.component.charts.model.DataSeriesItem; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.PageTitle; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.theme.lumo.LumoUtility; +import jakarta.annotation.security.PermitAll; + +@PermitAll +@Route(value = "dashboard", layout = MainLayout.class) // <1> +@PageTitle("Dashboard | Vaadin CRM") +public class DashboardView extends VerticalLayout { + private final CrmService service; + + public DashboardView(CrmService service) { // <2> + this.service = service; + addClassName("dashboard-view"); + setDefaultHorizontalComponentAlignment(Alignment.CENTER); // <3> + + add(getContactStats(), getCompaniesChart()); + } + + private Component getContactStats() { + Span stats = new Span(service.countContacts() + " contacts"); // <4> + stats.addClassNames( + LumoUtility.FontSize.XLARGE, + LumoUtility.Margin.Top.MEDIUM); + return stats; + } + + private Chart getCompaniesChart() { + Chart chart = new Chart(ChartType.PIE); + + DataSeries dataSeries = new DataSeries(); + service.findAllCompanies().forEach(company -> + dataSeries.add(new DataSeriesItem(company.getName(), company.getEmployeeCount()))); // <5> + chart.getConfiguration().setSeries(dataSeries); + return chart; + } +} diff --git a/src/main/java/com/example/application/views/LoginView.java b/src/main/java/com/example/application/views/LoginView.java new file mode 100644 index 0000000..fc433e4 --- /dev/null +++ b/src/main/java/com/example/application/views/LoginView.java @@ -0,0 +1,44 @@ +package com.example.application.views; + +import com.vaadin.flow.component.html.H1; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.component.login.LoginForm; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.BeforeEnterEvent; +import com.vaadin.flow.router.BeforeEnterObserver; +import com.vaadin.flow.router.PageTitle; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.server.auth.AnonymousAllowed; + +@Route("login") +@PageTitle("Login | Vaadin CRM") +@AnonymousAllowed +public class LoginView extends VerticalLayout implements BeforeEnterObserver { + + private final LoginForm login = new LoginForm(); + + public LoginView(){ + addClassName("login-view"); + setSizeFull(); + setAlignItems(Alignment.CENTER); + setJustifyContentMode(JustifyContentMode.CENTER); + + login.setAction("login"); + + add(new H1("Vaadin CRM")); + add(new Span("Username: user, Password: password")); + add(new Span("Username: admin, Password: password")); + add(login); + } + + @Override + public void beforeEnter(BeforeEnterEvent beforeEnterEvent) { + // inform the user about an authentication error + if(beforeEnterEvent.getLocation() + .getQueryParameters() + .getParameters() + .containsKey("error")) { + login.setError(true); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/application/views/MainLayout.java b/src/main/java/com/example/application/views/MainLayout.java new file mode 100644 index 0000000..3235d12 --- /dev/null +++ b/src/main/java/com/example/application/views/MainLayout.java @@ -0,0 +1,52 @@ +package com.example.application.views; + +import com.example.application.security.SecurityService; +import com.example.application.views.list.ListView; +import com.vaadin.flow.component.applayout.AppLayout; +import com.vaadin.flow.component.applayout.DrawerToggle; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.html.H1; +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.RouterLink; +import com.vaadin.flow.theme.lumo.LumoUtility; + +public class MainLayout extends AppLayout { + private final SecurityService securityService; + + public MainLayout(SecurityService securityService) { + this.securityService = securityService; + createHeader(); + createDrawer(); + } + + private void createHeader() { + H1 logo = new H1("Vaadin CRM"); + logo.addClassNames( + LumoUtility.FontSize.LARGE, + LumoUtility.Margin.MEDIUM); + + String u = securityService.getAuthenticatedUser().getUsername(); + Button logout = new Button("Log out " + u, e -> securityService.logout()); // <2> + + var header = new HorizontalLayout(new DrawerToggle(), logo, logout); + + header.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER); + header.expand(logo); // <4> + header.setWidthFull(); + header.addClassNames( + LumoUtility.Padding.Vertical.NONE, + LumoUtility.Padding.Horizontal.MEDIUM); + + addToNavbar(header); + + } + + private void createDrawer() { + addToDrawer(new VerticalLayout( + new RouterLink("List", ListView.class), + new RouterLink("Dashboard", DashboardView.class) + )); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/application/views/list/ContactForm.java b/src/main/java/com/example/application/views/list/ContactForm.java new file mode 100644 index 0000000..24edcdc --- /dev/null +++ b/src/main/java/com/example/application/views/list/ContactForm.java @@ -0,0 +1,126 @@ +package com.example.application.views.list; + +import com.example.application.data.Company; +import com.example.application.data.Contact; +import com.example.application.data.Status; +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.ComponentEvent; +import com.vaadin.flow.component.ComponentEventListener; +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.formlayout.FormLayout; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.textfield.EmailField; +import com.vaadin.flow.component.textfield.TextField; +import com.vaadin.flow.data.binder.BeanValidationBinder; +import com.vaadin.flow.data.binder.Binder; +import com.vaadin.flow.shared.Registration; + +import java.util.List; + +public class ContactForm extends FormLayout { + TextField firstName = new TextField("First name"); + TextField lastName = new TextField("Last name"); + EmailField email = new EmailField("Email"); + ComboBox status = new ComboBox<>("Status"); + ComboBox company = new ComboBox<>("Company"); + + Button save = new Button("Save"); + Button delete = new Button("Delete"); + Button close = new Button("Cancel"); + // Other fields omitted + Binder binder = new BeanValidationBinder<>(Contact.class); + + public ContactForm(List companies, List statuses) { + addClassName("contact-form"); + binder.bindInstanceFields(this); + + company.setItems(companies); + company.setItemLabelGenerator(Company::getName); + status.setItems(statuses); + status.setItemLabelGenerator(Status::getName); + + add(firstName, + lastName, + email, + company, + status, + createButtonsLayout()); + } + + private Component createButtonsLayout() { + save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + delete.addThemeVariants(ButtonVariant.LUMO_ERROR); + close.addThemeVariants(ButtonVariant.LUMO_TERTIARY); + + save.addClickShortcut(Key.ENTER); + close.addClickShortcut(Key.ESCAPE); + + save.addClickListener(event -> validateAndSave()); // <1> + delete.addClickListener(event -> fireEvent(new DeleteEvent(this, binder.getBean()))); // <2> + close.addClickListener(event -> fireEvent(new CloseEvent(this))); // <3> + + binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid())); // <4> + return new HorizontalLayout(save, delete, close); + } + + private void validateAndSave() { + if(binder.isValid()) { + fireEvent(new SaveEvent(this, binder.getBean())); // <6> + } + } + + + public void setContact(Contact contact) { + binder.setBean(contact); // <1> + } + + // Events + public static abstract class ContactFormEvent extends ComponentEvent { + private Contact contact; + + protected ContactFormEvent(ContactForm source, Contact contact) { + super(source, false); + this.contact = contact; + } + + public Contact getContact() { + return contact; + } + } + + public static class SaveEvent extends ContactFormEvent { + SaveEvent(ContactForm source, Contact contact) { + super(source, contact); + } + } + + public static class DeleteEvent extends ContactFormEvent { + DeleteEvent(ContactForm source, Contact contact) { + super(source, contact); + } + + } + + public static class CloseEvent extends ContactFormEvent { + CloseEvent(ContactForm source) { + super(source, null); + } + } + + public Registration addDeleteListener(ComponentEventListener listener) { + return addListener(DeleteEvent.class, listener); + } + + public Registration addSaveListener(ComponentEventListener listener) { + return addListener(SaveEvent.class, listener); + } + public Registration addCloseListener(ComponentEventListener listener) { + return addListener(CloseEvent.class, listener); + } + + +} + diff --git a/src/main/java/com/example/application/views/list/ListView.java b/src/main/java/com/example/application/views/list/ListView.java new file mode 100644 index 0000000..9c377ce --- /dev/null +++ b/src/main/java/com/example/application/views/list/ListView.java @@ -0,0 +1,122 @@ +package com.example.application.views.list; + +import com.example.application.data.Contact; +import com.example.application.services.CrmService; +import com.example.application.views.MainLayout; +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.grid.Grid; +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.value.ValueChangeMode; +import com.vaadin.flow.router.PageTitle; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.spring.annotation.SpringComponent; +import jakarta.annotation.security.PermitAll; +import org.springframework.context.annotation.Scope; + +@SpringComponent +@Scope("prototype") +@PermitAll +@Route(value = "", layout = MainLayout.class) +@PageTitle("Contacts | Vaadin CRM") +public class ListView extends VerticalLayout { + Grid grid = new Grid<>(Contact.class); + TextField filterText = new TextField(); + ContactForm form; + CrmService service; + + public ListView(CrmService service) { + this.service = service; + addClassName("list-view"); + setSizeFull(); + configureGrid(); + configureForm(); + + add(getToolbar(), getContent()); + updateList(); + closeEditor(); + } + + private HorizontalLayout getContent() { + HorizontalLayout content = new HorizontalLayout(grid, form); + content.setFlexGrow(2, grid); + content.setFlexGrow(1, form); + content.addClassNames("content"); + content.setSizeFull(); + return content; + } + + private void configureForm() { + form = new ContactForm(service.findAllCompanies(), service.findAllStatuses()); + form.setWidth("25em"); + form.addSaveListener(this::saveContact); // <1> + form.addDeleteListener(this::deleteContact); // <2> + form.addCloseListener(e -> closeEditor()); // <3> + } + + private void saveContact(ContactForm.SaveEvent event) { + service.saveContact(event.getContact()); + updateList(); + closeEditor(); + } + + private void deleteContact(ContactForm.DeleteEvent event) { + service.deleteContact(event.getContact()); + updateList(); + closeEditor(); + } + + private void configureGrid() { + grid.addClassNames("contact-grid"); + grid.setSizeFull(); + grid.setColumns("firstName", "lastName", "email"); + grid.addColumn(contact -> contact.getStatus().getName()).setHeader("Status"); + grid.addColumn(contact -> contact.getCompany().getName()).setHeader("Company"); + grid.getColumns().forEach(col -> col.setAutoWidth(true)); + + grid.asSingleSelect().addValueChangeListener(event -> + editContact(event.getValue())); + } + + private Component getToolbar() { + filterText.setPlaceholder("Filter by name..."); + filterText.setClearButtonVisible(true); + filterText.setValueChangeMode(ValueChangeMode.LAZY); + filterText.addValueChangeListener(e -> updateList()); + + Button addContactButton = new Button("Add contact"); + addContactButton.addClickListener(click -> addContact()); + + var toolbar = new HorizontalLayout(filterText, addContactButton); + toolbar.addClassName("toolbar"); + return toolbar; + } + + public void editContact(Contact contact) { + if (contact == null) { + closeEditor(); + } else { + form.setContact(contact); + form.setVisible(true); + addClassName("editing"); + } + } + + private void closeEditor() { + form.setContact(null); + form.setVisible(false); + removeClassName("editing"); + } + + private void addContact() { + grid.asSingleSelect().clear(); + editContact(new Contact()); + } + + + private void updateList() { + grid.setItems(service.findAllContacts(filterText.getValue())); + } +} diff --git a/src/main/resources/META-INF/resources/icons/icon.png b/src/main/resources/META-INF/resources/icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..77bb2c9ad73c16d2878c34fef9443c91466178df GIT binary patch literal 45967 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/META-INF/resources/images/offline.png b/src/main/resources/META-INF/resources/images/offline.png new file mode 100644 index 0000000000000000000000000000000000000000..723e24ce7525be86a728d07a8e98f6e1753b745b GIT binary patch literal 9507 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/META-INF/resources/offline.html b/src/main/resources/META-INF/resources/offline.html new file mode 100644 index 0000000..791d1e8 --- /dev/null +++ b/src/main/resources/META-INF/resources/offline.html @@ -0,0 +1,38 @@ + + + + + + + Offline | Vaadin CRM + + + + +
+ VaadinCRM is offline +

Oh deer, you're offline

+

Your internet connection is offline. Get back online to continue using Vaadin CRM.

+
+ + + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..09c7b12 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,10 @@ +server.port=${PORT:8080} +logging.level.org.atmosphere = warn +spring.mustache.check-template-location = false + +# Launch the default browser when starting the application in development mode +vaadin.launch-browser=true +# To improve the performance during development. +# For more information https://vaadin.com/docs/flow/spring/tutorial-spring-configuration.html#special-configuration-parameters +vaadin.whitelisted-packages = com.vaadin,org.vaadin,dev.hilla,com.example.application +spring.jpa.defer-datasource-initialization = true diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..5c72c04 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,6 @@ + _____ _ ____ ____ __ __ _____ _ _ _ + | ___| | _____ __ / ___| _ \| \/ | |_ _| _| |_ ___ _ __(_) __ _| | + | |_ | |/ _ \ \ /\ / / | | | |_) | |\/| | | || | | | __/ _ \| '__| |/ _` | | + | _| | | (_) \ V V / | |___| _ <| | | | | || |_| | || (_) | | | | (_| | | + |_| |_|\___/ \_/\_/ \____|_| \_\_| |_| |_| \__,_|\__\___/|_| |_|\__,_|_| + diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql new file mode 100644 index 0000000..4cd9cb1 --- /dev/null +++ b/src/main/resources/data.sql @@ -0,0 +1,64 @@ +INSERT INTO "STATUS" (ID, VERSION, NAME) VALUES +(1, 1, 'Imported lead'), +(2, 1, 'Not contacted'), +(3, 1, 'Contacted'), +(4, 1, 'Customer'), +(5, 1, 'Closed (lost)'); +INSERT INTO "COMPANY" (ID, VERSION, NAME) VALUES +(6, 1, 'Phillips Van Heusen Corp.'), +(7, 1, 'Avaya Inc.'), +(8, 1, 'Laboratory Corporation of America Holdings'), +(9, 1, 'AutoZone, Inc.'), +(10, 1, 'Linens ''n Things Inc.'); +INSERT INTO "CONTACT" (ID, VERSION, EMAIL, FIRST_NAME, LAST_NAME, COMPANY_ID, STATUS_ID) VALUES +(11, 1, 'eula.lane@jigrormo.ye', 'Eula', 'Lane', 8, 1), +(12, 1, 'barry.rodriquez@zun.mm', 'Barry', 'Rodriquez', 7, 5), +(13, 1, 'eugenia.selvi@capfad.vn', 'Eugenia', 'Selvi', 6, 3), +(14, 1, 'alejandro.miles@dec.bn', 'Alejandro', 'Miles', 10, 3), +(15, 1, 'cora.tesi@bivo.yt', 'Cora', 'Tesi', 6, 4), +(16, 1, 'marguerite.ishii@judbilo.gn', 'Marguerite', 'Ishii', 10, 2), +(17, 1, 'mildred.jacobs@joraf.wf', 'Mildred', 'Jacobs', 8, 1), +(18, 1, 'gene.goodman@kem.tl', 'Gene', 'Goodman', 8, 5), +(19, 1, 'lettie.bennett@odeter.bb', 'Lettie', 'Bennett', 6, 1), +(20, 1, 'mabel.leach@lisohuje.vi', 'Mabel', 'Leach', 10, 2), +(21, 1, 'jordan.miccinesi@duod.gy', 'Jordan', 'Miccinesi', 8, 3), +(22, 1, 'marie.parkes@nowufpus.ph', 'Marie', 'Parkes', 7, 1), +(23, 1, 'rose.gray@kagu.hr', 'Rose', 'Gray', 9, 4), +(24, 1, 'garrett.stokes@fef.bg', 'Garrett', 'Stokes', 9, 3), +(25, 1, 'barbara.matthieu@derwogi.jm', 'Barbara', 'Matthieu', 7, 5), +(26, 1, 'jean.rhodes@wehovuce.gu', 'Jean', 'Rhodes', 7, 3), +(27, 1, 'jack.romoli@zamum.bw', 'Jack', 'Romoli', 6, 4), +(28, 1, 'pearl.holden@dunebuh.cr', 'Pearl', 'Holden', 8, 1), +(29, 1, 'belle.montero@repiwid.si', 'Belle', 'Montero', 9, 5), +(30, 1, 'olive.molina@razuppa.ga', 'Olive', 'Molina', 6, 2), +(31, 1, 'minerva.todd@kulmenim.ad', 'Minerva', 'Todd', 9, 3), +(32, 1, 'bobby.pearson@ib.kg', 'Bobby', 'Pearson', 9, 1), +(33, 1, 'larry.ciappi@ba.lk', 'Larry', 'Ciappi', 10, 2), +(34, 1, 'ronnie.salucci@tohhij.lv', 'Ronnie', 'Salucci', 9, 1), +(35, 1, 'walter.grossi@tuvo.sa', 'Walter', 'Grossi', 9, 1); +INSERT INTO "CONTACT" (ID, VERSION, EMAIL, FIRST_NAME, LAST_NAME, COMPANY_ID, STATUS_ID) VALUES +(36, 1, 'frances.koopmans@foga.tw', 'Frances', 'Koopmans', 7, 5), +(37, 1, 'frances.fujimoto@uswuzzub.jp', 'Frances', 'Fujimoto', 6, 5), +(38, 1, 'olivia.vidal@hivwerip.vc', 'Olivia', 'Vidal', 9, 2), +(39, 1, 'edna.henry@gugusu.rw', 'Edna', 'Henry', 8, 4), +(40, 1, 'lydia.brun@zedekak.md', 'Lydia', 'Brun', 7, 3), +(41, 1, 'jay.blake@ral.mk', 'Jay', 'Blake', 10, 4), +(42, 1, 'isabel.serafini@turuhu.bh', 'Isabel', 'Serafini', 10, 1), +(43, 1, 'rebecca.carter@omjo.et', 'Rebecca', 'Carter', 8, 4), +(44, 1, 'maurice.fabbrini@rig.bh', 'Maurice', 'Fabbrini', 9, 3), +(45, 1, 'ollie.turnbull@sicewap.org', 'Ollie', 'Turnbull', 6, 1), +(46, 1, 'jerry.hopkins@fo.mh', 'Jerry', 'Hopkins', 9, 5), +(47, 1, 'nora.lyons@gegijap.na', 'Nora', 'Lyons', 10, 1), +(48, 1, 'anne.weis@kuvesa.pe', 'Anne', 'Weis', 7, 4), +(49, 1, 'louise.gauthier@lapahu.mt', 'Louise', 'Gauthier', 6, 2), +(50, 1, 'lloyd.fani@zev.ru', 'Lloyd', 'Fani', 8, 1), +(51, 1, 'maud.dunn@nabeaga.ni', 'Maud', 'Dunn', 6, 1), +(52, 1, 'henry.gigli@kaot.ps', 'Henry', 'Gigli', 6, 5), +(53, 1, 'virgie.werner@tawuctuj.cf', 'Virgie', 'Werner', 10, 4), +(54, 1, 'gregory.cozzi@eh.ru', 'Gregory', 'Cozzi', 8, 2), +(55, 1, 'lucinda.gil@fajjusuz.kr', 'Lucinda', 'Gil', 7, 5), +(56, 1, 'gertrude.verbeek@pave.cc', 'Gertrude', 'Verbeek', 6, 5), +(57, 1, 'mattie.graham@ispaviw.gt', 'Mattie', 'Graham', 7, 2), +(58, 1, 'bryan.shaw@ha.ee', 'Bryan', 'Shaw', 9, 1), +(59, 1, 'essie.adams@iliat.cw', 'Essie', 'Adams', 8, 5), +(60, 1, 'gary.osborne@do.ga', 'Gary', 'Osborne', 7, 5); \ No newline at end of file diff --git a/src/test/java/com/example/application/it/LoginE2ETest.java b/src/test/java/com/example/application/it/LoginE2ETest.java new file mode 100644 index 0000000..b6cc3a2 --- /dev/null +++ b/src/test/java/com/example/application/it/LoginE2ETest.java @@ -0,0 +1,46 @@ +package com.example.application.it; + +import com.example.application.it.elements.LoginViewElement; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.env.Environment; + +import com.vaadin.testbench.BrowserTest; +import com.vaadin.testbench.BrowserTestBase; +import com.vaadin.testbench.IPAddress; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +//@RunLocally(Browser.FIREFOX) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class LoginE2ETest extends BrowserTestBase { + + @Autowired + Environment environment; + + static { + // Prevent Vaadin Development mode to launch browser window + System.setProperty("vaadin.launch-browser", "false"); + } + + @BeforeEach + void openBrowser() { + getDriver().get("http://" + IPAddress.findSiteLocalAddress() + ":" + + environment.getProperty("local.server.port") + "/"); + } + + @BrowserTest + public void loginAsValidUserSucceeds() { + LoginViewElement loginView = $(LoginViewElement.class).onPage().first(); + assertTrue(loginView.login("user", "password")); + } + + @BrowserTest + public void loginAsInvalidUserFails() { + LoginViewElement loginView = $(LoginViewElement.class).onPage().first(); + assertFalse(loginView.login("user", "invalid")); + } + +} diff --git a/src/test/java/com/example/application/it/elements/LoginViewElement.java b/src/test/java/com/example/application/it/elements/LoginViewElement.java new file mode 100644 index 0000000..281d77c --- /dev/null +++ b/src/test/java/com/example/application/it/elements/LoginViewElement.java @@ -0,0 +1,32 @@ +package com.example.application.it.elements; + +import com.vaadin.flow.component.applayout.testbench.AppLayoutElement; +import com.vaadin.flow.component.login.testbench.LoginFormElement; +import com.vaadin.flow.component.orderedlayout.testbench.VerticalLayoutElement; +import com.vaadin.testbench.annotations.Attribute; +import org.openqa.selenium.By; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; + +@Attribute(name = "class", contains = "login-view") +public class LoginViewElement extends VerticalLayoutElement { + + public boolean login(String username, String password) { + LoginFormElement form = $(LoginFormElement.class).first(); + form.getUsernameField().setValue(username); + form.getPasswordField().setValue(password); + form.getSubmitButton().click(); + + // Return true if we end up on another page + try { + getDriver().manage().timeouts().implicitlyWait(Duration.of(1, ChronoUnit.SECONDS)); + getDriver().findElement(By.tagName("vaadin-app-layout")); + return true; + } catch (Exception e) { + return false; + } + } + +} \ No newline at end of file diff --git a/src/test/java/com/example/application/views/list/ContactFormTest.java b/src/test/java/com/example/application/views/list/ContactFormTest.java new file mode 100644 index 0000000..b7ac6c7 --- /dev/null +++ b/src/test/java/com/example/application/views/list/ContactFormTest.java @@ -0,0 +1,84 @@ +package com.example.application.views.list; + +import com.example.application.data.Company; +import com.example.application.data.Contact; +import com.example.application.data.Status; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ContactFormTest { + private List companies; + private List statuses; + private Contact marcUsher; + private Company company1; + private Company company2; + private Status status1; + private Status status2; + + @BeforeEach + public void setupData() { + companies = new ArrayList<>(); + company1 = new Company(); + company1.setName("Vaadin Ltd"); + company2 = new Company(); + company2.setName("IT Mill"); + companies.add(company1); + companies.add(company2); + + statuses = new ArrayList<>(); + status1 = new Status(); + status1.setName("Status 1"); + status2 = new Status(); + status2.setName("Status 2"); + statuses.add(status1); + statuses.add(status2); + + marcUsher = new Contact(); + marcUsher.setFirstName("Marc"); + marcUsher.setLastName("Usher"); + marcUsher.setEmail("marc@usher.com"); + marcUsher.setStatus(status1); + marcUsher.setCompany(company2); + } + + @Test + public void formFieldsPopulated() { + ContactForm form = new ContactForm(companies, statuses); + form.setContact(marcUsher); + assertEquals("Marc", form.firstName.getValue()); + assertEquals("Usher", form.lastName.getValue()); + assertEquals("marc@usher.com", form.email.getValue()); + assertEquals(company2, form.company.getValue()); + assertEquals(status1, form.status.getValue()); + } + + @Test + public void saveEventHasCorrectValues() { + ContactForm form = new ContactForm(companies, statuses); + Contact contact = new Contact(); + form.setContact(contact); + form.firstName.setValue("John"); + form.lastName.setValue("Doe"); + form.company.setValue(company1); + form.email.setValue("john@doe.com"); + form.status.setValue(status2); + + AtomicReference savedContactRef = new AtomicReference<>(null); + form.addSaveListener(e -> { + savedContactRef.set(e.getContact()); + }); + form.save.click(); + Contact savedContact = savedContactRef.get(); + + assertEquals("John", savedContact.getFirstName()); + assertEquals("Doe", savedContact.getLastName()); + assertEquals("john@doe.com", savedContact.getEmail()); + assertEquals(company1, savedContact.getCompany()); + assertEquals(status2, savedContact.getStatus()); + } +} diff --git a/src/test/java/com/example/application/views/list/ListViewTest.java b/src/test/java/com/example/application/views/list/ListViewTest.java new file mode 100644 index 0000000..86a7fc0 --- /dev/null +++ b/src/test/java/com/example/application/views/list/ListViewTest.java @@ -0,0 +1,41 @@ +package com.example.application.views.list; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.example.application.data.Contact; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.data.provider.ListDataProvider; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class ListViewTest { + + static { + // Prevent Vaadin Development mode to launch browser window + System.setProperty("vaadin.launch-browser", "false"); + } + + @Autowired + private ListView listView; + + @Test + public void formShownWhenContactSelected() { + Grid grid = listView.grid; + Contact firstContact = getFirstItem(grid); + + ContactForm form = listView.form; + + assertFalse(form.isVisible()); + grid.asSingleSelect().setValue(firstContact); + assertTrue(form.isVisible()); + assertEquals(firstContact.getFirstName(), form.firstName.getValue()); + } + + private Contact getFirstItem(Grid grid) { + return( (ListDataProvider) grid.getDataProvider()).getItems().iterator().next(); + } +}