commit bbe436c758dde1f2ccc8ae71cac7c46e1a2bf91f Author: FyloZ Date: Wed Aug 14 16:50:42 2019 -0400 Initialisation du repo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..153c933 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +HELP.md +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ + +### VS Code ### +.vscode/ diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..cb4749f --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,114 @@ +/* +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 + + https://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.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.util.Properties; + +public class MavenWrapperDownloader { + + /** + * 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/0.4.2/maven-wrapper-0.4.2.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 existsByName, 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 direcrory '" + 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 { + 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 0000000..01e6799 Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..cd0d451 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip diff --git a/TestProd.pdf b/TestProd.pdf new file mode 100644 index 0000000..7d99b11 Binary files /dev/null and b/TestProd.pdf differ diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..8b9da3b --- /dev/null +++ b/mvnw @@ -0,0 +1,286 @@ +#!/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 +# +# https://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 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)`" + # TODO classpath? +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 + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" + 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 command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + wget "$jarUrl" -O "$wrapperJarPath" + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + curl -o "$wrapperJarPath" "$jarUrl" + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + 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 + +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..fef5a8f --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,161 @@ +@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 https://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 Maven2 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 key stroke 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 my 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.4.2/maven-wrapper-0.4.2.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% ( + echo Found %WRAPPER_JAR% +) else ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" + echo Finished downloading %WRAPPER_JAR% +) +@REM End of extension + +%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..36f60e6 --- /dev/null +++ b/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.4.RELEASE + + + fyloz.trial + ColorRecipesExplorer + 0.0.1-SNAPSHOT + ColorRecipesExplorer + Demo project for Spring Boot + + + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + com.h2database + h2 + runtime + + + + commons-codec + commons-codec + 1.11 + + + + + com.itextpdf + itextpdf + 5.5.10 + + + org.apache.pdfbox + pdfbox + 2.0.4 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/recipes.mv.db b/recipes.mv.db new file mode 100644 index 0000000..6895fe6 Binary files /dev/null and b/recipes.mv.db differ diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/ColorRecipesExplorerApplication.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/ColorRecipesExplorerApplication.java new file mode 100644 index 0000000..3d6efd4 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/ColorRecipesExplorerApplication.java @@ -0,0 +1,57 @@ +package fyloz.trial.ColorRecipesExplorer; + +import fyloz.trial.ColorRecipesExplorer.core.io.FileHandler; +import fyloz.trial.ColorRecipesExplorer.web.StringBank; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; + +@SpringBootApplication +public class ColorRecipesExplorerApplication { + + public static final Logger logger = LoggerFactory.getLogger(ColorRecipesExplorerApplication.class); + // Chemin du fichier contenant les utilisateurs + public static final String USERS_FILE_NAME = "passwords"; + + public static void main(String[] args) { + logger.info("Le fichier des utilisateurs se situe à: " + new File(StringBank.UPLOAD_LOCATION + "/" + USERS_FILE_NAME).getAbsolutePath()); + loadPasswords(); + + SpringApplication.run(ColorRecipesExplorerApplication.class, args); + } + + /** + * Charge les mots de passes contenus dans le fichier. + *

+ * Un mot de passe correspond à une ligne dans le fichier passwords.txt. + * + * @throws IOException + */ + private static void loadPasswords() { + FileHandler fileHandler = new FileHandler(USERS_FILE_NAME, FileHandler.FileContext.OTHERS, FileHandler.FileExtension.TXT); + if (!fileHandler.isValid()) { + fileHandler.createFile(); + } + + try { + List fileContent = Files.readAllLines(fileHandler.getPath()); + + if (fileContent.size() < 1) { + logger.warn("Aucun mot de passe trouvé. Il sera impossible d'utiliser certaines fonctionnalitées de l'application."); + } + + for (String line : fileContent) { + PasswordValidator.addPassword(line); + } + } catch (IOException e) { + logger.error("Une erreur est survenue lors du chargement du fichier des utilisateurs", e); + logger.warn("Il sera impossible d'utiliser certaines fonctionnalitées de l'application."); + } + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/PasswordValidator.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/PasswordValidator.java new file mode 100644 index 0000000..7cad7f5 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/PasswordValidator.java @@ -0,0 +1,17 @@ +package fyloz.trial.ColorRecipesExplorer; + +import java.util.ArrayList; +import java.util.List; + +public class PasswordValidator { + + private static List passwords = new ArrayList<>(); + + public static boolean isValid(String password) { + return passwords.contains(password); + } + + public static void addPassword(String password) { + passwords.add(password); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/core/configuration/LoggingConfiguration.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/core/configuration/LoggingConfiguration.java new file mode 100644 index 0000000..1573517 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/core/configuration/LoggingConfiguration.java @@ -0,0 +1,37 @@ +package fyloz.trial.ColorRecipesExplorer.core.configuration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InjectionPoint; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.core.MethodParameter; + +import java.lang.reflect.Field; + +import static java.util.Optional.of; +import static java.util.Optional.ofNullable; + +@Configuration +public class LoggingConfiguration { + + /** + * Injecte un Logger dans les méthodes qui @Autowired un Logger + * + * @param ip Le point d'injection + * @return Le Logger à injecter. + */ + @Bean + @Scope("prototype") + public Logger logger(final InjectionPoint ip) { + return LoggerFactory.getLogger(of(ip.getMethodParameter()) + .map(MethodParameter::getContainingClass) + .orElseGet(() -> + ofNullable(ip.getField()) + .map(Field::getDeclaringClass) + .orElseThrow(IllegalArgumentException::new) + ) + ); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/core/io/FileHandler.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/core/io/FileHandler.java new file mode 100644 index 0000000..451075e --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/core/io/FileHandler.java @@ -0,0 +1,134 @@ +package fyloz.trial.ColorRecipesExplorer.core.io; + +import fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication; +import org.slf4j.Logger; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.UPLOAD_LOCATION; + +public class FileHandler { + + protected String name; + protected Logger logger = ColorRecipesExplorerApplication.logger; + + private FileContext context; + private FileExtension extension; + private File file; + + public FileHandler(String name, FileContext context, FileExtension extension) { + this.context = context; + this.extension = extension; + + setName(name); + file = getFile(); + } + + public boolean createFile() { + File parent = file.getParentFile(); + + if (!isValid()) { + if ((!parent.exists() || !parent.isDirectory()) && !parent.mkdirs()) { + return false; + } + + try { + logger.info("Création du fichier " + file.getAbsolutePath()); + return file.createNewFile(); + } catch (IOException e) { + logger.error("Erreur à la création d'un fichier", e); + return false; + } + } else { + logger.warn("Tentative de création du fichier existant " + file.getAbsolutePath()); + return false; + } + } + + public byte[] readFile() { + if (isValid()) { + try { + return Files.readAllBytes(getPath()); + } catch (IOException e) { + logger.error("Erreur lors de la lecture d'un fichier", e); + } + } + + logger.warn("Tentative de lecture du fichier invalide " + file.getAbsolutePath()); + return null; + } + + public boolean deleteFile() { + if (isValid()) { + logger.info("Suppression du fichier " + file.getAbsolutePath()); + return file.delete(); + } + + logger.warn("Tentative de suppression du fichier invalide " + file.getAbsolutePath()); + return false; + } + + public boolean isValid() { + return file.exists() && file.isFile(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + + file = getFile(); + } + + public FileContext getContext() { + return context; + } + + public Path getPath() { + return Paths.get(String.format("%s/%s/%s%s", UPLOAD_LOCATION, context.getPath(), name, extension.getExtension())); + } + + public File getFile() { + return getPath().toFile(); + } + + public enum FileContext { + IMAGE("images"), + SIMDUT("simdut"), + PDF("pdf"), + OTHERS(""); + + private String path; + + FileContext(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + } + + public enum FileExtension { + JPEG("jpeg"), + PDF("pdf"), + TXT("txt"), + DB("db"); + + private String extension; + + FileExtension(String extension) { + this.extension = extension; + } + + public String getExtension() { + return "." + extension; + } + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/core/io/ImageHandler.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/core/io/ImageHandler.java new file mode 100644 index 0000000..7e13c22 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/core/io/ImageHandler.java @@ -0,0 +1,49 @@ +package fyloz.trial.ColorRecipesExplorer.core.io; + +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import fyloz.trial.ColorRecipesExplorer.services.RecipeService; + +import java.util.List; +import java.util.stream.Collectors; + +public class ImageHandler extends FileHandler { + + private Recipe recipe; + private int index = 0; + private RecipeService recipeService; + + public ImageHandler(Recipe recipe, RecipeService recipeService) { + super(String.format("%s_%s", recipe.getRecipeID(), recipe.getRecipeCode()), FileContext.IMAGE, FileExtension.JPEG); + + this.recipe = recipe; + this.recipeService = recipeService; + } + + public ImageHandler(String name, RecipeService recipeService) { + super(name.replace(".jpeg", ""), FileContext.IMAGE, FileExtension.JPEG); + + String[] nameParts = name.split("-"); + index = Integer.parseInt(nameParts[1].replace(".jpeg", "")); + } + + @Override + public boolean createFile() { + if (recipe != null) { + List existingImages = recipeService.getImageFiles(recipe); + + if (existingImages != null) { + List existingImagesIndexes = existingImages.stream().map(n -> Integer.parseInt(n.replace(name + "-", "").replace(".jpeg", ""))).collect(Collectors.toList()); + + while (existingImagesIndexes.contains(index)) { + index++; + } + } + + setName(String.format("%s-%s", getName(), index)); + return super.createFile(); + } + + logger.warn("Tentative de créer une image sans recette"); + return false; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/CompanyDao.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/CompanyDao.java new file mode 100644 index 0000000..003e036 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/CompanyDao.java @@ -0,0 +1,13 @@ +package fyloz.trial.ColorRecipesExplorer.dao; + +import fyloz.trial.ColorRecipesExplorer.model.Company; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CompanyDao extends JpaRepository { + + void deleteByCompanyID(int companyID); + + Company findByCompanyName(String companyName); +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MaterialDao.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MaterialDao.java new file mode 100644 index 0000000..ce494dc --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MaterialDao.java @@ -0,0 +1,12 @@ +package fyloz.trial.ColorRecipesExplorer.dao; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MaterialDao extends JpaRepository { + Material findByMaterialID(int materialID); + + Material findByMaterialCode(String materialCode); +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MaterialTypeDao.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MaterialTypeDao.java new file mode 100644 index 0000000..dedbc81 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MaterialTypeDao.java @@ -0,0 +1,10 @@ +package fyloz.trial.ColorRecipesExplorer.dao; + +import fyloz.trial.ColorRecipesExplorer.model.MaterialType; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MaterialTypeDao extends JpaRepository { + + MaterialType findByMaterialTypeName(String name); + +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MixDao.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MixDao.java new file mode 100644 index 0000000..729df1b --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MixDao.java @@ -0,0 +1,16 @@ +package fyloz.trial.ColorRecipesExplorer.dao; + +import fyloz.trial.ColorRecipesExplorer.model.Mix; +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface MixDao extends JpaRepository { + + Mix findByMixID(int mixID); + + List findAllByRecipe(Recipe recipe); +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MixQuantityDao.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MixQuantityDao.java new file mode 100644 index 0000000..463a157 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MixQuantityDao.java @@ -0,0 +1,15 @@ +package fyloz.trial.ColorRecipesExplorer.dao; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.model.MixQuantity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface MixQuantityDao extends JpaRepository { + MixQuantity findByMixQuantityID(int mixQuantityID); + + List findAllByMaterial(Material material); +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MixTypeDao.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MixTypeDao.java new file mode 100644 index 0000000..fe7d839 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/MixTypeDao.java @@ -0,0 +1,15 @@ +package fyloz.trial.ColorRecipesExplorer.dao; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.model.MixType; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface MixTypeDao extends JpaRepository { + MixType findByTypeID(int id); + + MixType findByTypeName(String typeName); + + MixType findByMaterial(Material material); +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/RecipeDao.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/RecipeDao.java new file mode 100644 index 0000000..4c87f35 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/RecipeDao.java @@ -0,0 +1,18 @@ +package fyloz.trial.ColorRecipesExplorer.dao; + +import fyloz.trial.ColorRecipesExplorer.model.Company; +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface RecipeDao extends JpaRepository { + + List findAllByCompany(Company company); + + Recipe findByRecipeCode(String recipeCode); + + Recipe findByRecipeID(int recipeID); +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/StepDao.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/StepDao.java new file mode 100644 index 0000000..9d04a0a --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/dao/StepDao.java @@ -0,0 +1,15 @@ +package fyloz.trial.ColorRecipesExplorer.dao; + +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import fyloz.trial.ColorRecipesExplorer.model.RecipeStep; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface StepDao extends JpaRepository { + + RecipeStep findByStepID(int stepID); + + List findAllByRecipe(Recipe recipe); + +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/factory/files/PdfFactory.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/factory/files/PdfFactory.java new file mode 100644 index 0000000..17a40bd --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/factory/files/PdfFactory.java @@ -0,0 +1,44 @@ +package fyloz.trial.ColorRecipesExplorer.factory.files; + +import com.itextpdf.text.Document; +import com.itextpdf.text.DocumentException; +import com.itextpdf.text.pdf.PdfWriter; +import fyloz.trial.ColorRecipesExplorer.core.io.FileHandler; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + +public class PdfFactory { + + protected Document document; + + private String fileName; + private FileHandler fileHandler; + + public PdfFactory(String fileName) { + this(fileName, FileHandler.FileContext.OTHERS); + } + + public PdfFactory(String fileName, FileHandler.FileContext context) { + this.fileName = fileName; + + fileHandler = new FileHandler(fileName, context, FileHandler.FileExtension.PDF); + } + + public PdfFactory open() throws FileNotFoundException, DocumentException { + document = new Document(); + PdfWriter.getInstance(document, new FileOutputStream(fileHandler.getPath().toString())); + + document.open(); + + return this; + } + + public void write() { + document.close(); + } + + public FileHandler getFileHandler() { + return fileHandler; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/factory/files/TouchUpKitPdfFactory.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/factory/files/TouchUpKitPdfFactory.java new file mode 100644 index 0000000..ba7bff0 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/factory/files/TouchUpKitPdfFactory.java @@ -0,0 +1,59 @@ +package fyloz.trial.ColorRecipesExplorer.factory.files; + +import com.itextpdf.text.*; +import com.itextpdf.text.pdf.PdfWriter; +import fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class TouchUpKitPdfFactory { + + private String kitName; + private ByteArrayOutputStream pdfContent; + private Document document; + + public TouchUpKitPdfFactory(String kitName) { + this.kitName = kitName.toUpperCase(); + + pdfContent = new ByteArrayOutputStream(); + document = new Document(); + } + + public TouchUpKitPdfFactory build() throws DocumentException { + ColorRecipesExplorerApplication.logger.info(String.format("Génération d'un PDF pour le kit de retouche '%s'", kitName)); + + PdfWriter.getInstance(document, pdfContent); + + Font descFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD, 48, BaseColor.BLACK); + Font kitNameFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD, kitName.length() > 22 ? 32 : 38, BaseColor.BLACK); + + Paragraph descFr = new Paragraph("KIT DE RETOUCHE", descFont); + Paragraph descEn = new Paragraph("TOUCH UP KIT", descFont); + Paragraph touchUpKitName = new Paragraph(kitName, kitNameFont); + + descFr.setAlignment(Element.ALIGN_CENTER); + descEn.setAlignment(Element.ALIGN_CENTER); + touchUpKitName.setAlignment(Element.ALIGN_CENTER); + + document.open(); + document.add(descFr); + document.add(descEn); + document.add(touchUpKitName); + document.close(); + + return this; + } + + public byte[] toByteArray() { + byte[] content = pdfContent.toByteArray(); + + try { + pdfContent.close(); + } catch (IOException e) { + ColorRecipesExplorerApplication.logger.error("Une erreur est survenue lors de la fermeture d'un ByteArrayOutputStream", e); + } + + return content; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/BeanModel.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/BeanModel.java new file mode 100644 index 0000000..f374df9 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/BeanModel.java @@ -0,0 +1,5 @@ +package fyloz.trial.ColorRecipesExplorer.model; + +public abstract class BeanModel { + public abstract Integer getID(); +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Company.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Company.java new file mode 100644 index 0000000..d0261b8 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Company.java @@ -0,0 +1,68 @@ +package fyloz.trial.ColorRecipesExplorer.model; + +import org.hibernate.validator.constraints.Length; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; + +@Entity +@Table(name = "companies") +public class Company extends BeanModel implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private int companyID; + + @Length(min = 2, max = 50) + @Column(unique = true) + @NotNull + private String companyName; + + public Company() { + + } + + public Company(@Length(min = 2, max = 50) @NotNull String companyName) { + this.companyName = companyName; + } + + public int getCompanyID() { + return companyID; + } + + public void setCompanyID(int companyID) { + this.companyID = companyID; + } + + public String getCompanyName() { + return companyName; + } + + public void setCompanyName(String companyName) { + this.companyName = companyName; + } + + @Override + public Integer getID() { + return companyID; + } + + @Override + public String toString() { + return "Company{" + + "companyID=" + companyID + + ", companyName='" + companyName + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Company company = (Company) o; + return companyID == company.companyID && + Objects.equals(companyName, company.companyName); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Material.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Material.java new file mode 100644 index 0000000..691c1df --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Material.java @@ -0,0 +1,110 @@ +package fyloz.trial.ColorRecipesExplorer.model; + +import org.hibernate.annotations.ColumnDefault; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; + +@Entity +@Table(name = "materials") +public class Material extends BeanModel implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private Integer materialID = 0; + + @NotNull + private String materialCode; + + @NotNull + private float inventoryQuantity = 0; + + @NotNull + @ColumnDefault("false") + private boolean isMixType = false; + + @ManyToOne + @NotNull + private MaterialType materialType; + + public Material() { + + } + + public Material(@NotNull String materialCode, @NotNull float inventoryQuantity, @NotNull boolean isMixType, MaterialType materialType) { + this.materialCode = materialCode; + this.inventoryQuantity = inventoryQuantity; + this.isMixType = isMixType; + this.materialType = materialType; + } + + public Integer getMaterialID() { + return materialID; + } + + public void setMaterialID(Integer materialID) { + this.materialID = materialID; + } + + public String getMaterialCode() { + return materialCode; + } + + public void setMaterialCode(String materialCode) { + this.materialCode = materialCode; + } + + public float getInventoryQuantity() { + return inventoryQuantity; + } + + public void setInventoryQuantity(float inventoryQuantity) { + this.inventoryQuantity = inventoryQuantity; + } + + public boolean isMixType() { + return isMixType; + } + + public void setMixType(boolean mixType) { + isMixType = mixType; + } + + public MaterialType getMaterialType() { + return materialType; + } + + public void setMaterialType(MaterialType materialType) { + this.materialType = materialType; + } + + @Override + public Integer getID() { + return materialID; + } + + @Override + public String toString() { + return "Material{" + + "materialID=" + materialID + + ", materialCode='" + materialCode + '\'' + + ", inventoryQuantity=" + inventoryQuantity + + ", isMixType=" + isMixType + + ", materialType=" + materialType + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Material material = (Material) o; + return Float.compare(material.inventoryQuantity, inventoryQuantity) == 0 && + isMixType == material.isMixType && + Objects.equals(materialID, material.materialID) && + Objects.equals(materialCode, material.materialCode) && + Objects.equals(materialType, material.materialType); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/MaterialType.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/MaterialType.java new file mode 100644 index 0000000..2faae83 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/MaterialType.java @@ -0,0 +1,104 @@ +package fyloz.trial.ColorRecipesExplorer.model; + +import org.hibernate.annotations.ColumnDefault; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +@Entity +public class MaterialType extends BeanModel implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private Integer materialTypeID; + + @Column(unique = true) + @NotNull + private String materialTypeName; + + @NotNull + private String prefix; + + @NotNull + @ColumnDefault("false") + private Boolean usePercentages = false; + + @OneToMany + @JoinColumn(name = "material_type") + private List materials; + + public MaterialType() { + + } + + public MaterialType(@NotNull String materialTypeName, @NotNull String prefix, @NotNull Boolean usePercentages) { + this.materialTypeName = materialTypeName; + this.prefix = prefix.toUpperCase(); + this.usePercentages = usePercentages; + } + + public Integer getMaterialTypeID() { + return materialTypeID; + } + + public void setMaterialTypeID(Integer materialTypeID) { + this.materialTypeID = materialTypeID; + } + + public String getMaterialTypeName() { + return materialTypeName; + } + + public void setMaterialTypeName(String materialTypeName) { + this.materialTypeName = materialTypeName; + } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix.toUpperCase(); + } + + public Boolean getUsePercentages() { + return usePercentages; + } + + public void setUsePercentages(Boolean usePercentages) { + this.usePercentages = usePercentages; + } + + public List getMaterials() { + return materials; + } + + @Override + public Integer getID() { + return materialTypeID; + } + + @Override + public String toString() { + return "MaterialType{" + + "materialTypeID=" + materialTypeID + + ", materialTypeName='" + materialTypeName + '\'' + + ", prefix='" + prefix + '\'' + + ", usePercentages=" + usePercentages + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MaterialType that = (MaterialType) o; + return Objects.equals(materialTypeID, that.materialTypeID) && + Objects.equals(materialTypeName, that.materialTypeName) && + Objects.equals(prefix, that.prefix) && + Objects.equals(usePercentages, that.usePercentages); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Mix.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Mix.java new file mode 100644 index 0000000..febddbe --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Mix.java @@ -0,0 +1,107 @@ +package fyloz.trial.ColorRecipesExplorer.model; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +@Entity +@Table(name = "mixes") +public class Mix extends BeanModel implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @Basic + private Integer mixID = 0; + + @NotNull + @ManyToOne + private Recipe recipe; + + @NotNull + @ManyToOne + private MixType mixType; + + @OneToMany(mappedBy = "mix") + private List mixQuantities; + + // Casier + private String location; + + public Mix() { + + } + + public Mix(@NotNull Recipe recipe, @NotNull MixType mixType, List mixQuantities) { + this.recipe = recipe; + this.mixType = mixType; + this.mixQuantities = mixQuantities; + } + + public Recipe getRecipe() { + return recipe; + } + + public void setRecipe(Recipe recipe) { + this.recipe = recipe; + } + + public Integer getMixID() { + return mixID; + } + + public void setMixID(Integer mixID) { + this.mixID = mixID; + } + + public MixType getMixType() { + return mixType; + } + + public void setMixType(MixType mixType) { + this.mixType = mixType; + } + + public List getMixQuantities() { + return mixQuantities; + } + + public void setMixQuantities(List mixQuantities) { + this.mixQuantities = mixQuantities; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + @Override + public Integer getID() { + return mixID; + } + + @Override + public String toString() { + return "Mix{" + + "mixID=" + mixID + +// ", recipe=" + recipe + + ", mixType=" + mixType + +// ", mixQuantities=" + mixQuantities + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Mix mix = (Mix) o; + return mixID.equals(mix.mixID) && + Objects.equals(recipe, mix.recipe) && + Objects.equals(mixType, mix.mixType) && + Objects.equals(mixQuantities, mix.mixQuantities); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/MixQuantity.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/MixQuantity.java new file mode 100644 index 0000000..1d3d80c --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/MixQuantity.java @@ -0,0 +1,96 @@ +package fyloz.trial.ColorRecipesExplorer.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; + +@Entity +@Table(name = "mixQuantities") +public class MixQuantity extends BeanModel implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @Basic + private Integer mixQuantityID = 0; + + @JsonIgnore + @ManyToOne + private Mix mix; + + @ManyToOne + private Material material; + + @NotNull + private Float quantity; + + public MixQuantity() { + + } + + public MixQuantity(Mix mix, Material material, @NotNull Float quantity) { + this.mix = mix; + this.material = material; + this.quantity = quantity; + } + + public Integer getMixQuantityID() { + return mixQuantityID; + } + + public void setMixQuantityID(Integer mixQuantityID) { + this.mixQuantityID = mixQuantityID; + } + + public Mix getMix() { + return mix; + } + + public void setMix(Mix mix) { + this.mix = mix; + } + + public Material getMaterial() { + return material; + } + + public void setMaterial(Material material) { + this.material = material; + } + + public Float getQuantity() { + return quantity; + } + + public void setQuantity(Float quantity) { + this.quantity = quantity; + } + + @Override + public Integer getID() { + return mixQuantityID; + } + + @Override + public String toString() { + return "MixQuantity{" + + "mixQuantityID=" + mixQuantityID + + ", mix=" + mix + + ", material=" + material + + ", quantity=" + quantity + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MixQuantity that = (MixQuantity) o; + return mixQuantityID.equals(that.mixQuantityID) && + quantity == that.quantity && + Objects.equals(mix, that.mix) && + Objects.equals(material, that.material); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/MixType.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/MixType.java new file mode 100644 index 0000000..6caf2db --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/MixType.java @@ -0,0 +1,79 @@ +package fyloz.trial.ColorRecipesExplorer.model; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; + +@Entity +@Table(name = "mixTypes") +public class MixType extends BeanModel implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private int typeID; + + @NotNull + @Column(unique = true) + private String typeName; + + @NotNull + @OneToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "materialid") + private Material material; + + public MixType() { + + } + + public MixType(@NotNull String typeName, @NotNull Material material) { + this.typeName = typeName; + this.material = material; + } + + public int getTypeID() { + return typeID; + } + + public void setTypeID(int typeID) { + this.typeID = typeID; + } + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public Material getMaterial() { + return material; + } + + public void setMaterial(Material material) { + this.material = material; + } + + @Override + public Integer getID() { + return typeID; + } + + @Override + public String toString() { + return "MixType{" + + "typeID=" + typeID + + ", typeName='" + typeName + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MixType mixType = (MixType) o; + return typeID == mixType.typeID && + Objects.equals(typeName, mixType.typeName); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Recipe.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Recipe.java new file mode 100644 index 0000000..3e45aff --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/Recipe.java @@ -0,0 +1,176 @@ +package fyloz.trial.ColorRecipesExplorer.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.hibernate.validator.constraints.Length; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +@Entity +@Table(name = "recipes") +public class Recipe extends BeanModel implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private Integer recipeID = 0; + + @Length(min = 2) + @NotNull + private String recipeCode; + + @ManyToOne + @NotNull + private Company company; + + @NotNull + private String recipeDescription; + + @NotNull + private int sample; + + private String approbationDate; + + private String remark; + + private String note; + + @JsonIgnore + @OneToMany(mappedBy = "recipe") + private List recipeMixes; + + @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL) + private List recipeSteps; + + public Recipe() { + + } + + public Recipe(@Length(min = 2) String recipeCode, @NotNull Company company, String recipeDescription, @NotNull int sample, @NotNull String approbationDate, String remark, String note) { + this.recipeCode = recipeCode; + this.company = company; + this.recipeDescription = recipeDescription; + this.sample = sample; + this.approbationDate = approbationDate; + this.remark = remark; + this.note = note; + } + + public Integer getRecipeID() { + return recipeID; + } + + public void setRecipeID(Integer recipeID) { + this.recipeID = recipeID; + } + + public String getRecipeCode() { + return recipeCode; + } + + public void setRecipeCode(String recipeCode) { + this.recipeCode = recipeCode; + } + + public Company getCompany() { + return company; + } + + public void setCompany(Company company) { + this.company = company; + } + + public String getRecipeDescription() { + return recipeDescription; + } + + public void setRecipeDescription(String recipeDescription) { + this.recipeDescription = recipeDescription; + } + + public int getSample() { + return sample; + } + + public void setSample(int sample) { + this.sample = sample; + } + + public String getApprobationDate() { + return approbationDate; + } + + public void setApprobationDate(String approbationDate) { + this.approbationDate = approbationDate; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public List getRecipeMixes() { + return recipeMixes; + } + + public void setRecipeMixes(List recipeMixes) { + this.recipeMixes = recipeMixes; + } + + public List getRecipeSteps() { + return recipeSteps; + } + + public void setRecipeSteps(List recipeSteps) { + this.recipeSteps = recipeSteps; + } + + @Override + public Integer getID() { + return recipeID; + } + + @Override + public String toString() { + return "Recipe{" + + "recipeID=" + recipeID + + ", recipeCode=" + recipeCode + + ", company=" + company + + ", recipeDescription='" + recipeDescription + + ", sample=" + sample + + ", approbationDate=" + approbationDate + + ", remark='" + remark + + ", note='" + note + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Recipe recipe = (Recipe) o; + return recipeID.equals(recipe.recipeID) && + recipeCode.equals(recipe.recipeCode) && + sample == recipe.sample && + Objects.equals(company, recipe.company) && + Objects.equals(recipeDescription, recipe.recipeDescription) && + Objects.equals(approbationDate, recipe.approbationDate) && + Objects.equals(remark, recipe.remark) && + Objects.equals(note, recipe.note) && + Objects.equals(recipeMixes, recipe.recipeMixes) && + Objects.equals(recipeSteps, recipe.recipeSteps); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/RecipeStep.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/RecipeStep.java new file mode 100644 index 0000000..9da6426 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/RecipeStep.java @@ -0,0 +1,80 @@ +package fyloz.trial.ColorRecipesExplorer.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Objects; + +@Entity +@Table(name = "steps") +public class RecipeStep extends BeanModel implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + private int stepID; + + @ManyToOne + @JsonIgnore + private Recipe recipe; + + @NotNull + private String stepMessage; + + public RecipeStep() { + + } + + public RecipeStep(Recipe recipe, @NotNull String stepMessage) { + this.recipe = recipe; + this.stepMessage = stepMessage; + } + + public int getStepID() { + return stepID; + } + + public void setStepID(int stepID) { + this.stepID = stepID; + } + + public String getStepMessage() { + return stepMessage; + } + + public void setStepMessage(String stepMessage) { + this.stepMessage = stepMessage; + } + + public Recipe getRecipe() { + return recipe; + } + + public void setRecipe(Recipe recipe) { + this.recipe = recipe; + } + + @Override + public Integer getID() { + return stepID; + } + + @Override + public String toString() { + return "RecipeStep{" + + "stepID=" + stepID + + ", stepMessage='" + stepMessage + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RecipeStep recipeStep = (RecipeStep) o; + return stepID == recipeStep.stepID && + Objects.equals(stepMessage, recipeStep.stepMessage) && + Objects.equals(recipe, recipeStep.recipe); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/model/form/MaterialForm.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/form/MaterialForm.java new file mode 100644 index 0000000..ed4ceb6 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/model/form/MaterialForm.java @@ -0,0 +1,12 @@ +package fyloz.trial.ColorRecipesExplorer.model.form; + +import org.springframework.web.multipart.MultipartFile; + +public class MaterialForm { + + private String materialCode; + + private int inventoryQuantity; + + private MultipartFile file; +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/CompanyService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/CompanyService.java new file mode 100644 index 0000000..97555ca --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/CompanyService.java @@ -0,0 +1,37 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.dao.CompanyDao; +import fyloz.trial.ColorRecipesExplorer.dao.RecipeDao; +import fyloz.trial.ColorRecipesExplorer.model.Company; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CompanyService extends GenericService { + + private RecipeDao recipeDao; // Utilise le Dao pour éviter une erreur au démarrage, citant une récursion (CompanyService -> RecipeService -> CompanyService -> ...) + + @Autowired + public CompanyService(CompanyDao companyDao, RecipeDao recipeDao) { + super(companyDao); + this.recipeDao = recipeDao; + } + + /** + * Vérifie si une bannière est liée à une ou plusieurs recettes en faisant une requête dans la base de données, + * pour retourner s'il y a des résultats ou non. + * + * @return Si la bannière est liée à une ou plusieurs recettes. + */ + public boolean isLinkedToRecipes(Company company) { + return !recipeDao.findAllByCompany(company).isEmpty(); + } + + public boolean deleteIfNotLinked(Company company) { + if (!isLinkedToRecipes(company)) { + return super.delete(company); + } + + return false; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/GenericService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/GenericService.java new file mode 100644 index 0000000..0ef707b --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/GenericService.java @@ -0,0 +1,119 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication; +import fyloz.trial.ColorRecipesExplorer.model.BeanModel; +import org.slf4j.Logger; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public abstract class GenericService implements IGenericService { + + protected Logger logger = ColorRecipesExplorerApplication.logger; + + protected JpaRepository dao; + + public GenericService(JpaRepository dao) { + this.dao = dao; + } + + @Override + public T getByID(int id) { + return dao.findById(id).get(); + } + + @Override + public List getAll() { + return dao.findAll(); + } + + @Override + public T save(T entity) { + if (isValidForCreation(entity)) { + return dao.save(entity); + } + + return null; + } + + @Override + public boolean saveAll(List entities) { + for (T e : entities) { + if (save(e) == null) { + return false; + } + } + + return true; + } + + @Override + public T update(T entity) { + if (exists(entity)) { + return dao.save(entity); + } + + return null; + } + + @Override + public boolean delete(T entity) { + if (exists(entity)) { + dao.delete(entity); + return true; + } + + return false; + } + + @Override + public boolean deleteAll(List entities) { + for (T e : entities) { + if (!delete(e)) { + return false; + } + } + + return true; + } + + /** + * Vérifie si une entité est valide pour la création dans la base de données. + * + * @param entity L'entité à vérifier. + * @return Si l'entité est valide pour la création. + */ + public boolean isValidForCreation(T entity) { + return entity != null && !exists(entity); + } + + /** + * Vérifie si une entité existe dans la base de données. + * + * @param entity L'entité à vérifier + * @return Si l'entité existe. + */ + @Override + public boolean exists(T entity) { + + return dao.findById(entity.getID()).isPresent(); + } + + /** + * Transforme un objet en Json. + * + * @param obj L'objet à transformer + * @return L'objet sous forme de Json. + */ + public String asJson(Object obj) { + ObjectMapper objectMapper = new ObjectMapper(); + try { + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + logger.error("Une erreur est survenue lors de la transformation d'un objet en Json", e); + return null; + } + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/IGenericService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/IGenericService.java new file mode 100644 index 0000000..7dbae0f --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/IGenericService.java @@ -0,0 +1,78 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.model.BeanModel; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +public interface IGenericService { + + /** + * Récupère toutes les entités du même type dans la base de données. + * + * @return Une liste de toutes les entités du même type. + */ + List getAll(); + + /** + * Récupère l'entité correspondant à l'identifiant spécifié dans la base de données. + * + * @param id L'identifiant de l'entité + * @return L'entité correspondant à l'identifiant. + */ + T getByID(int id); + + /** + * Sauvegarde une entité dans la base de données. + *

+ * La sauvegarde sera exécutée si l'entité est valide pour la création. + * + * @param entity L'entité à sauvegarder + * @return Si la sauvegarde a fonctionné. + */ + T save(T entity); + + /** + * Sauvegarde des entités dans la base de données. + *

+ * Les sauvegardes seront exécutée si toutes les entités sont valides pour la création. + * + * @param entities La liste d'entités à sauvegarder + * @return Si la sauvegarde a fonctionné. + */ + @Transactional + boolean saveAll(List entities); + + /** + * Met à jour une entité dans la base de données. + *

+ * La mise à jour sera exécutée si l'entité existe dans la BDD. + * + * @param entity L'entité à mettre à jour + * @return Si la mise à jour a fonctionné. + */ + T update(T entity); + + /** + * Supprime une entité dans la base de données. + *

+ * La suppression sera exécutée si l'entité existe dans la BDD. + * + * @param entity L'entité à supprimer + * @return Si la suppression a fonctionné. + */ + boolean delete(T entity); + + /** + * Supprime des entités dans la base de données. + *

+ * Les suppressions seront exécutée si toutes les entités existes dans la BDD. + * + * @param entities La liste d'entités à supprimer + * @return Si la suppression a fonctionné. + */ + @Transactional + boolean deleteAll(List entities); + + boolean exists(T entity); +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/InventoryService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/InventoryService.java new file mode 100644 index 0000000..4c094de --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/InventoryService.java @@ -0,0 +1,46 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.model.Mix; +import fyloz.trial.ColorRecipesExplorer.model.MixQuantity; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class InventoryService { + + private MaterialService materialService; + + public InventoryService(MaterialService materialService) { + this.materialService = materialService; + } + + public String checkQuantities(Mix mix, Map quantities) { + for (Material material : mix.getMixQuantities().stream().map(MixQuantity::getMaterial).collect(Collectors.toList())) { + if (!material.isMixType()) { + float quantity = quantities.get(material); + + if (quantity > material.getInventoryQuantity()) { + return String.format("%s-%s", mix.getMixID(), material.getMaterialID()); + } + } + } + + return null; + } + + public boolean useMix(Mix mix, Map quantities) { + for (Material material : mix.getMixQuantities().stream().map(MixQuantity::getMaterial).collect(Collectors.toList())) { + if (!material.isMixType()) { + float quantity = quantities.get(material); + + material.setInventoryQuantity(material.getInventoryQuantity() - quantity); + if (materialService.update(material) == null) return false; + } + } + + return true; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MaterialService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MaterialService.java new file mode 100644 index 0000000..c9318c5 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MaterialService.java @@ -0,0 +1,98 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.core.io.FileHandler; +import fyloz.trial.ColorRecipesExplorer.dao.MaterialDao; +import fyloz.trial.ColorRecipesExplorer.model.Material; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; + +@Service +public class MaterialService extends GenericService { + + private MixQuantityService mixQuantityService; + + @Autowired + public MaterialService(MaterialDao materialDao, MixQuantityService mixQuantityService) { + super(materialDao); + this.mixQuantityService = mixQuantityService; + } + + /** + * Vérifie si un produit est lié à un ou plusieurs mélanges. + * + * @param material Le produit à vérifier. + * @return Si le produit est lié à d'autres mélanges. + */ + public boolean isLinkedToMixes(Material material) { + return !mixQuantityService.getByMaterial(material).isEmpty(); + } + + public boolean deleteIfNotLinked(Material material) { + if (!isLinkedToMixes(material)) { + return super.delete(material); + } + + return false; + } + + /** + * Crée le fichier SIMDUT sur le disque et y transfert le contenu du MultipartFile passé en paramètre. + *

+ * Cette méthode retournera true si le simdut est null ou si le transfert du fichier vers le disque a fonctionné. + * Cette méthode retournera false si la création du fichier échoue ou si une erreur survient lors du transfert. + * + * @param simdut Le contenu du fichier SIMDUT + * @param material Le produit du SIMDUT + * @return Si le fichier SIMDUT à bien été créé ou non. + */ + public boolean addSimdut(MultipartFile simdut, Material material) { + if (simdut.getSize() <= 0) return true; + + FileHandler fileHandler = getFilesServiceForMaterial(material); + + if (!fileHandler.createFile()) { + return false; + } + + try { + simdut.transferTo(new File(fileHandler.getFile().getAbsolutePath())); + return true; + } catch (IOException e) { + logger.error("Erreur lors du transfert d'un fichier SIMDUT vers le disque", e); + return false; + } + } + + /** + * Supprime le fichier SIMDUT pour un produit. + *

+ * Cette méthode retournera true si le fichier SIMDUT du produit n'existe pas ou si la suppression est faîtes. + * + * @param material Le produit dont on veut supprimer le fichier SIMDUT + * @return Si la suppression du fichier SIMDUT s'est effectuée. + */ + public boolean removeSimdut(Material material) { + FileHandler fileHandler = getFilesServiceForMaterial(material); + + if (fileHandler.getFile().exists()) { + return fileHandler.deleteFile(); + } + + return true; + } + + /** + * Crée un FileHandler pour le produit passé en paramètre. + * + * @param material Le produit dont on veut créer un FileHandler + * @return Le FileHandler correspondant au produit. + */ + private FileHandler getFilesServiceForMaterial(Material material) { + String filename = String.format("%s_%s", material.getMaterialID(), material.getMaterialCode()); + return new FileHandler(filename, FileHandler.FileContext.SIMDUT, FileHandler.FileExtension.PDF); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MaterialTypeService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MaterialTypeService.java new file mode 100644 index 0000000..ad627cb --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MaterialTypeService.java @@ -0,0 +1,27 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.dao.MaterialTypeDao; +import fyloz.trial.ColorRecipesExplorer.model.MaterialType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class MaterialTypeService extends GenericService { + + private MaterialTypeDao materialTypeDao; + + @Autowired + public MaterialTypeService(MaterialTypeDao materialTypeDao) { + super(materialTypeDao); + this.materialTypeDao = materialTypeDao; + } + + @Override + public boolean isValidForCreation(MaterialType entity) { + return entity != null && !existsByName(entity); + } + + public boolean existsByName(MaterialType entity) { + return materialTypeDao.findByMaterialTypeName(entity.getMaterialTypeName()) != null; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MixQuantityService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MixQuantityService.java new file mode 100644 index 0000000..ca69086 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MixQuantityService.java @@ -0,0 +1,31 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.dao.MixQuantityDao; +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.model.MixQuantity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class MixQuantityService extends GenericService { + + private MixQuantityDao mixQuantityDao; + + @Autowired + public MixQuantityService(MixQuantityDao mixQuantityDao) { + super(mixQuantityDao); + this.mixQuantityDao = mixQuantityDao; + } + + /** + * Récupère la liste des MixQuantity liées à un produit. + * + * @param material Le produit + * @return La page à afficher. + */ + public List getByMaterial(Material material) { + return mixQuantityDao.findAllByMaterial(material); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MixService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MixService.java new file mode 100644 index 0000000..09f93b5 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MixService.java @@ -0,0 +1,113 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.dao.MixDao; +import fyloz.trial.ColorRecipesExplorer.model.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.ERROR_SAVING; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.MATERIAL_NOT_FOUND; + +@Service +public class MixService extends GenericService { + + private MaterialService materialService; + private MixQuantityService mixQuantityService; + + @Autowired + public MixService(MixDao mixDao, MaterialService materialService, MixQuantityService mixQuantityService) { + super(mixDao); + this.materialService = materialService; + this.mixQuantityService = mixQuantityService; + } + + /** + * Crée un mélange, ainsi que tous ses sous-éléments. + *

+ * materials & quantities doivent avoir le même ordre. + * + * @param materials La liste des identifiants des matériaux + * @param quantities La liste des quantités de matériaux + * @param recipe La recette associée au mélange + * @param mixType Le type du mélange + * @return Le message d'erreur, s'il y a lieu + */ + @Transactional + public String create(List materials, List quantities, Recipe recipe, MixType mixType) { + // Crée le mélange en premier pour avoir accès à son ID pour les autres éléments + Mix mix = new Mix(recipe, mixType, null); + + if ((mix = save(mix)) != null) { + List mixQuantities = createMixQuantities(mix, materials, quantities); + if (mixQuantities == null) return MATERIAL_NOT_FOUND; + + if (mixQuantityService.saveAll(mixQuantities)) return null; + } + + return ERROR_SAVING; + } + + /** + * Modifie un mélange, ainsi que ses sous-éléments. + *

+ * materials & quantities doivent avoir le même ordre. + * + * @param mix Le mélange à modifier + * @param materials La liste des identifiants des matériaux + * @param quantities La liste des quantités de matériaux + * @return Si le mélange a été modifier. + */ + public boolean edit(Mix mix, List materials, List quantities) { + List mixQuantities = createMixQuantities(mix, materials, quantities); + if (mixQuantities == null) return false; + + // Supprime les anciens MixQuantity pour éviter les doublons et les entrées inutiles dans la base de données + if (!mixQuantityService.deleteAll(mix.getMixQuantities())) { + return false; + } + + return mixQuantityService.saveAll(mixQuantities); + } + + public boolean deleteMix(Mix mix) { + if (mixQuantityService.deleteAll(mix.getMixQuantities())) { + return super.delete(mix); + } + + return false; + } + + /** + * Crée une liste de MixQuantity, pour la création d'un mélange + * + * @param mix Le mélange associé aux MixQuantity + * @param materials Les matériaux + * @param quantities Les quantités + * @return La liste des MixQuantity créés. + */ + private List createMixQuantities(Mix mix, List materials, List quantities) { + List mixQuantities = new ArrayList<>(); + + int i = 0; + for (int materialID : materials) { + Material material = materialService.getByID(materialID); + + if (material == null) { + return null; + } + + float associatedQuantity = quantities.get(i); + + MixQuantity quantity = new MixQuantity(mix, material, associatedQuantity); + mixQuantities.add(quantity); + + i++; + } + + return mixQuantities; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MixTypeService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MixTypeService.java new file mode 100644 index 0000000..d89147e --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/MixTypeService.java @@ -0,0 +1,39 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.dao.MixTypeDao; +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.model.MixType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class MixTypeService extends GenericService { + + private MixTypeDao mixTypeDao; + + @Autowired + public MixTypeService(MixTypeDao mixTypeDao) { + super(mixTypeDao); + this.mixTypeDao = mixTypeDao; + } + + public MixType getByName(String name) { + return mixTypeDao.findByTypeName(name); + } + + public MixType getByMaterial(Material material) { + return mixTypeDao.findByMaterial(material); + } + + public MixType createByName(String name) { + Material mixTypeMaterial = new Material(name, 0, true, null); + MixType mixType = new MixType(name, mixTypeMaterial); + + mixType = save(mixType); + if (mixType == null) { + return getByName(name); + } + + return mixType; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/RecipeService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/RecipeService.java new file mode 100644 index 0000000..c2209a4 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/RecipeService.java @@ -0,0 +1,145 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.core.io.FileHandler; +import fyloz.trial.ColorRecipesExplorer.dao.RecipeDao; +import fyloz.trial.ColorRecipesExplorer.model.*; +import fyloz.trial.ColorRecipesExplorer.web.StringBank; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.MultiValueMap; + +import java.io.File; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class RecipeService extends GenericService { + + private RecipeDao recipeDao; + private CompanyService companyService; + private StepService stepService; + private MixService mixService; + + @Autowired + public RecipeService(RecipeDao recipeDao, CompanyService companyService, StepService stepService, MixService mixService) { + super(recipeDao); + this.recipeDao = recipeDao; + this.companyService = companyService; + this.stepService = stepService; + this.mixService = mixService; + } + + /** + * Récupère la liste des recettes associées à une compagnie. + * + * @param company La compagnie. + * @return La liste des recettes associées à cette compagnie. + */ + public List getByCompany(Company company) { + return recipeDao.findAllByCompany(company); + } + + /** + * Crée et assigne des étapes à une recette, en se basant sur l'entrée de utilisateur. + * + * @param recipe La recette à assigner les étapes + * @param input L'entrée de l'utilisateur, contenant les étapes (key = "step_*") + * @return La recette avec les étapes. + */ + public Recipe createAndSetSteps(Recipe recipe, MultiValueMap input) { + List steps = new ArrayList<>(); + for (String key : input.keySet()) { + Object field = input.get(key); + if (key.startsWith("step_") && field instanceof LinkedList) { + steps.add(new RecipeStep(recipe, ((LinkedList) field).get(0).toString())); + } + } + + List recipeSteps = recipe.getRecipeSteps(); + if (recipeSteps != null) { + stepService.deleteAll(recipeSteps); + } + recipe.setRecipeSteps(steps); + save(recipe); + + return recipe; + } + + /** + * Supprime complètement une recette ainsi que les éléments qui y sont attachés de la base de données. + * + * @param recipe La recette à supprimer + */ + public void deleteRecipe(Recipe recipe) { + List mixes = recipe.getRecipeMixes(); + mixService.deleteAll(mixes); + + stepService.deleteAll(recipe.getRecipeSteps()); + delete(recipe); + + FileHandler fileHandler; + for (String image : getImageFiles(recipe)) { + fileHandler = new FileHandler(image.replace(".jpeg", ""), FileHandler.FileContext.IMAGE, FileHandler.FileExtension.JPEG); + fileHandler.deleteFile(); + } + } + + /** + * Récupère une liste triée des mélanges pour une recette. + * + * @param recipe La recette dont on veut récupérer les mélanges + * @return Une liste triée des mélanges. + */ + public List getSortedMixes(Recipe recipe) { + List mixes = recipe.getRecipeMixes(); + mixes.sort(Comparator.comparing(Mix::getMixID)); + return mixes; + } + + /** + * Récupère toutes les recettes dans la base de données et les classes par compagnie. + * + * @return Un Map contenant les recettes classées par compagnie. + */ + public Map> getRecipesByCompany() { + List companies = companyService.getAll(); + List recipes = getAll(); + + Map> mappedRecipes = new HashMap<>(); + + for (Company company : companies) { + mappedRecipes.put(company, recipes.stream().filter(r -> r.getCompany().equals(company)).collect(Collectors.toList())); + } + + return mappedRecipes; + } + + /** + * Récupère le nom des images liées à une recette. + * + * @param recipe La recette dont on veut récupérer les images + * @return Une liste contenant le nom des images liées à la recette. + */ + public List getImageFiles(Recipe recipe) { + int recipeID = recipe.getRecipeID(); + String recipeCode = recipe.getRecipeCode(); + String fileName = String.format("%s_%s", recipeID, recipeCode); + + File imageLocation = new File(StringBank.IMAGES_LOCATION); + File[] result = imageLocation.listFiles((d, n) -> n.startsWith(fileName) && n.endsWith("jpeg")); + + if (result == null) return null; + + return Arrays.stream(result).map(File::getName).collect(Collectors.toList()); + } + + /** + * Récupère les types de mélanges associés à la recette. + * + * @param recipe La recette dont on veut récupérer les types de mélange + * @return Une liste contenant les types de mélanges. + */ + public List getAssociatedMixesTypes(Recipe recipe) { + return recipe.getRecipeMixes().stream().map(Mix::getMixType).collect(Collectors.toList()); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/services/StepService.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/StepService.java new file mode 100644 index 0000000..f8dc210 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/services/StepService.java @@ -0,0 +1,15 @@ +package fyloz.trial.ColorRecipesExplorer.services; + +import fyloz.trial.ColorRecipesExplorer.dao.StepDao; +import fyloz.trial.ColorRecipesExplorer.model.RecipeStep; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class StepService extends GenericService { + + @Autowired + public StepService(StepDao stepDao) { + super(stepDao); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/PagesPaths.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/PagesPaths.java new file mode 100644 index 0000000..cf1dbbd --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/PagesPaths.java @@ -0,0 +1,57 @@ +package fyloz.trial.ColorRecipesExplorer.web; + +public class PagesPaths { + // Autres + public static final String INDEX = "index"; + public static final String SIMDUT_FILES = "simdut/{materialID}"; + public static final String PASSWORD_VALIDATION = "password/valid"; + public static final String TOUCHUP = "touchup"; + + // Images + public static final String IMAGES_FILES = "images/{image}"; + public static final String ADD_IMAGE = "images/add"; + public static final String ADD_IMAGE_SPECIFIC = "images/add/{recipeID}"; + public static final String DELETE_IMAGE = "images/deleteIfNotLinked"; + + // Inventaire + public static final String INVENTORY = "inventory"; + public static final String USE_INVENTORY = "inventory/use"; + + // Recettes + public static final String EXPLORER_RECIPE = "recipe/explore"; + public static final String EXPLORER_RECIPE_SPECIFIC = "recipe/explore/{recipeID}"; + public static final String CREATOR_RECIPE = "recipe/creator"; + public static final String CREATOR_RECIPE_SUCCESS = "recipe/created"; + public static final String EDITOR_RECIPE = "recipe/editor"; + public static final String EDITOR_RECIPE_SPECIFIC = "recipe/editor/{recipeID}"; + public static final String EDITOR_RECIPE_EDITOR = "recipe/edit"; + public static final String REMOVER_RECIPE = "recipe/remover"; + public static final String REMOVER_RECIPE_SPECIFIC = "recipe/remover/{recipeID}"; + + // Compagnies + public static final String CREATOR_COMPANY = "company/creator"; + public static final String CREATOR_COMPANY_SUCCESS = "company/created"; + public static final String REMOVER_COMPANY = "company/remover"; + public static final String REMOVER_COMPANY_SPECIFIC = "company/remover/{companyID}"; + + // Matériaux + public static final String CREATOR_MATERIAL = "material/creator"; + public static final String EDIT_MATERIAL_SIMDUT = "material/simdut"; + public static final String EDIT_MATERIAL_SIMDUT_SPECIFIC = "material/simdut/{materialID}"; + public static final String CREATOR_MATERIAL_SUCCESS = "material/created"; + public static final String REMOVER_MATERIAL = "material/remover"; + public static final String REMOVER_MATERIAL_SPECIFIC = "material/remover/{materialID}"; + public static final String EDITOR_MATERIAL = "material/editor"; + public static final String EDITOR_MATERIAL_SPECIFIC = "material/editor/{materialID}"; + public static final String EDITOR_MATERIAL_EDITOR = "material/edit"; + + // Types de matériaux + public static final String CREATOR_MATERIAL_TYPE = "materialType/creator"; + + // Mélanges + public static final String CREATOR_MIX = "mix/creator"; + public static final String CREATOR_MIX_SPECIFIC = "mix/creator/{recipeID}"; + public static final String EDITOR_MIX = "mix/editor"; + public static final String EDITOR_MIX_SPECIFIC = "mix/editor/{mixID}"; + public static final String REMOVER_MIX_SPECIFIC = "mix/remover/{mixID}"; +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/StringBank.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/StringBank.java new file mode 100644 index 0000000..15d61f3 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/StringBank.java @@ -0,0 +1,54 @@ +package fyloz.trial.ColorRecipesExplorer.web; + +public class StringBank { + + public static final String SUCCESS_USING_MATERIALS = "Les quantités de chaque produits utilisés ont été déduites de l'inventaire"; + public static final String SUCCESS_SAVING_RECIPE_INFORMATIONS = "Les informations de la recette ont été sauvegardées"; + public static final String ERROR_SAVING = "Une erreur est survenue lors de l'enregistrement"; + public static final String ERROR_SAVING_IMAGE = "Une erreur est survenue lors de l'enregistrement de l'image"; + public static final String ERROR_SAVING_SIMDUT = "Une erreur est survenue lors de l'enregistrement du fichier SIMDUT"; + public static final String AUTH_ERROR = "Votre mot de passe n'est pas valide"; + + // À formatter + public static final String RECIPE_NOT_FOUND = "Aucune recette ayant l'identifiant '%s' n'a été trouvée"; + public static final String MIX_NOT_FOUND = "Aucun mélange ayant l'identifiant '%s' n'a été trouvé"; + public static final String MATERIAL_NOT_FOUND = "Aucun produit ayant l'identifiant '%s' n'a été trouvé"; + public static final String MATERIAL_ALREADY_EXIST = "Il y a déjà un produit s'appellant '%s'"; + public static final String MATERIAL_TYPE_ALREADY_EXIST = "Il y a déjà un type de produit s'appellant '%s'"; + public static final String COMPANY_NOT_FOUND = "Aucune bannière ayant l'identifiant '%s' n'a été trouvée"; + public static final String COMPANY_ALREADY_EXIST = "Il y a déjà une bannière s'appellant '%s'"; + public static final String MATERIAL_LINKED = "Le produit '%s' est lié à une ou plusieurs recettes, veuillez les supprimer d'abord"; + public static final String COMPANY_LINKED = "La bannière '%s' est liée à une ou plusieurs recettes, veuillez les supprimer d'abord"; + public static final String MIX_NOT_ASSOCIATED_WITH_RECIPE = "Le mélange ayant l'identifiant '%s' n'est pas associé à la recette ayant l'identifiant '%s'"; + public static final String NOT_ENOUGH_MATERIAL = "Il n'y a pas assez de '%s' en inventaire pour cette recette"; + public static final String MIX_TYPE_ALREADY_USED = "Cette recette contient déjà un mélange du type '%s'"; + + // Types de réponse + public static final String RESPONSE_ERROR = "error"; + public static final String RESPONSE_SUCCESS = "success"; + public static final String RESPONSE_REASON = "reason"; + + // Types d'attributs communs + public static final String MATERIALS = "materials"; + public static final String MATERIAL = "material"; + public static final String MATERIAL_ID = "materialID"; + public static final String MATERIAL_CODE = "materialCode"; + public static final String MATERIAL_TYPES = "materialTypes"; + public static final String RECIPES = "recipes"; + public static final String RECIPE = "recipe"; + public static final String RECIPE_ID = "recipeID"; + public static final String RECIPE_CODE = "recipeCode"; + public static final String MIXES = "mixes"; + public static final String MIX = "mix"; + public static final String MIX_ID = "mixID"; + public static final String MIX_TYPE = "mixType"; + public static final String MIX_TYPES = "mixTypes"; + public static final String COMPANIES = "companies"; + public static final String COMPANY = "company"; + public static final String COMPANY_ID = "companyID"; + + public static final String UPLOAD_LOCATION = System.getProperty("upload.location"); + public static final String IMAGES_LOCATION = UPLOAD_LOCATION + "/images"; + public static final String SIMDUTS_LOCATION = UPLOAD_LOCATION + "/simdut/"; + +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/ImageFilesController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/ImageFilesController.java new file mode 100644 index 0000000..384cd97 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/ImageFilesController.java @@ -0,0 +1,147 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller; + +import fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication; +import fyloz.trial.ColorRecipesExplorer.core.io.ImageHandler; +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import fyloz.trial.ColorRecipesExplorer.services.RecipeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.*; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class ImageFilesController { + + private RecipeService recipeService; + + @Autowired + public ImageFilesController(RecipeService recipeService) { + this.recipeService = recipeService; + } + + /** + * Récupère l'image voulue sous forme JPEG. + * + * @param image Le nom de l'image + * @return L'image en JPEG. + */ + @GetMapping(IMAGES_FILES) + public ResponseEntity getImage(@PathVariable String image) { + ImageHandler imageHandler = new ImageHandler(image, recipeService); + + byte[] content = imageHandler.readFile(); + if (content == null) return ResponseEntity.notFound().build(); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.IMAGE_JPEG); + return new ResponseEntity<>(content, headers, HttpStatus.OK); + } + + /** + * Affiche la page pour ajouter une image pour une recette. + * Cette méthode requiert l'identifiant de la recette dans l'URL. + *

+ * Modèle de la page: + * - recipeID: Contient l'identifiant de la recette + * + * @param model Le Model injecté par Thymeleaf + * @param recipeID L'identifiant de la recette + * @return La page à afficher. + */ + @GetMapping(ADD_IMAGE_SPECIFIC) + public String showPage(Model model, @PathVariable int recipeID) { + model.addAttribute(RECIPE_ID, recipeID); + return ADD_IMAGE; + } + + /** + * Permet à l'utilisateur d'uploader une image. + *

+ * L'upload échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action. + * - La recette n'existe pas + * - Une erreur est survenue lors de la création du fichier de l'image + * - Une erreur est survenue lors du transfert de l'image vers le fichier + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - recipeCode: Contient la couleur de la recette + * - recipeID: Contient l'identifiant de la recette + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param recipeID L'identifiant de la recette + * @param image L'image uploadée + * @return La page à afficher. + */ + @PostMapping(ADD_IMAGE) + public String addImage(Model model, int recipeID, MultipartFile image) { + Recipe recipe = recipeService.getByID(recipeID); + if (recipe == null) { + model.addAttribute(RESPONSE_ERROR, RECIPE_NOT_FOUND); + return showPage(model, recipeID); + } + + ImageHandler imageHandler = new ImageHandler(recipe, recipeService); + if (!imageHandler.createFile()) { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING_IMAGE); + return showPage(model, recipeID); + } + + model.addAttribute(RECIPE_CODE, recipe.getRecipeCode()); + model.addAttribute(RECIPE_ID, recipe.getRecipeID()); + + try { + // Si je n'utilise pas le path, il cherche un fichier dans les tmp ? + image.transferTo(new File(imageHandler.getFile().getAbsolutePath())); + + return "redirect:/" + EDITOR_RECIPE_SPECIFIC.replace("{recipeID}", String.valueOf(recipeID)); + } catch (IOException e) { + ColorRecipesExplorerApplication.logger.error("Erreur inconnue lors de la création d'une image", e); + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING_IMAGE); + return showPage(model, recipeID); + } + } + + /** + * Permet à l'utilisateur de supprimer une image. + *

+ * La suppression échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action + * - Une erreur est survenue lors de la suppression du fichier + *

+ * Réponse de la méthode: + * - error: Contient le message d'erreur, s'il y a lieu + *

+ * REQUIERT UNE AUTORISATION + * + * @param form Le formulaire contenant les informations sur l'opération + * @return La réponse sous forme de Map (-> Json) + */ + @PostMapping(value = DELETE_IMAGE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public Map deleteImage(@RequestBody Map form) { + Map response = new HashMap<>(); + + ImageHandler imageHandler = new ImageHandler(form.get("image"), recipeService); + if (!imageHandler.deleteFile()) { + response.put(RESPONSE_ERROR, ERROR_SAVING_IMAGE); + } + + return response; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/IndexController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/IndexController.java new file mode 100644 index 0000000..0ded820 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/IndexController.java @@ -0,0 +1,68 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller; + +import fyloz.trial.ColorRecipesExplorer.PasswordValidator; +import fyloz.trial.ColorRecipesExplorer.model.Company; +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import fyloz.trial.ColorRecipesExplorer.services.CompanyService; +import fyloz.trial.ColorRecipesExplorer.services.RecipeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.INDEX; +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.PASSWORD_VALIDATION; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.RECIPES; + +@Controller +public class IndexController { + + private CompanyService companyService; + private RecipeService recipeService; + + @Autowired + public IndexController(CompanyService companyService, RecipeService recipeService) { + this.companyService = companyService; + this.recipeService = recipeService; + } + + /** + * Affiche la page d'acceuil. + * + * @param model Le Model injecté par Thymeleaf + * @return La page à afficher. + */ + @GetMapping({INDEX, "/"}) + public String showPage(Model model) { + List companies = companyService.getAll(); + Map> recipes = new HashMap<>(); + + for (Company company : companies) { + recipes.put(company, recipeService.getByCompany(company)); + } + + model.addAttribute(RECIPES, recipes); + + return INDEX; + } + + /** + * Valide un mot de passe reçu dans une requête HTTP POST, dans le champ "password". + * + * @param data Corps de la requête HTTP + * @return Si le mot de passe est valide. + */ + @PostMapping(value = PASSWORD_VALIDATION, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public boolean validatePassword(@RequestBody Map data) { + return PasswordValidator.isValid((String) data.get("password")); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/InventoryController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/InventoryController.java new file mode 100644 index 0000000..cb3534a --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/InventoryController.java @@ -0,0 +1,140 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.model.Mix; +import fyloz.trial.ColorRecipesExplorer.model.MixQuantity; +import fyloz.trial.ColorRecipesExplorer.services.InventoryService; +import fyloz.trial.ColorRecipesExplorer.services.MaterialService; +import fyloz.trial.ColorRecipesExplorer.services.MaterialTypeService; +import fyloz.trial.ColorRecipesExplorer.services.MixService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.INVENTORY; +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.USE_INVENTORY; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class InventoryController { + + private InventoryService inventoryService; + private MaterialService materialService; + private MixService mixService; + private MaterialTypeService materialTypeService; + + @Autowired + public InventoryController(InventoryService inventoryService, MaterialService materialService, MixService mixService, MaterialTypeService materialTypeService) { + this.inventoryService = inventoryService; + this.materialService = materialService; + this.mixService = mixService; + this.materialTypeService = materialTypeService; + } + + /** + * Affiche la page de l'inventaire. + *

+ * Modèle de la page: + * - materials: Contient la liste de tous les matériaux + * + * @param model Le Model injecté par Thymeleaf + * @return La page à afficher. + */ + @GetMapping(INVENTORY) + public String getInventory(Model model) { + model.addAttribute(MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList())); + model.addAttribute(MATERIAL_TYPES, materialTypeService.getAll()); + + return INVENTORY; + } + + /** + * Déduit les quantités utilisées de chaque matériaux présents dans le corps de la requête (JSON) de l'inventaire. + * Le JSON dans le corps de la requête doit prendre cette forme: + * { + * "mixID 1": { + * "materialID 1": "quantité 1", + * "materialID 2": "quantité 2", + * "materialID 3": "quantité 3" + * }, + * "mixID 2": { + * "materialID 1": "quantité 1" + * }, + * .. + * } + * S'il y a une erreur, l'opération est annulée et aucun changement n'est fait dans l'inventaire. + *

+ * Réponse de la méthode: + * - error: Contient le message d'erreur, s'il y a lieu + * - reason: Contient la raison de l'erreur (le champ fautif) + * - success: Contient le message à affiche lors du succès de la méthode + * + * @param form Le JSON contenant les quantités à utiliser. + * @return Une réponse sous forme de Map (-> Json). + */ + @PostMapping(value = USE_INVENTORY, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + @Transactional + public Map consumeMaterials(@RequestBody Map form) { + Map response = new HashMap<>(); + + List mixes = new ArrayList<>(); + Map> quantities = new HashMap<>(); + + for (String mixIDStr : form.keySet()) { + int mixID = Integer.parseInt(mixIDStr); + + Mix mix = mixService.getByID(mixID); + if (mix == null) { + response.put(RESPONSE_ERROR, String.format(MIX_NOT_FOUND, mixID)); + return response; + } + + mixes.add(mix); + + Map formMaterials = (Map) form.get(mixIDStr); + Map mixQuantities = new HashMap<>(); + + for (Material material : mix.getMixQuantities().stream().map(MixQuantity::getMaterial).collect(Collectors.toList())) { + String materialIDAsString = String.valueOf(material.getMaterialID()); + + if (formMaterials.containsKey(materialIDAsString)) { + mixQuantities.put(material, Float.parseFloat(formMaterials.get(materialIDAsString))); + } + } + + quantities.put(mix, mixQuantities); + } + + for (Mix mix : mixes) { + String errorCode = inventoryService.checkQuantities(mix, quantities.get(mix)); + if (errorCode != null) { + response.put(RESPONSE_ERROR, String.format(NOT_ENOUGH_MATERIAL, materialService.getByID(Integer.parseInt(errorCode.split("-")[1])).getMaterialCode())); + response.put(RESPONSE_REASON, errorCode); + return response; + } + } + + for (Mix mix : mixes) { + if (!inventoryService.useMix(mix, quantities.get(mix))) { + response.put(RESPONSE_ERROR, ERROR_SAVING); + return response; + } + } + + response.put(RESPONSE_SUCCESS, SUCCESS_USING_MATERIALS); + return response; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/OthersController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/OthersController.java new file mode 100644 index 0000000..8042e07 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/OthersController.java @@ -0,0 +1,14 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.TOUCHUP; + +@Controller +public class OthersController { + @GetMapping(TOUCHUP) + public String getTouchUpPdf() { + return "redirect:/pdf/touchup.pdf"; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/RecipeExplorerController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/RecipeExplorerController.java new file mode 100644 index 0000000..e293285 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/RecipeExplorerController.java @@ -0,0 +1,118 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller; + +import fyloz.trial.ColorRecipesExplorer.model.Mix; +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import fyloz.trial.ColorRecipesExplorer.services.MixService; +import fyloz.trial.ColorRecipesExplorer.services.RecipeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.*; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class RecipeExplorerController { + + private RecipeService recipeService; + private MixService mixService; + + @Autowired + public RecipeExplorerController(RecipeService recipeService, MixService mixService) { + this.recipeService = recipeService; + this.mixService = mixService; + } + + /** + * Affiche la recette. + * Cette méthode requiert l'identifiant de la recette à affiche dans l'URL. + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - recipe: Contient la recette + * - mixes: Contient les mélanges associées à la recette + * - images: Contient la liste des noms des images associées à la recette + * + * @param model Le Model injecté par Thymeleaf + * @param recipeID L'identifiant de la recette + * @return La page à afficher. + */ + @GetMapping(EXPLORER_RECIPE_SPECIFIC) + public String showRecipe(Model model, @PathVariable int recipeID) { + Recipe recipe = recipeService.getByID(recipeID); + + if (recipe == null) { + model.addAttribute(RESPONSE_ERROR, String.format(RECIPE_NOT_FOUND, recipeID)); + return INDEX; + } + + List mixes = recipe.getRecipeMixes(); + mixes.sort(Comparator.comparing(Mix::getMixID)); + + model.addAttribute(RECIPE, recipe); + model.addAttribute(MIXES, mixes); + model.addAttribute("images", recipeService.getImageFiles(recipe)); + + return EXPLORER_RECIPE; + } + + /** + * Sauvegarde les informations de la recette. + *

+ * Certaines parties de la sauvegarde échouera si: + * - La recette n'existe pas + * - Un des mélanges n'existe pas + * - Un des mélanges n'est pas associé à la recette + *

+ * Réponse de la méthode: + * - error: Contient le message d'erreur, s'il y a lieu + * - success: Contient le message à afficher lors du succès de la méthode + * + * @param form Le formulaire contenant les informations de la recette + * @return La réponse sous forme de Map (-> Json) + */ + @PostMapping(value = EXPLORER_RECIPE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public Map saveRecipeInformations(@RequestBody Map form) { + int recipeID = Integer.parseInt(form.get("recipeID").toString()); + Map location = (Map) form.get("locations"); + String note = form.get("note").toString(); + + Map response = new HashMap<>(); + + Recipe recipe = recipeService.getByID(recipeID); + if (recipe == null) { + response.put(RESPONSE_ERROR, String.format(RECIPE_NOT_FOUND, recipeID)); + } else { + recipe.setNote(note); + recipeService.save(recipe); + } + + for (String mixIDStr : location.keySet()) { + int mixID = Integer.parseInt(mixIDStr); + + Mix mix = mixService.getByID(mixID); + if (mix == null) { + response.put(RESPONSE_ERROR, String.format(MIX_NOT_FOUND, mixID)); + } else if (mix.getRecipe().getRecipeID() != recipeID) { + response.put(RESPONSE_ERROR, String.format(MIX_NOT_ASSOCIATED_WITH_RECIPE, mixID, recipeID)); + } else { + mix.setLocation(location.get(mixIDStr)); + mixService.update(mix); + } + } + + if (response.get(RESPONSE_ERROR) == null) { + response.put(RESPONSE_SUCCESS, SUCCESS_SAVING_RECIPE_INFORMATIONS); + } + + return response; + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/SIMDUTFilesController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/SIMDUTFilesController.java new file mode 100644 index 0000000..7046482 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/SIMDUTFilesController.java @@ -0,0 +1,53 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller; + +import fyloz.trial.ColorRecipesExplorer.core.io.FileHandler; +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.services.MaterialService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.SIMDUT_FILES; + +@Controller +public class SIMDUTFilesController { + + private MaterialService materialService; + + @Autowired + public SIMDUTFilesController(MaterialService materialService) { + this.materialService = materialService; + } + + /** + * Récupère le fichier SIMDUT voulu sous forme PDF. + * Cette méthode requiert l'identifiant du produit dans l'URL. + * + * @param materialID L'identifiant du produit + * @return Le fichier SIMDUT en PDF. + */ + @GetMapping(SIMDUT_FILES) + public ResponseEntity getFile(@PathVariable int materialID) { + Material material = materialService.getByID(materialID); + + if (material == null) { + return ResponseEntity.notFound().build(); + } + + HttpHeaders headers = new HttpHeaders(); + + FileHandler fileHandler = new FileHandler(String.format("%s_%s", materialID, material.getMaterialCode()), FileHandler.FileContext.SIMDUT, FileHandler.FileExtension.PDF); + if (!fileHandler.isValid()) { + return ResponseEntity.notFound().build(); + } + + byte[] fileContent = fileHandler.readFile(); + headers.setContentType(MediaType.APPLICATION_PDF); + return new ResponseEntity<>(fileContent, headers, HttpStatus.OK); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/CompanyCreatorController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/CompanyCreatorController.java new file mode 100644 index 0000000..8d49d61 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/CompanyCreatorController.java @@ -0,0 +1,71 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.creators; + +import fyloz.trial.ColorRecipesExplorer.model.Company; +import fyloz.trial.ColorRecipesExplorer.services.CompanyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; + +import javax.validation.Valid; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.CREATOR_COMPANY; +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.CREATOR_COMPANY_SUCCESS; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class CompanyCreatorController { + + private CompanyService companyService; + + @Autowired + public CompanyCreatorController(CompanyService companyService) { + this.companyService = companyService; + } + + /** + * Affiche la page de création d'une bannière. + * + * @return La page à afficher. + */ + @GetMapping(CREATOR_COMPANY) + public String showCreationPage() { + return CREATOR_COMPANY; + } + + /** + * Permet à l'utilisateur de créer un bannière. + *

+ * La création échouera si: + * - L'utilisateur n'est pas autorisé à effectuer cette action + * - La bannière existe déjà + * - Une erreur est survenue lors de la sauvegarde dans la base de données + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - companyName: Contient le nom de la bannière + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le modèle injecté par Thymeleaf + * @param company La bannière à créer + * @return La page à afficher. + */ + @PostMapping(value = CREATOR_COMPANY, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public String createCompany(Model model, @Valid Company company) { + if (companyService.isValidForCreation(company)) { + if ((company = companyService.save(company)) != null) { + model.addAttribute("companyName", company.getCompanyName()); + return CREATOR_COMPANY_SUCCESS; + } else { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING); + } + } else { + model.addAttribute(RESPONSE_ERROR, String.format(COMPANY_ALREADY_EXIST, company.getCompanyName())); + } + + return showCreationPage(); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/MaterialCreatorController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/MaterialCreatorController.java new file mode 100644 index 0000000..2460312 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/MaterialCreatorController.java @@ -0,0 +1,82 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.creators; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.services.MaterialService; +import fyloz.trial.ColorRecipesExplorer.services.MaterialTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.Valid; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.CREATOR_MATERIAL; +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.CREATOR_MATERIAL_SUCCESS; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class MaterialCreatorController { + + private MaterialService materialService; + private MaterialTypeService materialTypeService; + + @Autowired + public MaterialCreatorController(MaterialService materialService, MaterialTypeService materialTypeService) { + this.materialService = materialService; + this.materialTypeService = materialTypeService; + } + + /** + * Affiche la page de création d'un produit. + * + * @return La page à afficher. + */ + @GetMapping(CREATOR_MATERIAL) + public String showCreationPage(Model model) { + model.addAttribute(MATERIAL_TYPES, materialTypeService.getAll()); + return CREATOR_MATERIAL; + } + + /** + * Permet à l'utilisateur de créer un produit. + *

+ * La création échouera si: + * - L'utilisateur n'est pas autorisé à effectuer cette action + * - Le produit existe déjà + * - Une erreur est survenue lors de la sauvegarde dans la base de données + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - materialCode: Contient le code de produit du produit créé + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le modèle injecté par Thymeleaf + * @param material Le produit à créer + * @param simdut Le fichier SIMDUT du produit (optionnel) + * @return La page à afficher. + */ + @PostMapping(value = CREATOR_MATERIAL, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public String create(Model model, @Valid Material material, MultipartFile simdut) { + if (!materialService.exists(material)) { + if ((material = materialService.save(material)) != null) { + model.addAttribute(MATERIAL_CODE, material.getMaterialCode()); + + if (!materialService.addSimdut(simdut, material)) { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING_SIMDUT); + } + + return CREATOR_MATERIAL_SUCCESS; + } else { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING); + } + } else { + model.addAttribute(RESPONSE_ERROR, String.format(MATERIAL_ALREADY_EXIST, material.getMaterialCode())); + } + + return showCreationPage(model); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/MaterialTypeCreatorController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/MaterialTypeCreatorController.java new file mode 100644 index 0000000..630e600 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/MaterialTypeCreatorController.java @@ -0,0 +1,46 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.creators; + +import fyloz.trial.ColorRecipesExplorer.model.MaterialType; +import fyloz.trial.ColorRecipesExplorer.services.MaterialTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; + +import javax.validation.Valid; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.CREATOR_MATERIAL_TYPE; +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.INDEX; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class MaterialTypeCreatorController { + + private MaterialTypeService materialTypeService; + + @Autowired + public MaterialTypeCreatorController(MaterialTypeService materialTypeService) { + this.materialTypeService = materialTypeService; + } + + @GetMapping(CREATOR_MATERIAL_TYPE) + public String showPage() { + return CREATOR_MATERIAL_TYPE; + } + + @PostMapping(CREATOR_MATERIAL_TYPE) + public String create(Model model, @Valid MaterialType materialType) { + if (materialTypeService.isValidForCreation(materialType)) { + if (materialTypeService.save(materialType) != null) { + return "redirect:/" + INDEX; + } else { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING); + } + } else { + model.addAttribute(RESPONSE_ERROR, String.format(MIX_TYPE_ALREADY_USED, materialType.getMaterialTypeName())); + } + + return showPage(); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/MixCreatorController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/MixCreatorController.java new file mode 100644 index 0000000..2ee93fb --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/MixCreatorController.java @@ -0,0 +1,126 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.creators; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.model.MixType; +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import fyloz.trial.ColorRecipesExplorer.services.MaterialService; +import fyloz.trial.ColorRecipesExplorer.services.MixService; +import fyloz.trial.ColorRecipesExplorer.services.MixTypeService; +import fyloz.trial.ColorRecipesExplorer.services.RecipeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.*; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class MixCreatorController { + + private MixService mixService; + private RecipeService recipeService; + private MaterialService materialService; + private MixTypeService mixTypeService; + + @Autowired + public MixCreatorController(MixService mixService, RecipeService recipeService, MaterialService materialService, MixTypeService mixTypeService) { + this.mixService = mixService; + this.recipeService = recipeService; + this.materialService = materialService; + this.mixTypeService = mixTypeService; + } + + /** + * Affiche la page de création d'un mélange. + * Cette méthode requiert l'identifiant d'une recette dans l'URL. + * + * @param model Le Model injecté par Thymeleaf + * @param recipeID L'identifiant de la recette + * @return La page à afficher. + */ + @GetMapping(CREATOR_MIX_SPECIFIC) + public String showCreationPage(Model model, @PathVariable int recipeID) { + Recipe recipe = recipeService.getByID(recipeID); + if (recipe == null) { + return "redirect:/" + EDITOR_RECIPE; + } + + List associatedMixTypes = recipeService.getAssociatedMixesTypes(recipe); + // Récupère seulement les produits qui ne sont pas des types de mélange OU que ce type existe dans la recette + List materials = materialService.getAll().stream().filter(m -> !m.isMixType() || associatedMixTypes.contains(mixTypeService.getByMaterial(m))).collect(Collectors.toList()); + + model.addAttribute(RECIPE, recipe); + model.addAttribute(MATERIALS, materials); + model.addAttribute("materialsJson", materialService.asJson(materials)); // Ajoute les matériaux sous forme de JSON pour utiliser la liste en Javascript + + return CREATOR_MIX; + } + + /** + * Permet à l'utilisateur de créer un mélange. + *

+ * La création échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action + * - La recette n'existe pas + * - Un des produits n'existe pas + * - Une erreur est survenue lors de la sauvegarde du type de mélange + * - Une erreur est survenue lors de la sauvegarde du mélange + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param form La formulaire du mélange entré par l'utilisateur + * @return La page à afficher. + */ + @PostMapping(value = CREATOR_MIX, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public String createMix(Model model, @RequestBody MultiValueMap form) { + String mixTypeName = form.getFirst(MIX_TYPE); + int recipeID = Integer.parseInt(form.getFirst(RECIPE_ID)); + + List materials = new ArrayList<>(); + List quantities = new ArrayList<>(); + // Trie les valeurs du formulaire de création du mélange + for (String key : form.keySet()) { + String value = form.getFirst(key); + + if (key.startsWith("product")) materials.add(Integer.parseInt(value)); + else if (key.startsWith("quantity") && !value.isEmpty()) quantities.add(Float.parseFloat(value)); + } + + Recipe recipe = recipeService.getByID(recipeID); + if (recipe == null) { + model.addAttribute(RESPONSE_ERROR, String.format(RECIPE_NOT_FOUND, recipeID)); + return EDITOR_RECIPE; + } + + MixType mixType = mixTypeService.createByName(mixTypeName); + if (mixType == null) { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING); + return EDITOR_RECIPE; + } else if (recipeService.getAssociatedMixesTypes(recipe).contains(mixType)) { + model.addAttribute(RESPONSE_ERROR, MIX_TYPE_ALREADY_USED); + return EDITOR_RECIPE; + } + + String errorMessage = mixService.create(materials, quantities, recipe, mixType); + if (errorMessage != null) { + model.addAttribute(RESPONSE_ERROR, errorMessage); + return showCreationPage(model, recipeID); + } + + return "redirect:/" + EDITOR_RECIPE_SPECIFIC.replaceAll("\\{" + RECIPE_ID + "}", String.valueOf(recipeID)); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/RecipeCreatorController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/RecipeCreatorController.java new file mode 100644 index 0000000..c34cb84 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/creators/RecipeCreatorController.java @@ -0,0 +1,70 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.creators; + +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import fyloz.trial.ColorRecipesExplorer.services.CompanyService; +import fyloz.trial.ColorRecipesExplorer.services.RecipeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; + +import javax.validation.Valid; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.CREATOR_RECIPE; +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.CREATOR_RECIPE_SUCCESS; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class RecipeCreatorController { + + private RecipeService recipeService; + private CompanyService companyService; + + @Autowired + public RecipeCreatorController(RecipeService recipeService, CompanyService companyService) { + this.recipeService = recipeService; + this.companyService = companyService; + } + + /** + * Affiche la page de création d'une recette. + * + * @param model Le Model injecté par Thymeleaf + * @return La page à afficher. + */ + @GetMapping(CREATOR_RECIPE) + public String showCreationPage(Model model) { + model.addAttribute(COMPANIES, companyService.getAll()); + + return CREATOR_RECIPE; + } + + /** + * Permet à l'utilisateur de créer une nouvelle recette. + *

+ * La création échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action + * - Une erreur est survenue lors de la sauvegarde dans la base de données + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param recipe La recette à créer + * @return La page à afficher. + */ + @PostMapping(value = CREATOR_RECIPE) + public String createRecipe(Model model, @Valid Recipe recipe) { + if ((recipe = recipeService.save(recipe)) != null) { + model.addAttribute(RECIPE_CODE, recipe.getRecipeCode()); + model.addAttribute(RECIPE_ID, recipe.getRecipeID()); + return CREATOR_RECIPE_SUCCESS; + } else { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING); + return showCreationPage(model); + } + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/editors/MaterialEditorController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/editors/MaterialEditorController.java new file mode 100644 index 0000000..fba3bff --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/editors/MaterialEditorController.java @@ -0,0 +1,153 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.editors; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.services.MaterialService; +import fyloz.trial.ColorRecipesExplorer.services.MaterialTypeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.multipart.MultipartFile; + +import java.util.stream.Collectors; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.*; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class MaterialEditorController { + + private MaterialService materialService; + private MaterialTypeService materialTypeService; + + @Autowired + public MaterialEditorController(MaterialService materialService, MaterialTypeService materialTypeService) { + this.materialService = materialService; + this.materialTypeService = materialTypeService; + } + + /** + * Affiche la page de tous les produits. + *

+ * Modèle de la page: + * - materials: La liste de tous les produits + * + * @param model Le Model injecté par Thymeleaf + * @return La page à afficher. + */ + @GetMapping(EDITOR_MATERIAL) + public String listMaterials(Model model) { + model.addAttribute(MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList())); + + return EDITOR_MATERIAL; + } + + /** + * Affiche la page d'édition d'un produit. + * La méthode requiert l'identifiant du produit à modifier dans l'URL. + * + * @param model Le Model injecté par Thymeleaf + * @param materialID L'identifiant du produit à modifier + * @return La page à afficher. + */ + @GetMapping(EDITOR_MATERIAL_SPECIFIC) + public String showEditPage(Model model, @PathVariable int materialID) { + Material material = materialService.getByID(materialID); + + if (material == null) { + model.addAttribute(RESPONSE_ERROR, String.format(MATERIAL_NOT_FOUND, materialID)); + + return listMaterials(model); + } + + model.addAttribute(MATERIAL, material); + model.addAttribute(MATERIAL_TYPES, materialTypeService.getAll()); + return EDITOR_MATERIAL_EDITOR; + } + + /** + * Affiche la page d'upload d'un fichier SIMDUT pour un produit + * Cette méthode requiert l'identifiant du produit dans l'URL + *

+ * Modèle de la page: + * - materialID: Contient l'identifiant du produit + * + * @param model Le Model injecté par Thymeleaf + * @param materialID L'identifiant du produit à modifier le SIMDUT + * @return La page à afficher. + */ + @GetMapping(value = EDIT_MATERIAL_SIMDUT_SPECIFIC) + public String chooseSIMDUTFile(Model model, @PathVariable int materialID) { + model.addAttribute(MATERIAL_ID, materialID); + + return EDIT_MATERIAL_SIMDUT; + } + + /** + * Sauvegarde le fichier SIMDUT d'un produit. + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param materialID L'identifiant du produit + * @param simdut Le fichier SIMDUT uploadé + * @return La page à afficher. + */ + @PostMapping(value = EDIT_MATERIAL_SIMDUT, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public String saveSIMDUT(Model model, int materialID, MultipartFile simdut) { + Material material = materialService.getByID(materialID); + if (material == null) { + model.addAttribute(RESPONSE_ERROR, MATERIAL_NOT_FOUND); + return chooseSIMDUTFile(model, materialID); + } + + if (!materialService.removeSimdut(material) || !materialService.addSimdut(simdut, material)) { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING_SIMDUT); + return chooseSIMDUTFile(model, materialID); + } + + return "redirect:/material/editor/" + materialID; + } + + /** + * Permet à l'utilisateur de modifier un produit. + *

+ * La modification échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action + * - Il n'y a aucun produit avec l'identifiant passé en paramètre + * - Une erreur est survenue lors de la mise à jour dans la base de données + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - materialCode: Contient le code du produit mit à jour + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param material Le produit à mettre à jour + * @return La page à afficher. + */ + @PostMapping(value = EDITOR_MATERIAL, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public String saveEditedMaterial(Model model, Material material) { + int materialID = material.getMaterialID(); + + if (materialService.getByID(materialID) == null) { + model.addAttribute(RESPONSE_ERROR, String.format(MATERIAL_NOT_FOUND, materialID)); + } + + if ((material = materialService.update(material)) != null) { + model.addAttribute(MATERIAL_CODE, material.getMaterialCode()); + } else { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING); + return showEditPage(model, materialID); + } + + return listMaterials(model); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/editors/MixEditorController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/editors/MixEditorController.java new file mode 100644 index 0000000..0ccc185 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/editors/MixEditorController.java @@ -0,0 +1,136 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.editors; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.model.Mix; +import fyloz.trial.ColorRecipesExplorer.model.MixType; +import fyloz.trial.ColorRecipesExplorer.services.MaterialService; +import fyloz.trial.ColorRecipesExplorer.services.MixService; +import fyloz.trial.ColorRecipesExplorer.services.MixTypeService; +import fyloz.trial.ColorRecipesExplorer.services.RecipeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.ArrayList; +import java.util.List; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.*; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class MixEditorController { + + private MixService mixService; + private MaterialService materialService; + private RecipeService recipeService; + private MixTypeService mixTypeService; + + @Autowired + public MixEditorController(MixService mixService, MaterialService materialService, RecipeService recipeService, MixTypeService mixTypeService) { + this.mixService = mixService; + this.materialService = materialService; + this.recipeService = recipeService; + this.mixTypeService = mixTypeService; + } + + /** + * Affiche la page d'édition d'un mélange. + * Cette méthode requiert l'identifiant du mélange à modifier dans l'URL. + * + * @param model Le Model injecté par Thymeleaf + * @param mixID L'identifiant du mélange à modifier + * @return La page à afficher. + */ + @GetMapping(EDITOR_MIX_SPECIFIC) + public String showPage(Model model, @PathVariable int mixID) { + Mix mix = mixService.getByID(mixID); + + // Renvoie l'utilisateur à la page d'édition des recettes si le mélange n'est pas trouvé + if (mix == null) { + return "redirect:/" + EDITOR_RECIPE; + } + + List materials = new ArrayList<>(); + List associatedMixTypes = recipeService.getAssociatedMixesTypes(mix.getRecipe()); + for (Material material : materialService.getAll()) { + if (!material.isMixType() || (!mix.getMixType().getMaterial().equals(material) && associatedMixTypes.contains(mixTypeService.getByMaterial(material)))) { + materials.add(material); + } + } + + model.addAttribute(MIX, mix); + model.addAttribute("mixJson", mixService.asJson(mix)); + model.addAttribute(RECIPE_CODE, mix.getRecipe().getRecipeCode()); + model.addAttribute("materialsJson", materialService.asJson(materials)); + + return EDITOR_MIX_SPECIFIC.replaceAll("/\\{" + MIX_ID + "}", ""); + } + + /** + * Permet à l'utilisateur de modifier un mélange. + *

+ * La mise à jour échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action + * - Le mélange n'existe pas + * - Un des produits n'existe pas + * - Une erreur est survenue lors de la mise à jour dans la base de données + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param form Le formulaire du mélange entré par l'utilisateur + * @return La page à afficher. + */ + @PostMapping(value = EDITOR_MIX, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public String saveMix(Model model, @RequestBody MultiValueMap form) { + int mixID = Integer.parseInt(form.getFirst(MIX_ID)); + + Mix mix = mixService.getByID(mixID); + if (mix == null) { + model.addAttribute(RESPONSE_ERROR, String.format(MIX_NOT_FOUND, mixID)); + return showPage(model, mixID); + } + + List materials = new ArrayList<>(); + List quantities = new ArrayList<>(); + + for (String key : form.keySet()) { + String value = form.getFirst(key); + + if (key.startsWith("product")) materials.add(Integer.parseInt(value)); + else if (key.startsWith("quantity")) quantities.add(Float.parseFloat(value)); + } + + if (!mixService.edit(mix, materials, quantities)) { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING); + return showPage(model, mixID); + } + + return "redirect:/" + EDITOR_RECIPE_SPECIFIC.replace("{recipeID}", String.valueOf(mix.getRecipe().getRecipeID())); + } + + @GetMapping(REMOVER_MIX_SPECIFIC) + public String deleteMix(Model model, @PathVariable int mixID) { + Mix mix = mixService.getByID(mixID); + if (mix == null) { + model.addAttribute(RESPONSE_ERROR, MIX_NOT_FOUND); + return showPage(model, mixID); + } + + if (!mixService.deleteMix(mix)) { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING); + return showPage(model, mixID); + } + + return "redirect:/" + EDITOR_RECIPE_SPECIFIC.replace("{recipeID}", String.valueOf(mix.getRecipe().getRecipeID())); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/editors/RecipeEditorController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/editors/RecipeEditorController.java new file mode 100644 index 0000000..668adf5 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/editors/RecipeEditorController.java @@ -0,0 +1,116 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.editors; + +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import fyloz.trial.ColorRecipesExplorer.services.CompanyService; +import fyloz.trial.ColorRecipesExplorer.services.RecipeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import javax.validation.Valid; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.*; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class RecipeEditorController { + + private RecipeService recipeService; + private CompanyService companyService; + + @Autowired + public RecipeEditorController(RecipeService recipeService, CompanyService companyService) { + this.recipeService = recipeService; + this.companyService = companyService; + } + + /** + * Affiche la page listant toutes les recettes. + *

+ * Modèle de la page: + * - recipes: Contient un Map de toutes les recettes triées par compagnie + * + * @param model Le Model injecté par Thymeleaf + * @return La page à afficher. + */ + @GetMapping(EDITOR_RECIPE) + public String listRecipes(Model model) { + model.addAttribute(RECIPES, recipeService.getRecipesByCompany()); + + return EDITOR_RECIPE; + } + + /** + * Affiche la page d'édition d'une recette. + * Cette méthode requiert l'identifiant de la recette dans l'URL. + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - recipe: Contient la recette + * - recipeJSON: Contient la recette sous forme de JSON + * - companies: Contient la liste des compagnies + * - mixes: Contient la liste des mélanges de la recettes, classées + * - images: Contient la liste des noms des images liées à la recette + * + * @param model Le Model injecté par Thymeleaf + * @param recipeID L'identifiant de la recette + * @return La page à afficher. + */ + @GetMapping(EDITOR_RECIPE_SPECIFIC) + public String showEditPage(Model model, @PathVariable int recipeID) { + Recipe recipe = recipeService.getByID(recipeID); + + if (recipe == null) { + model.addAttribute(RESPONSE_ERROR, String.format(RECIPE_NOT_FOUND, recipeID)); + return listRecipes(model); + } + + model.addAttribute(RECIPE, recipe); + model.addAttribute("recipeJSON", recipeService.asJson(recipe)); + model.addAttribute(COMPANIES, companyService.getAll()); + model.addAttribute(MIXES, recipeService.getSortedMixes(recipe)); + model.addAttribute("images", recipeService.getImageFiles(recipe)); + + return EDITOR_RECIPE_EDITOR; + } + + /** + * Permet à l'utilisateur de modifier une recette. + *

+ * La mise à jour échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action + * - La recette n'existe pas + * - Une erreur est survenue lors de la mise à jour dans la base de données + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - recipeCode: Contient la couleur d'une recette + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param recipe La recette à modifier + * @param form Le formulaire entré par l'utilisateur + * @return La page à afficher. + */ + @PostMapping(value = EDITOR_RECIPE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public String saveRecipe(Model model, @Valid Recipe recipe, @RequestBody MultiValueMap form) { + int recipeID = recipe.getRecipeID(); + + if (recipeService.getByID(recipeID) == null) { + model.addAttribute(RESPONSE_ERROR, String.format(RECIPE_NOT_FOUND, recipeID)); + return listRecipes(model); + } + + recipe = recipeService.createAndSetSteps(recipe, form); + + model.addAttribute(RECIPE_CODE, recipe.getRecipeCode()); + return listRecipes(model); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/removers/CompanyRemoverController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/removers/CompanyRemoverController.java new file mode 100644 index 0000000..1c2bec6 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/removers/CompanyRemoverController.java @@ -0,0 +1,77 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.removers; + +import fyloz.trial.ColorRecipesExplorer.model.Company; +import fyloz.trial.ColorRecipesExplorer.services.CompanyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_COMPANY; +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_COMPANY_SPECIFIC; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class CompanyRemoverController { + + private CompanyService companyService; + + @Autowired + public CompanyRemoverController(CompanyService companyService) { + this.companyService = companyService; + } + + /** + * Affiche la page listant toutes les bannières. + *

+ * Modèle de la page: + * - companies: Contient la liste de toutes les bannières + * + * @param model Le Model injecté par Thymeleaf + * @return La page à afficher + */ + @GetMapping(REMOVER_COMPANY) + public String showPage(Model model) { + model.addAttribute(COMPANIES, companyService.getAll()); + + return REMOVER_COMPANY; + } + + /** + * Permet à l'utilisateur de supprimer une bannière. + * La méthode requiert l'identifiant de la bannière à supprimer dans l'URL. + *

+ * La suppression échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action + * - Il n'y a aucune bannière avec cet identifiant + * - La bannière est associée à une ou plusieurs recettes + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - successCompanyName: Contient le nom de la bannière supprimée + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param companyID L'identifiant de la bannière + * @return La page à afficher + */ + + @PostMapping(REMOVER_COMPANY_SPECIFIC) + public String removeCompany(Model model, @PathVariable int companyID) { + Company company = companyService.getByID(companyID); + if (companyService.exists(company)) { + if (companyService.deleteIfNotLinked(company)) { + model.addAttribute("successCompanyName", company.getCompanyName()); + } else { + model.addAttribute(RESPONSE_ERROR, String.format(COMPANY_LINKED, company.getCompanyName())); + } + } else { + model.addAttribute(RESPONSE_ERROR, String.format(COMPANY_NOT_FOUND, companyID)); + } + + return showPage(model); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/removers/MaterialRemoverController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/removers/MaterialRemoverController.java new file mode 100644 index 0000000..f8edee6 --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/removers/MaterialRemoverController.java @@ -0,0 +1,80 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.removers; + +import fyloz.trial.ColorRecipesExplorer.model.Material; +import fyloz.trial.ColorRecipesExplorer.services.MaterialService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.stream.Collectors; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_MATERIAL; +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_MATERIAL_SPECIFIC; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class MaterialRemoverController { + + private MaterialService materialService; + + @Autowired + public MaterialRemoverController(MaterialService materialService) { + this.materialService = materialService; + } + + /** + * Affiche la page de suppression des produits + * + * @param model Le Model injecté par Thymeleaf + * @return La page à afficher. + */ + @GetMapping(REMOVER_MATERIAL) + public String showPage(Model model) { + model.addAttribute(MATERIALS, materialService.getAll().stream().filter(m -> !m.isMixType()).collect(Collectors.toList())); + + return REMOVER_MATERIAL; + } + + /** + * Permet à l'utilisateur de supprimer un produit. + * Cette méthode requiert l'identifiant du produit dans l'URL. + *

+ * La suppression échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action + * - Le produit n'existe pas + * - Le produit est lié à d'autres mélanges + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - materialCode: Contient le code de produit du produit supprimé + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param materialID L'identifiant du produit à supprimer + * @return La page à afficher. + */ + @PostMapping(REMOVER_MATERIAL_SPECIFIC) + public String removeMaterial(Model model, @PathVariable int materialID) { + Material material = materialService.getByID(materialID); + + if (material == null) { + model.addAttribute(RESPONSE_ERROR, String.format(MATERIAL_NOT_FOUND, materialID)); + } else { + if (materialService.deleteIfNotLinked(material)) { + model.addAttribute(MATERIAL_CODE, material.getMaterialCode()); + + if (!materialService.removeSimdut(material)) { + model.addAttribute(RESPONSE_ERROR, ERROR_SAVING_SIMDUT); + } + } else { + model.addAttribute(RESPONSE_ERROR, String.format(MATERIAL_LINKED, material.getMaterialCode())); + } + } + + return showPage(model); + } +} diff --git a/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/removers/RecipeRemoverController.java b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/removers/RecipeRemoverController.java new file mode 100644 index 0000000..d23698d --- /dev/null +++ b/src/main/java/fyloz/trial/ColorRecipesExplorer/web/controller/removers/RecipeRemoverController.java @@ -0,0 +1,71 @@ +package fyloz.trial.ColorRecipesExplorer.web.controller.removers; + +import fyloz.trial.ColorRecipesExplorer.model.Recipe; +import fyloz.trial.ColorRecipesExplorer.services.RecipeService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_RECIPE; +import static fyloz.trial.ColorRecipesExplorer.web.PagesPaths.REMOVER_RECIPE_SPECIFIC; +import static fyloz.trial.ColorRecipesExplorer.web.StringBank.*; + +@Controller +public class RecipeRemoverController { + + private RecipeService recipeService; + + public RecipeRemoverController(RecipeService recipeService) { + this.recipeService = recipeService; + } + + /** + * Affiche la liste de toutes les recettes. + * + * @param model Le Model injecté par Thymeleaf + * @return La page à afficher. + */ + @GetMapping(REMOVER_RECIPE) + public String listRecipes(Model model) { + model.addAttribute(RECIPES, recipeService.getRecipesByCompany()); + + return REMOVER_RECIPE; + } + + /** + * Permet à l'utilisateur de supprimer une recette. + * Cette méthode requiert l'identifiant de la recette dans l'URL. + *

+ * La suppression échouera si: + * - L'utilisateur n'est pas autorisé à exécuter cette action + * - La recette n'existe pas + * - Une erreur est survenue lors de la suppression dans la base de données + *

+ * Modèle de la page: + * - error: Contient le message d'erreur, s'il y a lieu + * - recipeCode: Contient la couleur de la recette + *

+ * REQUIERT UNE AUTORISATION + * + * @param model Le Model injecté par Thymeleaf + * @param recipeID L'identifiant de la recette + * @return La page à afficher. + */ + @PostMapping(REMOVER_RECIPE_SPECIFIC) + public String removeRecipe(Model model, @PathVariable int recipeID) { + Recipe recipe = recipeService.getByID(recipeID); + + // Affiche un erreur si le recette n'est pas trouvée + if (recipe == null) { + model.addAttribute(RESPONSE_ERROR, String.format(RECIPE_NOT_FOUND, recipeID)); + return listRecipes(model); + } + + recipeService.deleteRecipe(recipe); + + model.addAttribute(RECIPE_CODE, recipe.getRecipeCode()); + return listRecipes(model); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..2aacb8f --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,26 @@ +# Tomcat +server.port=8080 +# Favicon +spring.mvc.favicon.enabled=false +# H2 +spring.datasource.url=jdbc:h2:file:./recipes +spring.datasource.username=sa +spring.datasource.password=password +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect +spring.datasource.driver-class-name=org.h2.Driver +# JPA +#spring.jpa.show-sql=true +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.format_sql=false +# Thymeleaf +spring.thymeleaf.template-loader-path=classpath:/templates +spring.thymeleaf.suffix=.html +spring.thymeleaf.cache=false +# Max file size. +spring.servlet.multipart.max-file-size=10MB +# Max request size. +spring.servlet.multipart.max-request-size=15MB +# DEBUG +logging.level.org.springframework.web=DEBUG +logging.level.com.zaxxer.hikari=DEBUG +spring.h2.console.enabled=true \ No newline at end of file diff --git a/src/main/resources/import.sql b/src/main/resources/import.sql new file mode 100644 index 0000000..0787bff --- /dev/null +++ b/src/main/resources/import.sql @@ -0,0 +1,2 @@ +INSERT INTO MATERIAL_TYPE +VALUES (0, 'Aucun', '', FALSE); \ No newline at end of file diff --git a/src/main/resources/static/css/forms.css b/src/main/resources/static/css/forms.css new file mode 100644 index 0000000..137ae42 --- /dev/null +++ b/src/main/resources/static/css/forms.css @@ -0,0 +1,8 @@ +.form { + display: inline-block; + margin-top: 10px; +} + +td:not(.centerTd) { + text-align: right; +} \ No newline at end of file diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css new file mode 100644 index 0000000..ba028cc --- /dev/null +++ b/src/main/resources/static/css/main.css @@ -0,0 +1,109 @@ +body { + margin: 0; + font-family: 'Open Sans', sans-serif; +} + +h1 { + text-decoration: underline; +} + +header { + background-color: black; + height: 70px; + text-align: center; + width: 100%; +} + +header img { + float: right; +} + +section { + text-align: center; +} + +nav { + overflow: hidden; + background-color: black; +} + +nav a { + float: left; + font-size: 18px; + color: white; + text-align: center; + padding: 14px 16px; + text-decoration: none; +} + +.dropdown { + margin-top: 22px; + float: left; + overflow: hidden; +} + +.dropdown .dropbtn { + font-size: 16px; + border: none; + outline: none; + color: white; + padding: 14px 16px; + background-color: inherit; + font-family: inherit; + margin: 0; +} + +nav a:hover, .dropdown:hover .dropbtn { + background-color: #0d0d0d; +} + +.dropdown-content { + display: none; + position: absolute; + background-color: #0d0d0d; + min-width: 160px; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + z-index: 1; +} + +.dropdown-content a { + float: none; + color: white; + padding: 12px 16px; + text-decoration: none; + display: block; + text-align: left; +} + +.dropdown-content a:hover { + background-color: #1a1a1a; +} + +.dropdown:hover .dropdown-content { + display: block; +} + +.error { + color: red; +} + +.success { + color: green; +} + +.error, .success { + font-weight: bold; +} + +.mainTable { + border-spacing: 30px; +} + +.mainTableEndButtons { + text-align: center; +} + +.nosimdut td { + background-color: #ffc299; +} + diff --git a/src/main/resources/static/favicon.png b/src/main/resources/static/favicon.png new file mode 100644 index 0000000..6e80758 Binary files /dev/null and b/src/main/resources/static/favicon.png differ diff --git a/src/main/resources/static/header.html b/src/main/resources/static/header.html new file mode 100644 index 0000000..26f5b71 --- /dev/null +++ b/src/main/resources/static/header.html @@ -0,0 +1,51 @@ +

diff --git a/src/main/resources/static/js/libs/axios.min.js b/src/main/resources/static/js/libs/axios.min.js new file mode 100644 index 0000000..b927503 --- /dev/null +++ b/src/main/resources/static/js/libs/axios.min.js @@ -0,0 +1,9 @@ +/* axios v0.19.0 | (c) 2019 by Matt Zabriskie */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.axios=t():e.axios=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return e[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";function r(e){var t=new i(e),n=s(i.prototype.request,t);return o.extend(n,i.prototype,t),o.extend(n,t),n}var o=n(2),s=n(3),i=n(5),a=n(22),u=n(11),c=r(u);c.Axios=i,c.create=function(e){return r(a(c.defaults,e))},c.Cancel=n(23),c.CancelToken=n(24),c.isCancel=n(10),c.all=function(e){return Promise.all(e)},c.spread=n(25),e.exports=c,e.exports.default=c},function(e,t,n){"use strict";function r(e){return"[object Array]"===j.call(e)}function o(e){return"[object ArrayBuffer]"===j.call(e)}function s(e){return"undefined"!=typeof FormData&&e instanceof FormData}function i(e){var t;return t="undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function a(e){return"string"==typeof e}function u(e){return"number"==typeof e}function c(e){return"undefined"==typeof e}function f(e){return null!==e&&"object"==typeof e}function p(e){return"[object Date]"===j.call(e)}function d(e){return"[object File]"===j.call(e)}function l(e){return"[object Blob]"===j.call(e)}function h(e){return"[object Function]"===j.call(e)}function m(e){return f(e)&&h(e.pipe)}function y(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function g(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function x(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product&&"NativeScript"!==navigator.product&&"NS"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function v(e,t){if(null!==e&&"undefined"!=typeof e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n + * @license MIT + */ + e.exports=function(e){return null!=e&&null!=e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(6),i=n(7),a=n(8),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method=e.method?e.method.toLowerCase():"get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(o.merge(n||{},{method:e,url:t}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(o.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),s=n(9),i=n(10),a=n(11),u=n(20),c=n(21);e.exports=function(e){r(e),e.baseURL&&!u(e.url)&&(e.url=c(e.baseURL,e.url)),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";function r(e,t){!s.isUndefined(e)&&s.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)?e=n(13):"undefined"!=typeof XMLHttpRequest&&(e=n(13)),e}var s=n(2),i=n(12),a={"Content-Type":"application/x-www-form-urlencoded"},u={adapter:o(),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),s.isFormData(e)||s.isArrayBuffer(e)||s.isBuffer(e)||s.isStream(e)||s.isFile(e)||s.isBlob(e)?e:s.isArrayBufferView(e)?e.buffer:s.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):s.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){u.headers[e]={}}),s.forEach(["post","put","patch"],function(e){u.headers[e]=s.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(14),s=n(6),i=n(17),a=n(18),u=n(15);e.exports=function(e){return new Promise(function(t,c){var f=e.data,p=e.headers;r.isFormData(f)&&delete p["Content-Type"];var d=new XMLHttpRequest;if(e.auth){var l=e.auth.username||"",h=e.auth.password||"";p.Authorization="Basic "+btoa(l+":"+h)}if(d.open(e.method.toUpperCase(),s(e.url,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in d?i(d.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?d.response:d.responseText,s={data:r,status:d.status,statusText:d.statusText,headers:n,config:e,request:d};o(t,c,s),d=null}},d.onabort=function(){d&&(c(u("Request aborted",e,"ECONNABORTED",d)),d=null)},d.onerror=function(){c(u("Network Error",e,null,d)),d=null},d.ontimeout=function(){c(u("timeout of "+e.timeout+"ms exceeded",e,"ECONNABORTED",d)),d=null},r.isStandardBrowserEnv()){var m=n(19),y=(e.withCredentials||a(e.url))&&e.xsrfCookieName?m.read(e.xsrfCookieName):void 0;y&&(p[e.xsrfHeaderName]=y)}if("setRequestHeader"in d&&r.forEach(p,function(e,t){"undefined"==typeof f&&"content-type"===t.toLowerCase()?delete p[t]:d.setRequestHeader(t,e)}),e.withCredentials&&(d.withCredentials=!0),e.responseType)try{d.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&d.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&d.upload&&d.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){d&&(d.abort(),c(e),d=null)}),void 0===f&&(f=null),d.send(f)})}},function(e,t,n){"use strict";var r=n(15);e.exports=function(e,t,n){var o=n.config.validateStatus;!o||o(n.status)?e(n):t(r("Request failed with status code "+n.status,n.config,null,n.request,n))}},function(e,t,n){"use strict";var r=n(16);e.exports=function(e,t,n,o,s){var i=new Error(e);return r(i,t,n,o,s)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,s,i={};return e?(r.forEach(e.split("\n"),function(e){if(s=e.indexOf(":"),t=r.trim(e.substr(0,s)).toLowerCase(),n=r.trim(e.substr(s+1)),t){if(i[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?i[t]=(i[t]?i[t]:[]).concat([n]):i[t]=i[t]?i[t]+", "+n:n}}),i):i}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,s,i){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(s)&&a.push("domain="+s),i===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){t=t||{};var n={};return r.forEach(["url","method","params","data"],function(e){"undefined"!=typeof t[e]&&(n[e]=t[e])}),r.forEach(["headers","auth","proxy"],function(o){r.isObject(t[o])?n[o]=r.deepMerge(e[o],t[o]):"undefined"!=typeof t[o]?n[o]=t[o]:r.isObject(e[o])?n[o]=r.deepMerge(e[o]):"undefined"!=typeof e[o]&&(n[o]=e[o])}),r.forEach(["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"],function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])}),n}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])}); +//# sourceMappingURL=axios.min.map \ No newline at end of file diff --git a/src/main/resources/static/js/libs/axios.min.map b/src/main/resources/static/js/libs/axios.min.map new file mode 100644 index 0000000..9a4bbcc --- /dev/null +++ b/src/main/resources/static/js/libs/axios.min.map @@ -0,0 +1,336 @@ +{ + "version": 3, + "sources": [ + "webpack:///webpack/universalModuleDefinition", + "webpack:///axios.min.js", + "webpack:///webpack/bootstrap 3e317bb69558239a2225", + "webpack:///./index.js", + "webpack:///./lib/axios.js", + "webpack:///./lib/utils.js", + "webpack:///./lib/helpers/bind.js", + "webpack:///./~/is-buffer/index.js", + "webpack:///./lib/core/Axios.js", + "webpack:///./lib/helpers/buildURL.js", + "webpack:///./lib/core/InterceptorManager.js", + "webpack:///./lib/core/dispatchRequest.js", + "webpack:///./lib/core/transformData.js", + "webpack:///./lib/cancel/isCancel.js", + "webpack:///./lib/defaults.js", + "webpack:///./lib/helpers/normalizeHeaderName.js", + "webpack:///./lib/adapters/xhr.js", + "webpack:///./lib/core/settle.js", + "webpack:///./lib/core/createError.js", + "webpack:///./lib/core/enhanceError.js", + "webpack:///./lib/helpers/parseHeaders.js", + "webpack:///./lib/helpers/isURLSameOrigin.js", + "webpack:///./lib/helpers/cookies.js", + "webpack:///./lib/helpers/isAbsoluteURL.js", + "webpack:///./lib/helpers/combineURLs.js", + "webpack:///./lib/core/mergeConfig.js", + "webpack:///./lib/cancel/Cancel.js", + "webpack:///./lib/cancel/CancelToken.js", + "webpack:///./lib/helpers/spread.js" + ], + "names": [ + "root", + "factory", + "exports", + "module", + "define", + "amd", + "this", + "modules", + "__webpack_require__", + "moduleId", + "installedModules", + "id", + "loaded", + "call", + "m", + "c", + "p", + "createInstance", + "defaultConfig", + "context", + "Axios", + "instance", + "bind", + "prototype", + "request", + "utils", + "extend", + "mergeConfig", + "defaults", + "axios", + "create", + "instanceConfig", + "Cancel", + "CancelToken", + "isCancel", + "all", + "promises", + "Promise", + "spread", + "default", + "isArray", + "val", + "toString", + "isArrayBuffer", + "isFormData", + "FormData", + "isArrayBufferView", + "result", + "ArrayBuffer", + "isView", + "buffer", + "isString", + "isNumber", + "isUndefined", + "isObject", + "isDate", + "isFile", + "isBlob", + "isFunction", + "isStream", + "pipe", + "isURLSearchParams", + "URLSearchParams", + "trim", + "str", + "replace", + "isStandardBrowserEnv", + "navigator", + "product", + "window", + "document", + "forEach", + "obj", + "fn", + "i", + "l", + "length", + "key", + "Object", + "hasOwnProperty", + "merge", + "assignValue", + "arguments", + "deepMerge", + "a", + "b", + "thisArg", + "isBuffer", + "args", + "Array", + "apply", + "constructor", + "interceptors", + "InterceptorManager", + "response", + "buildURL", + "dispatchRequest", + "config", + "url", + "method", + "toLowerCase", + "chain", + "undefined", + "promise", + "resolve", + "interceptor", + "unshift", + "fulfilled", + "rejected", + "push", + "then", + "shift", + "getUri", + "params", + "paramsSerializer", + "data", + "encode", + "encodeURIComponent", + "serializedParams", + "parts", + "v", + "toISOString", + "JSON", + "stringify", + "join", + "hashmarkIndex", + "indexOf", + "slice", + "handlers", + "use", + "eject", + "h", + "throwIfCancellationRequested", + "cancelToken", + "throwIfRequested", + "transformData", + "isAbsoluteURL", + "combineURLs", + "baseURL", + "headers", + "transformRequest", + "common", + "adapter", + "transformResponse", + "reason", + "reject", + "fns", + "value", + "__CANCEL__", + "setContentTypeIfUnset", + "getDefaultAdapter", + "process", + "XMLHttpRequest", + "normalizeHeaderName", + "DEFAULT_CONTENT_TYPE", + "Content-Type", + "parse", + "e", + "timeout", + "xsrfCookieName", + "xsrfHeaderName", + "maxContentLength", + "validateStatus", + "status", + "Accept", + "normalizedName", + "name", + "toUpperCase", + "settle", + "parseHeaders", + "isURLSameOrigin", + "createError", + "requestData", + "requestHeaders", + "auth", + "username", + "password", + "Authorization", + "btoa", + "open", + "onreadystatechange", + "readyState", + "responseURL", + "responseHeaders", + "getAllResponseHeaders", + "responseData", + "responseType", + "responseText", + "statusText", + "onabort", + "onerror", + "ontimeout", + "cookies", + "xsrfValue", + "withCredentials", + "read", + "setRequestHeader", + "onDownloadProgress", + "addEventListener", + "onUploadProgress", + "upload", + "cancel", + "abort", + "send", + "enhanceError", + "message", + "code", + "error", + "Error", + "isAxiosError", + "toJSON", + "description", + "number", + "fileName", + "lineNumber", + "columnNumber", + "stack", + "ignoreDuplicateOf", + "parsed", + "split", + "line", + "substr", + "concat", + "resolveURL", + "href", + "msie", + "urlParsingNode", + "setAttribute", + "protocol", + "host", + "search", + "hash", + "hostname", + "port", + "pathname", + "charAt", + "originURL", + "test", + "userAgent", + "createElement", + "location", + "requestURL", + "write", + "expires", + "path", + "domain", + "secure", + "cookie", + "Date", + "toGMTString", + "match", + "RegExp", + "decodeURIComponent", + "remove", + "now", + "relativeURL", + "config1", + "config2", + "prop", + "executor", + "TypeError", + "resolvePromise", + "token", + "source", + "callback", + "arr" + ], + "mappings": "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", + "file": "axios.min.js", + "sourcesContent": [ + "(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"axios\"] = factory();\n\telse\n\t\troot[\"axios\"] = factory();\n})(this, function() {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition", + "(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"axios\"] = factory();\n\telse\n\t\troot[\"axios\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\texports: {},\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\tloaded: false\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.loaded = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\tmodule.exports = __webpack_require__(1);\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\tvar bind = __webpack_require__(3);\n\tvar Axios = __webpack_require__(5);\n\tvar mergeConfig = __webpack_require__(22);\n\tvar defaults = __webpack_require__(11);\n\t\n\t/**\n\t * Create an instance of Axios\n\t *\n\t * @param {Object} defaultConfig The default config for the instance\n\t * @return {Axios} A new instance of Axios\n\t */\n\tfunction createInstance(defaultConfig) {\n\t var context = new Axios(defaultConfig);\n\t var instance = bind(Axios.prototype.request, context);\n\t\n\t // Copy axios.prototype to instance\n\t utils.extend(instance, Axios.prototype, context);\n\t\n\t // Copy context to instance\n\t utils.extend(instance, context);\n\t\n\t return instance;\n\t}\n\t\n\t// Create the default instance to be exported\n\tvar axios = createInstance(defaults);\n\t\n\t// Expose Axios class to allow class inheritance\n\taxios.Axios = Axios;\n\t\n\t// Factory for creating new instances\n\taxios.create = function create(instanceConfig) {\n\t return createInstance(mergeConfig(axios.defaults, instanceConfig));\n\t};\n\t\n\t// Expose Cancel & CancelToken\n\taxios.Cancel = __webpack_require__(23);\n\taxios.CancelToken = __webpack_require__(24);\n\taxios.isCancel = __webpack_require__(10);\n\t\n\t// Expose all/spread\n\taxios.all = function all(promises) {\n\t return Promise.all(promises);\n\t};\n\taxios.spread = __webpack_require__(25);\n\t\n\tmodule.exports = axios;\n\t\n\t// Allow use of default import syntax in TypeScript\n\tmodule.exports.default = axios;\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar bind = __webpack_require__(3);\n\tvar isBuffer = __webpack_require__(4);\n\t\n\t/*global toString:true*/\n\t\n\t// utils is a library of generic helper functions non-specific to axios\n\t\n\tvar toString = Object.prototype.toString;\n\t\n\t/**\n\t * Determine if a value is an Array\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is an Array, otherwise false\n\t */\n\tfunction isArray(val) {\n\t return toString.call(val) === '[object Array]';\n\t}\n\t\n\t/**\n\t * Determine if a value is an ArrayBuffer\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is an ArrayBuffer, otherwise false\n\t */\n\tfunction isArrayBuffer(val) {\n\t return toString.call(val) === '[object ArrayBuffer]';\n\t}\n\t\n\t/**\n\t * Determine if a value is a FormData\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is an FormData, otherwise false\n\t */\n\tfunction isFormData(val) {\n\t return (typeof FormData !== 'undefined') && (val instanceof FormData);\n\t}\n\t\n\t/**\n\t * Determine if a value is a view on an ArrayBuffer\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false\n\t */\n\tfunction isArrayBufferView(val) {\n\t var result;\n\t if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {\n\t result = ArrayBuffer.isView(val);\n\t } else {\n\t result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer);\n\t }\n\t return result;\n\t}\n\t\n\t/**\n\t * Determine if a value is a String\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is a String, otherwise false\n\t */\n\tfunction isString(val) {\n\t return typeof val === 'string';\n\t}\n\t\n\t/**\n\t * Determine if a value is a Number\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is a Number, otherwise false\n\t */\n\tfunction isNumber(val) {\n\t return typeof val === 'number';\n\t}\n\t\n\t/**\n\t * Determine if a value is undefined\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if the value is undefined, otherwise false\n\t */\n\tfunction isUndefined(val) {\n\t return typeof val === 'undefined';\n\t}\n\t\n\t/**\n\t * Determine if a value is an Object\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is an Object, otherwise false\n\t */\n\tfunction isObject(val) {\n\t return val !== null && typeof val === 'object';\n\t}\n\t\n\t/**\n\t * Determine if a value is a Date\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is a Date, otherwise false\n\t */\n\tfunction isDate(val) {\n\t return toString.call(val) === '[object Date]';\n\t}\n\t\n\t/**\n\t * Determine if a value is a File\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is a File, otherwise false\n\t */\n\tfunction isFile(val) {\n\t return toString.call(val) === '[object File]';\n\t}\n\t\n\t/**\n\t * Determine if a value is a Blob\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is a Blob, otherwise false\n\t */\n\tfunction isBlob(val) {\n\t return toString.call(val) === '[object Blob]';\n\t}\n\t\n\t/**\n\t * Determine if a value is a Function\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is a Function, otherwise false\n\t */\n\tfunction isFunction(val) {\n\t return toString.call(val) === '[object Function]';\n\t}\n\t\n\t/**\n\t * Determine if a value is a Stream\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is a Stream, otherwise false\n\t */\n\tfunction isStream(val) {\n\t return isObject(val) && isFunction(val.pipe);\n\t}\n\t\n\t/**\n\t * Determine if a value is a URLSearchParams object\n\t *\n\t * @param {Object} val The value to test\n\t * @returns {boolean} True if value is a URLSearchParams object, otherwise false\n\t */\n\tfunction isURLSearchParams(val) {\n\t return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams;\n\t}\n\t\n\t/**\n\t * Trim excess whitespace off the beginning and end of a string\n\t *\n\t * @param {String} str The String to trim\n\t * @returns {String} The String freed of excess whitespace\n\t */\n\tfunction trim(str) {\n\t return str.replace(/^\\s*/, '').replace(/\\s*$/, '');\n\t}\n\t\n\t/**\n\t * Determine if we're running in a standard browser environment\n\t *\n\t * This allows axios to run in a web worker, and react-native.\n\t * Both environments support XMLHttpRequest, but not fully standard globals.\n\t *\n\t * web workers:\n\t * typeof window -> undefined\n\t * typeof document -> undefined\n\t *\n\t * react-native:\n\t * navigator.product -> 'ReactNative'\n\t * nativescript\n\t * navigator.product -> 'NativeScript' or 'NS'\n\t */\n\tfunction isStandardBrowserEnv() {\n\t if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||\n\t navigator.product === 'NativeScript' ||\n\t navigator.product === 'NS')) {\n\t return false;\n\t }\n\t return (\n\t typeof window !== 'undefined' &&\n\t typeof document !== 'undefined'\n\t );\n\t}\n\t\n\t/**\n\t * Iterate over an Array or an Object invoking a function for each item.\n\t *\n\t * If `obj` is an Array callback will be called passing\n\t * the value, index, and complete array for each item.\n\t *\n\t * If 'obj' is an Object callback will be called passing\n\t * the value, key, and complete object for each property.\n\t *\n\t * @param {Object|Array} obj The object to iterate\n\t * @param {Function} fn The callback to invoke for each item\n\t */\n\tfunction forEach(obj, fn) {\n\t // Don't bother if no value provided\n\t if (obj === null || typeof obj === 'undefined') {\n\t return;\n\t }\n\t\n\t // Force an array if not already something iterable\n\t if (typeof obj !== 'object') {\n\t /*eslint no-param-reassign:0*/\n\t obj = [obj];\n\t }\n\t\n\t if (isArray(obj)) {\n\t // Iterate over array values\n\t for (var i = 0, l = obj.length; i < l; i++) {\n\t fn.call(null, obj[i], i, obj);\n\t }\n\t } else {\n\t // Iterate over object keys\n\t for (var key in obj) {\n\t if (Object.prototype.hasOwnProperty.call(obj, key)) {\n\t fn.call(null, obj[key], key, obj);\n\t }\n\t }\n\t }\n\t}\n\t\n\t/**\n\t * Accepts varargs expecting each argument to be an object, then\n\t * immutably merges the properties of each object and returns result.\n\t *\n\t * When multiple objects contain the same key the later object in\n\t * the arguments list will take precedence.\n\t *\n\t * Example:\n\t *\n\t * ```js\n\t * var result = merge({foo: 123}, {foo: 456});\n\t * console.log(result.foo); // outputs 456\n\t * ```\n\t *\n\t * @param {Object} obj1 Object to merge\n\t * @returns {Object} Result of all merge properties\n\t */\n\tfunction merge(/* obj1, obj2, obj3, ... */) {\n\t var result = {};\n\t function assignValue(val, key) {\n\t if (typeof result[key] === 'object' && typeof val === 'object') {\n\t result[key] = merge(result[key], val);\n\t } else {\n\t result[key] = val;\n\t }\n\t }\n\t\n\t for (var i = 0, l = arguments.length; i < l; i++) {\n\t forEach(arguments[i], assignValue);\n\t }\n\t return result;\n\t}\n\t\n\t/**\n\t * Function equal to merge with the difference being that no reference\n\t * to original objects is kept.\n\t *\n\t * @see merge\n\t * @param {Object} obj1 Object to merge\n\t * @returns {Object} Result of all merge properties\n\t */\n\tfunction deepMerge(/* obj1, obj2, obj3, ... */) {\n\t var result = {};\n\t function assignValue(val, key) {\n\t if (typeof result[key] === 'object' && typeof val === 'object') {\n\t result[key] = deepMerge(result[key], val);\n\t } else if (typeof val === 'object') {\n\t result[key] = deepMerge({}, val);\n\t } else {\n\t result[key] = val;\n\t }\n\t }\n\t\n\t for (var i = 0, l = arguments.length; i < l; i++) {\n\t forEach(arguments[i], assignValue);\n\t }\n\t return result;\n\t}\n\t\n\t/**\n\t * Extends object a by mutably adding to it the properties of object b.\n\t *\n\t * @param {Object} a The object to be extended\n\t * @param {Object} b The object to copy properties from\n\t * @param {Object} thisArg The object to bind function to\n\t * @return {Object} The resulting value of object a\n\t */\n\tfunction extend(a, b, thisArg) {\n\t forEach(b, function assignValue(val, key) {\n\t if (thisArg && typeof val === 'function') {\n\t a[key] = bind(val, thisArg);\n\t } else {\n\t a[key] = val;\n\t }\n\t });\n\t return a;\n\t}\n\t\n\tmodule.exports = {\n\t isArray: isArray,\n\t isArrayBuffer: isArrayBuffer,\n\t isBuffer: isBuffer,\n\t isFormData: isFormData,\n\t isArrayBufferView: isArrayBufferView,\n\t isString: isString,\n\t isNumber: isNumber,\n\t isObject: isObject,\n\t isUndefined: isUndefined,\n\t isDate: isDate,\n\t isFile: isFile,\n\t isBlob: isBlob,\n\t isFunction: isFunction,\n\t isStream: isStream,\n\t isURLSearchParams: isURLSearchParams,\n\t isStandardBrowserEnv: isStandardBrowserEnv,\n\t forEach: forEach,\n\t merge: merge,\n\t deepMerge: deepMerge,\n\t extend: extend,\n\t trim: trim\n\t};\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\tmodule.exports = function bind(fn, thisArg) {\n\t return function wrap() {\n\t var args = new Array(arguments.length);\n\t for (var i = 0; i < args.length; i++) {\n\t args[i] = arguments[i];\n\t }\n\t return fn.apply(thisArg, args);\n\t };\n\t};\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports) {\n\n\t/*!\n\t * Determine if an object is a Buffer\n\t *\n\t * @author Feross Aboukhadijeh \n\t * @license MIT\n\t */\n\t\n\tmodule.exports = function isBuffer (obj) {\n\t return obj != null && obj.constructor != null &&\n\t typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)\n\t}\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\tvar buildURL = __webpack_require__(6);\n\tvar InterceptorManager = __webpack_require__(7);\n\tvar dispatchRequest = __webpack_require__(8);\n\tvar mergeConfig = __webpack_require__(22);\n\t\n\t/**\n\t * Create a new instance of Axios\n\t *\n\t * @param {Object} instanceConfig The default config for the instance\n\t */\n\tfunction Axios(instanceConfig) {\n\t this.defaults = instanceConfig;\n\t this.interceptors = {\n\t request: new InterceptorManager(),\n\t response: new InterceptorManager()\n\t };\n\t}\n\t\n\t/**\n\t * Dispatch a request\n\t *\n\t * @param {Object} config The config specific for this request (merged with this.defaults)\n\t */\n\tAxios.prototype.request = function request(config) {\n\t /*eslint no-param-reassign:0*/\n\t // Allow for axios('example/url'[, config]) a la fetch API\n\t if (typeof config === 'string') {\n\t config = arguments[1] || {};\n\t config.url = arguments[0];\n\t } else {\n\t config = config || {};\n\t }\n\t\n\t config = mergeConfig(this.defaults, config);\n\t config.method = config.method ? config.method.toLowerCase() : 'get';\n\t\n\t // Hook up interceptors middleware\n\t var chain = [dispatchRequest, undefined];\n\t var promise = Promise.resolve(config);\n\t\n\t this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {\n\t chain.unshift(interceptor.fulfilled, interceptor.rejected);\n\t });\n\t\n\t this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {\n\t chain.push(interceptor.fulfilled, interceptor.rejected);\n\t });\n\t\n\t while (chain.length) {\n\t promise = promise.then(chain.shift(), chain.shift());\n\t }\n\t\n\t return promise;\n\t};\n\t\n\tAxios.prototype.getUri = function getUri(config) {\n\t config = mergeConfig(this.defaults, config);\n\t return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\\?/, '');\n\t};\n\t\n\t// Provide aliases for supported request methods\n\tutils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {\n\t /*eslint func-names:0*/\n\t Axios.prototype[method] = function(url, config) {\n\t return this.request(utils.merge(config || {}, {\n\t method: method,\n\t url: url\n\t }));\n\t };\n\t});\n\t\n\tutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n\t /*eslint func-names:0*/\n\t Axios.prototype[method] = function(url, data, config) {\n\t return this.request(utils.merge(config || {}, {\n\t method: method,\n\t url: url,\n\t data: data\n\t }));\n\t };\n\t});\n\t\n\tmodule.exports = Axios;\n\n\n/***/ }),\n/* 6 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\t\n\tfunction encode(val) {\n\t return encodeURIComponent(val).\n\t replace(/%40/gi, '@').\n\t replace(/%3A/gi, ':').\n\t replace(/%24/g, '$').\n\t replace(/%2C/gi, ',').\n\t replace(/%20/g, '+').\n\t replace(/%5B/gi, '[').\n\t replace(/%5D/gi, ']');\n\t}\n\t\n\t/**\n\t * Build a URL by appending params to the end\n\t *\n\t * @param {string} url The base of the url (e.g., http://www.google.com)\n\t * @param {object} [params] The params to be appended\n\t * @returns {string} The formatted url\n\t */\n\tmodule.exports = function buildURL(url, params, paramsSerializer) {\n\t /*eslint no-param-reassign:0*/\n\t if (!params) {\n\t return url;\n\t }\n\t\n\t var serializedParams;\n\t if (paramsSerializer) {\n\t serializedParams = paramsSerializer(params);\n\t } else if (utils.isURLSearchParams(params)) {\n\t serializedParams = params.toString();\n\t } else {\n\t var parts = [];\n\t\n\t utils.forEach(params, function serialize(val, key) {\n\t if (val === null || typeof val === 'undefined') {\n\t return;\n\t }\n\t\n\t if (utils.isArray(val)) {\n\t key = key + '[]';\n\t } else {\n\t val = [val];\n\t }\n\t\n\t utils.forEach(val, function parseValue(v) {\n\t if (utils.isDate(v)) {\n\t v = v.toISOString();\n\t } else if (utils.isObject(v)) {\n\t v = JSON.stringify(v);\n\t }\n\t parts.push(encode(key) + '=' + encode(v));\n\t });\n\t });\n\t\n\t serializedParams = parts.join('&');\n\t }\n\t\n\t if (serializedParams) {\n\t var hashmarkIndex = url.indexOf('#');\n\t if (hashmarkIndex !== -1) {\n\t url = url.slice(0, hashmarkIndex);\n\t }\n\t\n\t url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;\n\t }\n\t\n\t return url;\n\t};\n\n\n/***/ }),\n/* 7 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\t\n\tfunction InterceptorManager() {\n\t this.handlers = [];\n\t}\n\t\n\t/**\n\t * Add a new interceptor to the stack\n\t *\n\t * @param {Function} fulfilled The function to handle `then` for a `Promise`\n\t * @param {Function} rejected The function to handle `reject` for a `Promise`\n\t *\n\t * @return {Number} An ID used to remove interceptor later\n\t */\n\tInterceptorManager.prototype.use = function use(fulfilled, rejected) {\n\t this.handlers.push({\n\t fulfilled: fulfilled,\n\t rejected: rejected\n\t });\n\t return this.handlers.length - 1;\n\t};\n\t\n\t/**\n\t * Remove an interceptor from the stack\n\t *\n\t * @param {Number} id The ID that was returned by `use`\n\t */\n\tInterceptorManager.prototype.eject = function eject(id) {\n\t if (this.handlers[id]) {\n\t this.handlers[id] = null;\n\t }\n\t};\n\t\n\t/**\n\t * Iterate over all the registered interceptors\n\t *\n\t * This method is particularly useful for skipping over any\n\t * interceptors that may have become `null` calling `eject`.\n\t *\n\t * @param {Function} fn The function to call for each interceptor\n\t */\n\tInterceptorManager.prototype.forEach = function forEach(fn) {\n\t utils.forEach(this.handlers, function forEachHandler(h) {\n\t if (h !== null) {\n\t fn(h);\n\t }\n\t });\n\t};\n\t\n\tmodule.exports = InterceptorManager;\n\n\n/***/ }),\n/* 8 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\tvar transformData = __webpack_require__(9);\n\tvar isCancel = __webpack_require__(10);\n\tvar defaults = __webpack_require__(11);\n\tvar isAbsoluteURL = __webpack_require__(20);\n\tvar combineURLs = __webpack_require__(21);\n\t\n\t/**\n\t * Throws a `Cancel` if cancellation has been requested.\n\t */\n\tfunction throwIfCancellationRequested(config) {\n\t if (config.cancelToken) {\n\t config.cancelToken.throwIfRequested();\n\t }\n\t}\n\t\n\t/**\n\t * Dispatch a request to the server using the configured adapter.\n\t *\n\t * @param {object} config The config that is to be used for the request\n\t * @returns {Promise} The Promise to be fulfilled\n\t */\n\tmodule.exports = function dispatchRequest(config) {\n\t throwIfCancellationRequested(config);\n\t\n\t // Support baseURL config\n\t if (config.baseURL && !isAbsoluteURL(config.url)) {\n\t config.url = combineURLs(config.baseURL, config.url);\n\t }\n\t\n\t // Ensure headers exist\n\t config.headers = config.headers || {};\n\t\n\t // Transform request data\n\t config.data = transformData(\n\t config.data,\n\t config.headers,\n\t config.transformRequest\n\t );\n\t\n\t // Flatten headers\n\t config.headers = utils.merge(\n\t config.headers.common || {},\n\t config.headers[config.method] || {},\n\t config.headers || {}\n\t );\n\t\n\t utils.forEach(\n\t ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],\n\t function cleanHeaderConfig(method) {\n\t delete config.headers[method];\n\t }\n\t );\n\t\n\t var adapter = config.adapter || defaults.adapter;\n\t\n\t return adapter(config).then(function onAdapterResolution(response) {\n\t throwIfCancellationRequested(config);\n\t\n\t // Transform response data\n\t response.data = transformData(\n\t response.data,\n\t response.headers,\n\t config.transformResponse\n\t );\n\t\n\t return response;\n\t }, function onAdapterRejection(reason) {\n\t if (!isCancel(reason)) {\n\t throwIfCancellationRequested(config);\n\t\n\t // Transform response data\n\t if (reason && reason.response) {\n\t reason.response.data = transformData(\n\t reason.response.data,\n\t reason.response.headers,\n\t config.transformResponse\n\t );\n\t }\n\t }\n\t\n\t return Promise.reject(reason);\n\t });\n\t};\n\n\n/***/ }),\n/* 9 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\t\n\t/**\n\t * Transform the data for a request or a response\n\t *\n\t * @param {Object|String} data The data to be transformed\n\t * @param {Array} headers The headers for the request or response\n\t * @param {Array|Function} fns A single function or Array of functions\n\t * @returns {*} The resulting transformed data\n\t */\n\tmodule.exports = function transformData(data, headers, fns) {\n\t /*eslint no-param-reassign:0*/\n\t utils.forEach(fns, function transform(fn) {\n\t data = fn(data, headers);\n\t });\n\t\n\t return data;\n\t};\n\n\n/***/ }),\n/* 10 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\tmodule.exports = function isCancel(value) {\n\t return !!(value && value.__CANCEL__);\n\t};\n\n\n/***/ }),\n/* 11 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\tvar normalizeHeaderName = __webpack_require__(12);\n\t\n\tvar DEFAULT_CONTENT_TYPE = {\n\t 'Content-Type': 'application/x-www-form-urlencoded'\n\t};\n\t\n\tfunction setContentTypeIfUnset(headers, value) {\n\t if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {\n\t headers['Content-Type'] = value;\n\t }\n\t}\n\t\n\tfunction getDefaultAdapter() {\n\t var adapter;\n\t // Only Node.JS has a process variable that is of [[Class]] process\n\t if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {\n\t // For node use HTTP adapter\n\t adapter = __webpack_require__(13);\n\t } else if (typeof XMLHttpRequest !== 'undefined') {\n\t // For browsers use XHR adapter\n\t adapter = __webpack_require__(13);\n\t }\n\t return adapter;\n\t}\n\t\n\tvar defaults = {\n\t adapter: getDefaultAdapter(),\n\t\n\t transformRequest: [function transformRequest(data, headers) {\n\t normalizeHeaderName(headers, 'Accept');\n\t normalizeHeaderName(headers, 'Content-Type');\n\t if (utils.isFormData(data) ||\n\t utils.isArrayBuffer(data) ||\n\t utils.isBuffer(data) ||\n\t utils.isStream(data) ||\n\t utils.isFile(data) ||\n\t utils.isBlob(data)\n\t ) {\n\t return data;\n\t }\n\t if (utils.isArrayBufferView(data)) {\n\t return data.buffer;\n\t }\n\t if (utils.isURLSearchParams(data)) {\n\t setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');\n\t return data.toString();\n\t }\n\t if (utils.isObject(data)) {\n\t setContentTypeIfUnset(headers, 'application/json;charset=utf-8');\n\t return JSON.stringify(data);\n\t }\n\t return data;\n\t }],\n\t\n\t transformResponse: [function transformResponse(data) {\n\t /*eslint no-param-reassign:0*/\n\t if (typeof data === 'string') {\n\t try {\n\t data = JSON.parse(data);\n\t } catch (e) { /* Ignore */ }\n\t }\n\t return data;\n\t }],\n\t\n\t /**\n\t * A timeout in milliseconds to abort a request. If set to 0 (default) a\n\t * timeout is not created.\n\t */\n\t timeout: 0,\n\t\n\t xsrfCookieName: 'XSRF-TOKEN',\n\t xsrfHeaderName: 'X-XSRF-TOKEN',\n\t\n\t maxContentLength: -1,\n\t\n\t validateStatus: function validateStatus(status) {\n\t return status >= 200 && status < 300;\n\t }\n\t};\n\t\n\tdefaults.headers = {\n\t common: {\n\t 'Accept': 'application/json, text/plain, */*'\n\t }\n\t};\n\t\n\tutils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {\n\t defaults.headers[method] = {};\n\t});\n\t\n\tutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n\t defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);\n\t});\n\t\n\tmodule.exports = defaults;\n\n\n/***/ }),\n/* 12 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\t\n\tmodule.exports = function normalizeHeaderName(headers, normalizedName) {\n\t utils.forEach(headers, function processHeader(value, name) {\n\t if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {\n\t headers[normalizedName] = value;\n\t delete headers[name];\n\t }\n\t });\n\t};\n\n\n/***/ }),\n/* 13 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\tvar settle = __webpack_require__(14);\n\tvar buildURL = __webpack_require__(6);\n\tvar parseHeaders = __webpack_require__(17);\n\tvar isURLSameOrigin = __webpack_require__(18);\n\tvar createError = __webpack_require__(15);\n\t\n\tmodule.exports = function xhrAdapter(config) {\n\t return new Promise(function dispatchXhrRequest(resolve, reject) {\n\t var requestData = config.data;\n\t var requestHeaders = config.headers;\n\t\n\t if (utils.isFormData(requestData)) {\n\t delete requestHeaders['Content-Type']; // Let the browser set it\n\t }\n\t\n\t var request = new XMLHttpRequest();\n\t\n\t // HTTP basic authentication\n\t if (config.auth) {\n\t var username = config.auth.username || '';\n\t var password = config.auth.password || '';\n\t requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);\n\t }\n\t\n\t request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);\n\t\n\t // Set the request timeout in MS\n\t request.timeout = config.timeout;\n\t\n\t // Listen for ready state\n\t request.onreadystatechange = function handleLoad() {\n\t if (!request || request.readyState !== 4) {\n\t return;\n\t }\n\t\n\t // The request errored out and we didn't get a response, this will be\n\t // handled by onerror instead\n\t // With one exception: request that using file: protocol, most browsers\n\t // will return status as 0 even though it's a successful request\n\t if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {\n\t return;\n\t }\n\t\n\t // Prepare the response\n\t var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;\n\t var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;\n\t var response = {\n\t data: responseData,\n\t status: request.status,\n\t statusText: request.statusText,\n\t headers: responseHeaders,\n\t config: config,\n\t request: request\n\t };\n\t\n\t settle(resolve, reject, response);\n\t\n\t // Clean up request\n\t request = null;\n\t };\n\t\n\t // Handle browser request cancellation (as opposed to a manual cancellation)\n\t request.onabort = function handleAbort() {\n\t if (!request) {\n\t return;\n\t }\n\t\n\t reject(createError('Request aborted', config, 'ECONNABORTED', request));\n\t\n\t // Clean up request\n\t request = null;\n\t };\n\t\n\t // Handle low level network errors\n\t request.onerror = function handleError() {\n\t // Real errors are hidden from us by the browser\n\t // onerror should only fire if it's a network error\n\t reject(createError('Network Error', config, null, request));\n\t\n\t // Clean up request\n\t request = null;\n\t };\n\t\n\t // Handle timeout\n\t request.ontimeout = function handleTimeout() {\n\t reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',\n\t request));\n\t\n\t // Clean up request\n\t request = null;\n\t };\n\t\n\t // Add xsrf header\n\t // This is only done if running in a standard browser environment.\n\t // Specifically not if we're in a web worker, or react-native.\n\t if (utils.isStandardBrowserEnv()) {\n\t var cookies = __webpack_require__(19);\n\t\n\t // Add xsrf header\n\t var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?\n\t cookies.read(config.xsrfCookieName) :\n\t undefined;\n\t\n\t if (xsrfValue) {\n\t requestHeaders[config.xsrfHeaderName] = xsrfValue;\n\t }\n\t }\n\t\n\t // Add headers to the request\n\t if ('setRequestHeader' in request) {\n\t utils.forEach(requestHeaders, function setRequestHeader(val, key) {\n\t if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {\n\t // Remove Content-Type if data is undefined\n\t delete requestHeaders[key];\n\t } else {\n\t // Otherwise add header to the request\n\t request.setRequestHeader(key, val);\n\t }\n\t });\n\t }\n\t\n\t // Add withCredentials to request if needed\n\t if (config.withCredentials) {\n\t request.withCredentials = true;\n\t }\n\t\n\t // Add responseType to request if needed\n\t if (config.responseType) {\n\t try {\n\t request.responseType = config.responseType;\n\t } catch (e) {\n\t // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.\n\t // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.\n\t if (config.responseType !== 'json') {\n\t throw e;\n\t }\n\t }\n\t }\n\t\n\t // Handle progress if needed\n\t if (typeof config.onDownloadProgress === 'function') {\n\t request.addEventListener('progress', config.onDownloadProgress);\n\t }\n\t\n\t // Not all browsers support upload events\n\t if (typeof config.onUploadProgress === 'function' && request.upload) {\n\t request.upload.addEventListener('progress', config.onUploadProgress);\n\t }\n\t\n\t if (config.cancelToken) {\n\t // Handle cancellation\n\t config.cancelToken.promise.then(function onCanceled(cancel) {\n\t if (!request) {\n\t return;\n\t }\n\t\n\t request.abort();\n\t reject(cancel);\n\t // Clean up request\n\t request = null;\n\t });\n\t }\n\t\n\t if (requestData === undefined) {\n\t requestData = null;\n\t }\n\t\n\t // Send the request\n\t request.send(requestData);\n\t });\n\t};\n\n\n/***/ }),\n/* 14 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar createError = __webpack_require__(15);\n\t\n\t/**\n\t * Resolve or reject a Promise based on response status.\n\t *\n\t * @param {Function} resolve A function that resolves the promise.\n\t * @param {Function} reject A function that rejects the promise.\n\t * @param {object} response The response.\n\t */\n\tmodule.exports = function settle(resolve, reject, response) {\n\t var validateStatus = response.config.validateStatus;\n\t if (!validateStatus || validateStatus(response.status)) {\n\t resolve(response);\n\t } else {\n\t reject(createError(\n\t 'Request failed with status code ' + response.status,\n\t response.config,\n\t null,\n\t response.request,\n\t response\n\t ));\n\t }\n\t};\n\n\n/***/ }),\n/* 15 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar enhanceError = __webpack_require__(16);\n\t\n\t/**\n\t * Create an Error with the specified message, config, error code, request and response.\n\t *\n\t * @param {string} message The error message.\n\t * @param {Object} config The config.\n\t * @param {string} [code] The error code (for example, 'ECONNABORTED').\n\t * @param {Object} [request] The request.\n\t * @param {Object} [response] The response.\n\t * @returns {Error} The created error.\n\t */\n\tmodule.exports = function createError(message, config, code, request, response) {\n\t var error = new Error(message);\n\t return enhanceError(error, config, code, request, response);\n\t};\n\n\n/***/ }),\n/* 16 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Update an Error with the specified config, error code, and response.\n\t *\n\t * @param {Error} error The error to update.\n\t * @param {Object} config The config.\n\t * @param {string} [code] The error code (for example, 'ECONNABORTED').\n\t * @param {Object} [request] The request.\n\t * @param {Object} [response] The response.\n\t * @returns {Error} The error.\n\t */\n\tmodule.exports = function enhanceError(error, config, code, request, response) {\n\t error.config = config;\n\t if (code) {\n\t error.code = code;\n\t }\n\t\n\t error.request = request;\n\t error.response = response;\n\t error.isAxiosError = true;\n\t\n\t error.toJSON = function() {\n\t return {\n\t // Standard\n\t message: this.message,\n\t name: this.name,\n\t // Microsoft\n\t description: this.description,\n\t number: this.number,\n\t // Mozilla\n\t fileName: this.fileName,\n\t lineNumber: this.lineNumber,\n\t columnNumber: this.columnNumber,\n\t stack: this.stack,\n\t // Axios\n\t config: this.config,\n\t code: this.code\n\t };\n\t };\n\t return error;\n\t};\n\n\n/***/ }),\n/* 17 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\t\n\t// Headers whose duplicates are ignored by node\n\t// c.f. https://nodejs.org/api/http.html#http_message_headers\n\tvar ignoreDuplicateOf = [\n\t 'age', 'authorization', 'content-length', 'content-type', 'etag',\n\t 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',\n\t 'last-modified', 'location', 'max-forwards', 'proxy-authorization',\n\t 'referer', 'retry-after', 'user-agent'\n\t];\n\t\n\t/**\n\t * Parse headers into an object\n\t *\n\t * ```\n\t * Date: Wed, 27 Aug 2014 08:58:49 GMT\n\t * Content-Type: application/json\n\t * Connection: keep-alive\n\t * Transfer-Encoding: chunked\n\t * ```\n\t *\n\t * @param {String} headers Headers needing to be parsed\n\t * @returns {Object} Headers parsed into an object\n\t */\n\tmodule.exports = function parseHeaders(headers) {\n\t var parsed = {};\n\t var key;\n\t var val;\n\t var i;\n\t\n\t if (!headers) { return parsed; }\n\t\n\t utils.forEach(headers.split('\\n'), function parser(line) {\n\t i = line.indexOf(':');\n\t key = utils.trim(line.substr(0, i)).toLowerCase();\n\t val = utils.trim(line.substr(i + 1));\n\t\n\t if (key) {\n\t if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {\n\t return;\n\t }\n\t if (key === 'set-cookie') {\n\t parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);\n\t } else {\n\t parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;\n\t }\n\t }\n\t });\n\t\n\t return parsed;\n\t};\n\n\n/***/ }),\n/* 18 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\t\n\tmodule.exports = (\n\t utils.isStandardBrowserEnv() ?\n\t\n\t // Standard browser envs have full support of the APIs needed to test\n\t // whether the request URL is of the same origin as current location.\n\t (function standardBrowserEnv() {\n\t var msie = /(msie|trident)/i.test(navigator.userAgent);\n\t var urlParsingNode = document.createElement('a');\n\t var originURL;\n\t\n\t /**\n\t * Parse a URL to discover it's components\n\t *\n\t * @param {String} url The URL to be parsed\n\t * @returns {Object}\n\t */\n\t function resolveURL(url) {\n\t var href = url;\n\t\n\t if (msie) {\n\t // IE needs attribute set twice to normalize properties\n\t urlParsingNode.setAttribute('href', href);\n\t href = urlParsingNode.href;\n\t }\n\t\n\t urlParsingNode.setAttribute('href', href);\n\t\n\t // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils\n\t return {\n\t href: urlParsingNode.href,\n\t protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',\n\t host: urlParsingNode.host,\n\t search: urlParsingNode.search ? urlParsingNode.search.replace(/^\\?/, '') : '',\n\t hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',\n\t hostname: urlParsingNode.hostname,\n\t port: urlParsingNode.port,\n\t pathname: (urlParsingNode.pathname.charAt(0) === '/') ?\n\t urlParsingNode.pathname :\n\t '/' + urlParsingNode.pathname\n\t };\n\t }\n\t\n\t originURL = resolveURL(window.location.href);\n\t\n\t /**\n\t * Determine if a URL shares the same origin as the current location\n\t *\n\t * @param {String} requestURL The URL to test\n\t * @returns {boolean} True if URL shares the same origin, otherwise false\n\t */\n\t return function isURLSameOrigin(requestURL) {\n\t var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;\n\t return (parsed.protocol === originURL.protocol &&\n\t parsed.host === originURL.host);\n\t };\n\t })() :\n\t\n\t // Non standard browser envs (web workers, react-native) lack needed support.\n\t (function nonStandardBrowserEnv() {\n\t return function isURLSameOrigin() {\n\t return true;\n\t };\n\t })()\n\t);\n\n\n/***/ }),\n/* 19 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\t\n\tmodule.exports = (\n\t utils.isStandardBrowserEnv() ?\n\t\n\t // Standard browser envs support document.cookie\n\t (function standardBrowserEnv() {\n\t return {\n\t write: function write(name, value, expires, path, domain, secure) {\n\t var cookie = [];\n\t cookie.push(name + '=' + encodeURIComponent(value));\n\t\n\t if (utils.isNumber(expires)) {\n\t cookie.push('expires=' + new Date(expires).toGMTString());\n\t }\n\t\n\t if (utils.isString(path)) {\n\t cookie.push('path=' + path);\n\t }\n\t\n\t if (utils.isString(domain)) {\n\t cookie.push('domain=' + domain);\n\t }\n\t\n\t if (secure === true) {\n\t cookie.push('secure');\n\t }\n\t\n\t document.cookie = cookie.join('; ');\n\t },\n\t\n\t read: function read(name) {\n\t var match = document.cookie.match(new RegExp('(^|;\\\\s*)(' + name + ')=([^;]*)'));\n\t return (match ? decodeURIComponent(match[3]) : null);\n\t },\n\t\n\t remove: function remove(name) {\n\t this.write(name, '', Date.now() - 86400000);\n\t }\n\t };\n\t })() :\n\t\n\t // Non standard browser env (web workers, react-native) lack needed support.\n\t (function nonStandardBrowserEnv() {\n\t return {\n\t write: function write() {},\n\t read: function read() { return null; },\n\t remove: function remove() {}\n\t };\n\t })()\n\t);\n\n\n/***/ }),\n/* 20 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Determines whether the specified URL is absolute\n\t *\n\t * @param {string} url The URL to test\n\t * @returns {boolean} True if the specified URL is absolute, otherwise false\n\t */\n\tmodule.exports = function isAbsoluteURL(url) {\n\t // A URL is considered absolute if it begins with \"://\" or \"//\" (protocol-relative URL).\n\t // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed\n\t // by any combination of letters, digits, plus, period, or hyphen.\n\t return /^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(url);\n\t};\n\n\n/***/ }),\n/* 21 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Creates a new URL by combining the specified URLs\n\t *\n\t * @param {string} baseURL The base URL\n\t * @param {string} relativeURL The relative URL\n\t * @returns {string} The combined URL\n\t */\n\tmodule.exports = function combineURLs(baseURL, relativeURL) {\n\t return relativeURL\n\t ? baseURL.replace(/\\/+$/, '') + '/' + relativeURL.replace(/^\\/+/, '')\n\t : baseURL;\n\t};\n\n\n/***/ }),\n/* 22 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar utils = __webpack_require__(2);\n\t\n\t/**\n\t * Config-specific merge-function which creates a new config-object\n\t * by merging two configuration objects together.\n\t *\n\t * @param {Object} config1\n\t * @param {Object} config2\n\t * @returns {Object} New object resulting from merging config2 to config1\n\t */\n\tmodule.exports = function mergeConfig(config1, config2) {\n\t // eslint-disable-next-line no-param-reassign\n\t config2 = config2 || {};\n\t var config = {};\n\t\n\t utils.forEach(['url', 'method', 'params', 'data'], function valueFromConfig2(prop) {\n\t if (typeof config2[prop] !== 'undefined') {\n\t config[prop] = config2[prop];\n\t }\n\t });\n\t\n\t utils.forEach(['headers', 'auth', 'proxy'], function mergeDeepProperties(prop) {\n\t if (utils.isObject(config2[prop])) {\n\t config[prop] = utils.deepMerge(config1[prop], config2[prop]);\n\t } else if (typeof config2[prop] !== 'undefined') {\n\t config[prop] = config2[prop];\n\t } else if (utils.isObject(config1[prop])) {\n\t config[prop] = utils.deepMerge(config1[prop]);\n\t } else if (typeof config1[prop] !== 'undefined') {\n\t config[prop] = config1[prop];\n\t }\n\t });\n\t\n\t utils.forEach([\n\t 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',\n\t 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',\n\t 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'maxContentLength',\n\t 'validateStatus', 'maxRedirects', 'httpAgent', 'httpsAgent', 'cancelToken',\n\t 'socketPath'\n\t ], function defaultToConfig2(prop) {\n\t if (typeof config2[prop] !== 'undefined') {\n\t config[prop] = config2[prop];\n\t } else if (typeof config1[prop] !== 'undefined') {\n\t config[prop] = config1[prop];\n\t }\n\t });\n\t\n\t return config;\n\t};\n\n\n/***/ }),\n/* 23 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * A `Cancel` is an object that is thrown when an operation is canceled.\n\t *\n\t * @class\n\t * @param {string=} message The message.\n\t */\n\tfunction Cancel(message) {\n\t this.message = message;\n\t}\n\t\n\tCancel.prototype.toString = function toString() {\n\t return 'Cancel' + (this.message ? ': ' + this.message : '');\n\t};\n\t\n\tCancel.prototype.__CANCEL__ = true;\n\t\n\tmodule.exports = Cancel;\n\n\n/***/ }),\n/* 24 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\tvar Cancel = __webpack_require__(23);\n\t\n\t/**\n\t * A `CancelToken` is an object that can be used to request cancellation of an operation.\n\t *\n\t * @class\n\t * @param {Function} executor The executor function.\n\t */\n\tfunction CancelToken(executor) {\n\t if (typeof executor !== 'function') {\n\t throw new TypeError('executor must be a function.');\n\t }\n\t\n\t var resolvePromise;\n\t this.promise = new Promise(function promiseExecutor(resolve) {\n\t resolvePromise = resolve;\n\t });\n\t\n\t var token = this;\n\t executor(function cancel(message) {\n\t if (token.reason) {\n\t // Cancellation has already been requested\n\t return;\n\t }\n\t\n\t token.reason = new Cancel(message);\n\t resolvePromise(token.reason);\n\t });\n\t}\n\t\n\t/**\n\t * Throws a `Cancel` if cancellation has been requested.\n\t */\n\tCancelToken.prototype.throwIfRequested = function throwIfRequested() {\n\t if (this.reason) {\n\t throw this.reason;\n\t }\n\t};\n\t\n\t/**\n\t * Returns an object that contains a new `CancelToken` and a function that, when called,\n\t * cancels the `CancelToken`.\n\t */\n\tCancelToken.source = function source() {\n\t var cancel;\n\t var token = new CancelToken(function executor(c) {\n\t cancel = c;\n\t });\n\t return {\n\t token: token,\n\t cancel: cancel\n\t };\n\t};\n\t\n\tmodule.exports = CancelToken;\n\n\n/***/ }),\n/* 25 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Syntactic sugar for invoking a function and expanding an array for arguments.\n\t *\n\t * Common use case would be to use `Function.prototype.apply`.\n\t *\n\t * ```js\n\t * function f(x, y, z) {}\n\t * var args = [1, 2, 3];\n\t * f.apply(null, args);\n\t * ```\n\t *\n\t * With `spread` this example can be re-written.\n\t *\n\t * ```js\n\t * spread(function(x, y, z) {})([1, 2, 3]);\n\t * ```\n\t *\n\t * @param {Function} callback\n\t * @returns {Function}\n\t */\n\tmodule.exports = function spread(callback) {\n\t return function wrap(arr) {\n\t return callback.apply(null, arr);\n\t };\n\t};\n\n\n/***/ })\n/******/ ])\n});\n;\n\n\n// WEBPACK FOOTER //\n// axios.min.js", + " \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 3e317bb69558239a2225", + "module.exports = require('./lib/axios');\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./index.js\n// module id = 0\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./utils');\nvar bind = require('./helpers/bind');\nvar Axios = require('./core/Axios');\nvar mergeConfig = require('./core/mergeConfig');\nvar defaults = require('./defaults');\n\n/**\n * Create an instance of Axios\n *\n * @param {Object} defaultConfig The default config for the instance\n * @return {Axios} A new instance of Axios\n */\nfunction createInstance(defaultConfig) {\n var context = new Axios(defaultConfig);\n var instance = bind(Axios.prototype.request, context);\n\n // Copy axios.prototype to instance\n utils.extend(instance, Axios.prototype, context);\n\n // Copy context to instance\n utils.extend(instance, context);\n\n return instance;\n}\n\n// Create the default instance to be exported\nvar axios = createInstance(defaults);\n\n// Expose Axios class to allow class inheritance\naxios.Axios = Axios;\n\n// Factory for creating new instances\naxios.create = function create(instanceConfig) {\n return createInstance(mergeConfig(axios.defaults, instanceConfig));\n};\n\n// Expose Cancel & CancelToken\naxios.Cancel = require('./cancel/Cancel');\naxios.CancelToken = require('./cancel/CancelToken');\naxios.isCancel = require('./cancel/isCancel');\n\n// Expose all/spread\naxios.all = function all(promises) {\n return Promise.all(promises);\n};\naxios.spread = require('./helpers/spread');\n\nmodule.exports = axios;\n\n// Allow use of default import syntax in TypeScript\nmodule.exports.default = axios;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/axios.js\n// module id = 1\n// module chunks = 0", + "'use strict';\n\nvar bind = require('./helpers/bind');\nvar isBuffer = require('is-buffer');\n\n/*global toString:true*/\n\n// utils is a library of generic helper functions non-specific to axios\n\nvar toString = Object.prototype.toString;\n\n/**\n * Determine if a value is an Array\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is an Array, otherwise false\n */\nfunction isArray(val) {\n return toString.call(val) === '[object Array]';\n}\n\n/**\n * Determine if a value is an ArrayBuffer\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is an ArrayBuffer, otherwise false\n */\nfunction isArrayBuffer(val) {\n return toString.call(val) === '[object ArrayBuffer]';\n}\n\n/**\n * Determine if a value is a FormData\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is an FormData, otherwise false\n */\nfunction isFormData(val) {\n return (typeof FormData !== 'undefined') && (val instanceof FormData);\n}\n\n/**\n * Determine if a value is a view on an ArrayBuffer\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false\n */\nfunction isArrayBufferView(val) {\n var result;\n if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) {\n result = ArrayBuffer.isView(val);\n } else {\n result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer);\n }\n return result;\n}\n\n/**\n * Determine if a value is a String\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a String, otherwise false\n */\nfunction isString(val) {\n return typeof val === 'string';\n}\n\n/**\n * Determine if a value is a Number\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Number, otherwise false\n */\nfunction isNumber(val) {\n return typeof val === 'number';\n}\n\n/**\n * Determine if a value is undefined\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if the value is undefined, otherwise false\n */\nfunction isUndefined(val) {\n return typeof val === 'undefined';\n}\n\n/**\n * Determine if a value is an Object\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is an Object, otherwise false\n */\nfunction isObject(val) {\n return val !== null && typeof val === 'object';\n}\n\n/**\n * Determine if a value is a Date\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Date, otherwise false\n */\nfunction isDate(val) {\n return toString.call(val) === '[object Date]';\n}\n\n/**\n * Determine if a value is a File\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a File, otherwise false\n */\nfunction isFile(val) {\n return toString.call(val) === '[object File]';\n}\n\n/**\n * Determine if a value is a Blob\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Blob, otherwise false\n */\nfunction isBlob(val) {\n return toString.call(val) === '[object Blob]';\n}\n\n/**\n * Determine if a value is a Function\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Function, otherwise false\n */\nfunction isFunction(val) {\n return toString.call(val) === '[object Function]';\n}\n\n/**\n * Determine if a value is a Stream\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a Stream, otherwise false\n */\nfunction isStream(val) {\n return isObject(val) && isFunction(val.pipe);\n}\n\n/**\n * Determine if a value is a URLSearchParams object\n *\n * @param {Object} val The value to test\n * @returns {boolean} True if value is a URLSearchParams object, otherwise false\n */\nfunction isURLSearchParams(val) {\n return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams;\n}\n\n/**\n * Trim excess whitespace off the beginning and end of a string\n *\n * @param {String} str The String to trim\n * @returns {String} The String freed of excess whitespace\n */\nfunction trim(str) {\n return str.replace(/^\\s*/, '').replace(/\\s*$/, '');\n}\n\n/**\n * Determine if we're running in a standard browser environment\n *\n * This allows axios to run in a web worker, and react-native.\n * Both environments support XMLHttpRequest, but not fully standard globals.\n *\n * web workers:\n * typeof window -> undefined\n * typeof document -> undefined\n *\n * react-native:\n * navigator.product -> 'ReactNative'\n * nativescript\n * navigator.product -> 'NativeScript' or 'NS'\n */\nfunction isStandardBrowserEnv() {\n if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||\n navigator.product === 'NativeScript' ||\n navigator.product === 'NS')) {\n return false;\n }\n return (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined'\n );\n}\n\n/**\n * Iterate over an Array or an Object invoking a function for each item.\n *\n * If `obj` is an Array callback will be called passing\n * the value, index, and complete array for each item.\n *\n * If 'obj' is an Object callback will be called passing\n * the value, key, and complete object for each property.\n *\n * @param {Object|Array} obj The object to iterate\n * @param {Function} fn The callback to invoke for each item\n */\nfunction forEach(obj, fn) {\n // Don't bother if no value provided\n if (obj === null || typeof obj === 'undefined') {\n return;\n }\n\n // Force an array if not already something iterable\n if (typeof obj !== 'object') {\n /*eslint no-param-reassign:0*/\n obj = [obj];\n }\n\n if (isArray(obj)) {\n // Iterate over array values\n for (var i = 0, l = obj.length; i < l; i++) {\n fn.call(null, obj[i], i, obj);\n }\n } else {\n // Iterate over object keys\n for (var key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n fn.call(null, obj[key], key, obj);\n }\n }\n }\n}\n\n/**\n * Accepts varargs expecting each argument to be an object, then\n * immutably merges the properties of each object and returns result.\n *\n * When multiple objects contain the same key the later object in\n * the arguments list will take precedence.\n *\n * Example:\n *\n * ```js\n * var result = merge({foo: 123}, {foo: 456});\n * console.log(result.foo); // outputs 456\n * ```\n *\n * @param {Object} obj1 Object to merge\n * @returns {Object} Result of all merge properties\n */\nfunction merge(/* obj1, obj2, obj3, ... */) {\n var result = {};\n function assignValue(val, key) {\n if (typeof result[key] === 'object' && typeof val === 'object') {\n result[key] = merge(result[key], val);\n } else {\n result[key] = val;\n }\n }\n\n for (var i = 0, l = arguments.length; i < l; i++) {\n forEach(arguments[i], assignValue);\n }\n return result;\n}\n\n/**\n * Function equal to merge with the difference being that no reference\n * to original objects is kept.\n *\n * @see merge\n * @param {Object} obj1 Object to merge\n * @returns {Object} Result of all merge properties\n */\nfunction deepMerge(/* obj1, obj2, obj3, ... */) {\n var result = {};\n function assignValue(val, key) {\n if (typeof result[key] === 'object' && typeof val === 'object') {\n result[key] = deepMerge(result[key], val);\n } else if (typeof val === 'object') {\n result[key] = deepMerge({}, val);\n } else {\n result[key] = val;\n }\n }\n\n for (var i = 0, l = arguments.length; i < l; i++) {\n forEach(arguments[i], assignValue);\n }\n return result;\n}\n\n/**\n * Extends object a by mutably adding to it the properties of object b.\n *\n * @param {Object} a The object to be extended\n * @param {Object} b The object to copy properties from\n * @param {Object} thisArg The object to bind function to\n * @return {Object} The resulting value of object a\n */\nfunction extend(a, b, thisArg) {\n forEach(b, function assignValue(val, key) {\n if (thisArg && typeof val === 'function') {\n a[key] = bind(val, thisArg);\n } else {\n a[key] = val;\n }\n });\n return a;\n}\n\nmodule.exports = {\n isArray: isArray,\n isArrayBuffer: isArrayBuffer,\n isBuffer: isBuffer,\n isFormData: isFormData,\n isArrayBufferView: isArrayBufferView,\n isString: isString,\n isNumber: isNumber,\n isObject: isObject,\n isUndefined: isUndefined,\n isDate: isDate,\n isFile: isFile,\n isBlob: isBlob,\n isFunction: isFunction,\n isStream: isStream,\n isURLSearchParams: isURLSearchParams,\n isStandardBrowserEnv: isStandardBrowserEnv,\n forEach: forEach,\n merge: merge,\n deepMerge: deepMerge,\n extend: extend,\n trim: trim\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/utils.js\n// module id = 2\n// module chunks = 0", + "'use strict';\n\nmodule.exports = function bind(fn, thisArg) {\n return function wrap() {\n var args = new Array(arguments.length);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i];\n }\n return fn.apply(thisArg, args);\n };\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/helpers/bind.js\n// module id = 3\n// module chunks = 0", + "/*!\n * Determine if an object is a Buffer\n *\n * @author Feross Aboukhadijeh \n * @license MIT\n */\n\nmodule.exports = function isBuffer (obj) {\n return obj != null && obj.constructor != null &&\n typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/is-buffer/index.js\n// module id = 4\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./../utils');\nvar buildURL = require('../helpers/buildURL');\nvar InterceptorManager = require('./InterceptorManager');\nvar dispatchRequest = require('./dispatchRequest');\nvar mergeConfig = require('./mergeConfig');\n\n/**\n * Create a new instance of Axios\n *\n * @param {Object} instanceConfig The default config for the instance\n */\nfunction Axios(instanceConfig) {\n this.defaults = instanceConfig;\n this.interceptors = {\n request: new InterceptorManager(),\n response: new InterceptorManager()\n };\n}\n\n/**\n * Dispatch a request\n *\n * @param {Object} config The config specific for this request (merged with this.defaults)\n */\nAxios.prototype.request = function request(config) {\n /*eslint no-param-reassign:0*/\n // Allow for axios('example/url'[, config]) a la fetch API\n if (typeof config === 'string') {\n config = arguments[1] || {};\n config.url = arguments[0];\n } else {\n config = config || {};\n }\n\n config = mergeConfig(this.defaults, config);\n config.method = config.method ? config.method.toLowerCase() : 'get';\n\n // Hook up interceptors middleware\n var chain = [dispatchRequest, undefined];\n var promise = Promise.resolve(config);\n\n this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {\n chain.unshift(interceptor.fulfilled, interceptor.rejected);\n });\n\n this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {\n chain.push(interceptor.fulfilled, interceptor.rejected);\n });\n\n while (chain.length) {\n promise = promise.then(chain.shift(), chain.shift());\n }\n\n return promise;\n};\n\nAxios.prototype.getUri = function getUri(config) {\n config = mergeConfig(this.defaults, config);\n return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\\?/, '');\n};\n\n// Provide aliases for supported request methods\nutils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, config) {\n return this.request(utils.merge(config || {}, {\n method: method,\n url: url\n }));\n };\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, data, config) {\n return this.request(utils.merge(config || {}, {\n method: method,\n url: url,\n data: data\n }));\n };\n});\n\nmodule.exports = Axios;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/core/Axios.js\n// module id = 5\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./../utils');\n\nfunction encode(val) {\n return encodeURIComponent(val).\n replace(/%40/gi, '@').\n replace(/%3A/gi, ':').\n replace(/%24/g, '$').\n replace(/%2C/gi, ',').\n replace(/%20/g, '+').\n replace(/%5B/gi, '[').\n replace(/%5D/gi, ']');\n}\n\n/**\n * Build a URL by appending params to the end\n *\n * @param {string} url The base of the url (e.g., http://www.google.com)\n * @param {object} [params] The params to be appended\n * @returns {string} The formatted url\n */\nmodule.exports = function buildURL(url, params, paramsSerializer) {\n /*eslint no-param-reassign:0*/\n if (!params) {\n return url;\n }\n\n var serializedParams;\n if (paramsSerializer) {\n serializedParams = paramsSerializer(params);\n } else if (utils.isURLSearchParams(params)) {\n serializedParams = params.toString();\n } else {\n var parts = [];\n\n utils.forEach(params, function serialize(val, key) {\n if (val === null || typeof val === 'undefined') {\n return;\n }\n\n if (utils.isArray(val)) {\n key = key + '[]';\n } else {\n val = [val];\n }\n\n utils.forEach(val, function parseValue(v) {\n if (utils.isDate(v)) {\n v = v.toISOString();\n } else if (utils.isObject(v)) {\n v = JSON.stringify(v);\n }\n parts.push(encode(key) + '=' + encode(v));\n });\n });\n\n serializedParams = parts.join('&');\n }\n\n if (serializedParams) {\n var hashmarkIndex = url.indexOf('#');\n if (hashmarkIndex !== -1) {\n url = url.slice(0, hashmarkIndex);\n }\n\n url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;\n }\n\n return url;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/helpers/buildURL.js\n// module id = 6\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./../utils');\n\nfunction InterceptorManager() {\n this.handlers = [];\n}\n\n/**\n * Add a new interceptor to the stack\n *\n * @param {Function} fulfilled The function to handle `then` for a `Promise`\n * @param {Function} rejected The function to handle `reject` for a `Promise`\n *\n * @return {Number} An ID used to remove interceptor later\n */\nInterceptorManager.prototype.use = function use(fulfilled, rejected) {\n this.handlers.push({\n fulfilled: fulfilled,\n rejected: rejected\n });\n return this.handlers.length - 1;\n};\n\n/**\n * Remove an interceptor from the stack\n *\n * @param {Number} id The ID that was returned by `use`\n */\nInterceptorManager.prototype.eject = function eject(id) {\n if (this.handlers[id]) {\n this.handlers[id] = null;\n }\n};\n\n/**\n * Iterate over all the registered interceptors\n *\n * This method is particularly useful for skipping over any\n * interceptors that may have become `null` calling `eject`.\n *\n * @param {Function} fn The function to call for each interceptor\n */\nInterceptorManager.prototype.forEach = function forEach(fn) {\n utils.forEach(this.handlers, function forEachHandler(h) {\n if (h !== null) {\n fn(h);\n }\n });\n};\n\nmodule.exports = InterceptorManager;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/core/InterceptorManager.js\n// module id = 7\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./../utils');\nvar transformData = require('./transformData');\nvar isCancel = require('../cancel/isCancel');\nvar defaults = require('../defaults');\nvar isAbsoluteURL = require('./../helpers/isAbsoluteURL');\nvar combineURLs = require('./../helpers/combineURLs');\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nfunction throwIfCancellationRequested(config) {\n if (config.cancelToken) {\n config.cancelToken.throwIfRequested();\n }\n}\n\n/**\n * Dispatch a request to the server using the configured adapter.\n *\n * @param {object} config The config that is to be used for the request\n * @returns {Promise} The Promise to be fulfilled\n */\nmodule.exports = function dispatchRequest(config) {\n throwIfCancellationRequested(config);\n\n // Support baseURL config\n if (config.baseURL && !isAbsoluteURL(config.url)) {\n config.url = combineURLs(config.baseURL, config.url);\n }\n\n // Ensure headers exist\n config.headers = config.headers || {};\n\n // Transform request data\n config.data = transformData(\n config.data,\n config.headers,\n config.transformRequest\n );\n\n // Flatten headers\n config.headers = utils.merge(\n config.headers.common || {},\n config.headers[config.method] || {},\n config.headers || {}\n );\n\n utils.forEach(\n ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],\n function cleanHeaderConfig(method) {\n delete config.headers[method];\n }\n );\n\n var adapter = config.adapter || defaults.adapter;\n\n return adapter(config).then(function onAdapterResolution(response) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n response.data = transformData(\n response.data,\n response.headers,\n config.transformResponse\n );\n\n return response;\n }, function onAdapterRejection(reason) {\n if (!isCancel(reason)) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n if (reason && reason.response) {\n reason.response.data = transformData(\n reason.response.data,\n reason.response.headers,\n config.transformResponse\n );\n }\n }\n\n return Promise.reject(reason);\n });\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/core/dispatchRequest.js\n// module id = 8\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./../utils');\n\n/**\n * Transform the data for a request or a response\n *\n * @param {Object|String} data The data to be transformed\n * @param {Array} headers The headers for the request or response\n * @param {Array|Function} fns A single function or Array of functions\n * @returns {*} The resulting transformed data\n */\nmodule.exports = function transformData(data, headers, fns) {\n /*eslint no-param-reassign:0*/\n utils.forEach(fns, function transform(fn) {\n data = fn(data, headers);\n });\n\n return data;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/core/transformData.js\n// module id = 9\n// module chunks = 0", + "'use strict';\n\nmodule.exports = function isCancel(value) {\n return !!(value && value.__CANCEL__);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/cancel/isCancel.js\n// module id = 10\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./utils');\nvar normalizeHeaderName = require('./helpers/normalizeHeaderName');\n\nvar DEFAULT_CONTENT_TYPE = {\n 'Content-Type': 'application/x-www-form-urlencoded'\n};\n\nfunction setContentTypeIfUnset(headers, value) {\n if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {\n headers['Content-Type'] = value;\n }\n}\n\nfunction getDefaultAdapter() {\n var adapter;\n // Only Node.JS has a process variable that is of [[Class]] process\n if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {\n // For node use HTTP adapter\n adapter = require('./adapters/http');\n } else if (typeof XMLHttpRequest !== 'undefined') {\n // For browsers use XHR adapter\n adapter = require('./adapters/xhr');\n }\n return adapter;\n}\n\nvar defaults = {\n adapter: getDefaultAdapter(),\n\n transformRequest: [function transformRequest(data, headers) {\n normalizeHeaderName(headers, 'Accept');\n normalizeHeaderName(headers, 'Content-Type');\n if (utils.isFormData(data) ||\n utils.isArrayBuffer(data) ||\n utils.isBuffer(data) ||\n utils.isStream(data) ||\n utils.isFile(data) ||\n utils.isBlob(data)\n ) {\n return data;\n }\n if (utils.isArrayBufferView(data)) {\n return data.buffer;\n }\n if (utils.isURLSearchParams(data)) {\n setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');\n return data.toString();\n }\n if (utils.isObject(data)) {\n setContentTypeIfUnset(headers, 'application/json;charset=utf-8');\n return JSON.stringify(data);\n }\n return data;\n }],\n\n transformResponse: [function transformResponse(data) {\n /*eslint no-param-reassign:0*/\n if (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n } catch (e) { /* Ignore */ }\n }\n return data;\n }],\n\n /**\n * A timeout in milliseconds to abort a request. If set to 0 (default) a\n * timeout is not created.\n */\n timeout: 0,\n\n xsrfCookieName: 'XSRF-TOKEN',\n xsrfHeaderName: 'X-XSRF-TOKEN',\n\n maxContentLength: -1,\n\n validateStatus: function validateStatus(status) {\n return status >= 200 && status < 300;\n }\n};\n\ndefaults.headers = {\n common: {\n 'Accept': 'application/json, text/plain, */*'\n }\n};\n\nutils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {\n defaults.headers[method] = {};\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);\n});\n\nmodule.exports = defaults;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/defaults.js\n// module id = 11\n// module chunks = 0", + "'use strict';\n\nvar utils = require('../utils');\n\nmodule.exports = function normalizeHeaderName(headers, normalizedName) {\n utils.forEach(headers, function processHeader(value, name) {\n if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {\n headers[normalizedName] = value;\n delete headers[name];\n }\n });\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/helpers/normalizeHeaderName.js\n// module id = 12\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./../utils');\nvar settle = require('./../core/settle');\nvar buildURL = require('./../helpers/buildURL');\nvar parseHeaders = require('./../helpers/parseHeaders');\nvar isURLSameOrigin = require('./../helpers/isURLSameOrigin');\nvar createError = require('../core/createError');\n\nmodule.exports = function xhrAdapter(config) {\n return new Promise(function dispatchXhrRequest(resolve, reject) {\n var requestData = config.data;\n var requestHeaders = config.headers;\n\n if (utils.isFormData(requestData)) {\n delete requestHeaders['Content-Type']; // Let the browser set it\n }\n\n var request = new XMLHttpRequest();\n\n // HTTP basic authentication\n if (config.auth) {\n var username = config.auth.username || '';\n var password = config.auth.password || '';\n requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);\n }\n\n request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);\n\n // Set the request timeout in MS\n request.timeout = config.timeout;\n\n // Listen for ready state\n request.onreadystatechange = function handleLoad() {\n if (!request || request.readyState !== 4) {\n return;\n }\n\n // The request errored out and we didn't get a response, this will be\n // handled by onerror instead\n // With one exception: request that using file: protocol, most browsers\n // will return status as 0 even though it's a successful request\n if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {\n return;\n }\n\n // Prepare the response\n var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;\n var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;\n var response = {\n data: responseData,\n status: request.status,\n statusText: request.statusText,\n headers: responseHeaders,\n config: config,\n request: request\n };\n\n settle(resolve, reject, response);\n\n // Clean up request\n request = null;\n };\n\n // Handle browser request cancellation (as opposed to a manual cancellation)\n request.onabort = function handleAbort() {\n if (!request) {\n return;\n }\n\n reject(createError('Request aborted', config, 'ECONNABORTED', request));\n\n // Clean up request\n request = null;\n };\n\n // Handle low level network errors\n request.onerror = function handleError() {\n // Real errors are hidden from us by the browser\n // onerror should only fire if it's a network error\n reject(createError('Network Error', config, null, request));\n\n // Clean up request\n request = null;\n };\n\n // Handle timeout\n request.ontimeout = function handleTimeout() {\n reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',\n request));\n\n // Clean up request\n request = null;\n };\n\n // Add xsrf header\n // This is only done if running in a standard browser environment.\n // Specifically not if we're in a web worker, or react-native.\n if (utils.isStandardBrowserEnv()) {\n var cookies = require('./../helpers/cookies');\n\n // Add xsrf header\n var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?\n cookies.read(config.xsrfCookieName) :\n undefined;\n\n if (xsrfValue) {\n requestHeaders[config.xsrfHeaderName] = xsrfValue;\n }\n }\n\n // Add headers to the request\n if ('setRequestHeader' in request) {\n utils.forEach(requestHeaders, function setRequestHeader(val, key) {\n if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {\n // Remove Content-Type if data is undefined\n delete requestHeaders[key];\n } else {\n // Otherwise add header to the request\n request.setRequestHeader(key, val);\n }\n });\n }\n\n // Add withCredentials to request if needed\n if (config.withCredentials) {\n request.withCredentials = true;\n }\n\n // Add responseType to request if needed\n if (config.responseType) {\n try {\n request.responseType = config.responseType;\n } catch (e) {\n // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.\n // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.\n if (config.responseType !== 'json') {\n throw e;\n }\n }\n }\n\n // Handle progress if needed\n if (typeof config.onDownloadProgress === 'function') {\n request.addEventListener('progress', config.onDownloadProgress);\n }\n\n // Not all browsers support upload events\n if (typeof config.onUploadProgress === 'function' && request.upload) {\n request.upload.addEventListener('progress', config.onUploadProgress);\n }\n\n if (config.cancelToken) {\n // Handle cancellation\n config.cancelToken.promise.then(function onCanceled(cancel) {\n if (!request) {\n return;\n }\n\n request.abort();\n reject(cancel);\n // Clean up request\n request = null;\n });\n }\n\n if (requestData === undefined) {\n requestData = null;\n }\n\n // Send the request\n request.send(requestData);\n });\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/adapters/xhr.js\n// module id = 13\n// module chunks = 0", + "'use strict';\n\nvar createError = require('./createError');\n\n/**\n * Resolve or reject a Promise based on response status.\n *\n * @param {Function} resolve A function that resolves the promise.\n * @param {Function} reject A function that rejects the promise.\n * @param {object} response The response.\n */\nmodule.exports = function settle(resolve, reject, response) {\n var validateStatus = response.config.validateStatus;\n if (!validateStatus || validateStatus(response.status)) {\n resolve(response);\n } else {\n reject(createError(\n 'Request failed with status code ' + response.status,\n response.config,\n null,\n response.request,\n response\n ));\n }\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/core/settle.js\n// module id = 14\n// module chunks = 0", + "'use strict';\n\nvar enhanceError = require('./enhanceError');\n\n/**\n * Create an Error with the specified message, config, error code, request and response.\n *\n * @param {string} message The error message.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The created error.\n */\nmodule.exports = function createError(message, config, code, request, response) {\n var error = new Error(message);\n return enhanceError(error, config, code, request, response);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/core/createError.js\n// module id = 15\n// module chunks = 0", + "'use strict';\n\n/**\n * Update an Error with the specified config, error code, and response.\n *\n * @param {Error} error The error to update.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The error.\n */\nmodule.exports = function enhanceError(error, config, code, request, response) {\n error.config = config;\n if (code) {\n error.code = code;\n }\n\n error.request = request;\n error.response = response;\n error.isAxiosError = true;\n\n error.toJSON = function() {\n return {\n // Standard\n message: this.message,\n name: this.name,\n // Microsoft\n description: this.description,\n number: this.number,\n // Mozilla\n fileName: this.fileName,\n lineNumber: this.lineNumber,\n columnNumber: this.columnNumber,\n stack: this.stack,\n // Axios\n config: this.config,\n code: this.code\n };\n };\n return error;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/core/enhanceError.js\n// module id = 16\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./../utils');\n\n// Headers whose duplicates are ignored by node\n// c.f. https://nodejs.org/api/http.html#http_message_headers\nvar ignoreDuplicateOf = [\n 'age', 'authorization', 'content-length', 'content-type', 'etag',\n 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',\n 'last-modified', 'location', 'max-forwards', 'proxy-authorization',\n 'referer', 'retry-after', 'user-agent'\n];\n\n/**\n * Parse headers into an object\n *\n * ```\n * Date: Wed, 27 Aug 2014 08:58:49 GMT\n * Content-Type: application/json\n * Connection: keep-alive\n * Transfer-Encoding: chunked\n * ```\n *\n * @param {String} headers Headers needing to be parsed\n * @returns {Object} Headers parsed into an object\n */\nmodule.exports = function parseHeaders(headers) {\n var parsed = {};\n var key;\n var val;\n var i;\n\n if (!headers) { return parsed; }\n\n utils.forEach(headers.split('\\n'), function parser(line) {\n i = line.indexOf(':');\n key = utils.trim(line.substr(0, i)).toLowerCase();\n val = utils.trim(line.substr(i + 1));\n\n if (key) {\n if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {\n return;\n }\n if (key === 'set-cookie') {\n parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);\n } else {\n parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;\n }\n }\n });\n\n return parsed;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/helpers/parseHeaders.js\n// module id = 17\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs have full support of the APIs needed to test\n // whether the request URL is of the same origin as current location.\n (function standardBrowserEnv() {\n var msie = /(msie|trident)/i.test(navigator.userAgent);\n var urlParsingNode = document.createElement('a');\n var originURL;\n\n /**\n * Parse a URL to discover it's components\n *\n * @param {String} url The URL to be parsed\n * @returns {Object}\n */\n function resolveURL(url) {\n var href = url;\n\n if (msie) {\n // IE needs attribute set twice to normalize properties\n urlParsingNode.setAttribute('href', href);\n href = urlParsingNode.href;\n }\n\n urlParsingNode.setAttribute('href', href);\n\n // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils\n return {\n href: urlParsingNode.href,\n protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',\n host: urlParsingNode.host,\n search: urlParsingNode.search ? urlParsingNode.search.replace(/^\\?/, '') : '',\n hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',\n hostname: urlParsingNode.hostname,\n port: urlParsingNode.port,\n pathname: (urlParsingNode.pathname.charAt(0) === '/') ?\n urlParsingNode.pathname :\n '/' + urlParsingNode.pathname\n };\n }\n\n originURL = resolveURL(window.location.href);\n\n /**\n * Determine if a URL shares the same origin as the current location\n *\n * @param {String} requestURL The URL to test\n * @returns {boolean} True if URL shares the same origin, otherwise false\n */\n return function isURLSameOrigin(requestURL) {\n var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;\n return (parsed.protocol === originURL.protocol &&\n parsed.host === originURL.host);\n };\n })() :\n\n // Non standard browser envs (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return function isURLSameOrigin() {\n return true;\n };\n })()\n);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/helpers/isURLSameOrigin.js\n// module id = 18\n// module chunks = 0", + "'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs support document.cookie\n (function standardBrowserEnv() {\n return {\n write: function write(name, value, expires, path, domain, secure) {\n var cookie = [];\n cookie.push(name + '=' + encodeURIComponent(value));\n\n if (utils.isNumber(expires)) {\n cookie.push('expires=' + new Date(expires).toGMTString());\n }\n\n if (utils.isString(path)) {\n cookie.push('path=' + path);\n }\n\n if (utils.isString(domain)) {\n cookie.push('domain=' + domain);\n }\n\n if (secure === true) {\n cookie.push('secure');\n }\n\n document.cookie = cookie.join('; ');\n },\n\n read: function read(name) {\n var match = document.cookie.match(new RegExp('(^|;\\\\s*)(' + name + ')=([^;]*)'));\n return (match ? decodeURIComponent(match[3]) : null);\n },\n\n remove: function remove(name) {\n this.write(name, '', Date.now() - 86400000);\n }\n };\n })() :\n\n // Non standard browser env (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return {\n write: function write() {},\n read: function read() { return null; },\n remove: function remove() {}\n };\n })()\n);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/helpers/cookies.js\n// module id = 19\n// module chunks = 0", + "'use strict';\n\n/**\n * Determines whether the specified URL is absolute\n *\n * @param {string} url The URL to test\n * @returns {boolean} True if the specified URL is absolute, otherwise false\n */\nmodule.exports = function isAbsoluteURL(url) {\n // A URL is considered absolute if it begins with \"://\" or \"//\" (protocol-relative URL).\n // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed\n // by any combination of letters, digits, plus, period, or hyphen.\n return /^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(url);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/helpers/isAbsoluteURL.js\n// module id = 20\n// module chunks = 0", + "'use strict';\n\n/**\n * Creates a new URL by combining the specified URLs\n *\n * @param {string} baseURL The base URL\n * @param {string} relativeURL The relative URL\n * @returns {string} The combined URL\n */\nmodule.exports = function combineURLs(baseURL, relativeURL) {\n return relativeURL\n ? baseURL.replace(/\\/+$/, '') + '/' + relativeURL.replace(/^\\/+/, '')\n : baseURL;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/helpers/combineURLs.js\n// module id = 21\n// module chunks = 0", + "'use strict';\n\nvar utils = require('../utils');\n\n/**\n * Config-specific merge-function which creates a new config-object\n * by merging two configuration objects together.\n *\n * @param {Object} config1\n * @param {Object} config2\n * @returns {Object} New object resulting from merging config2 to config1\n */\nmodule.exports = function mergeConfig(config1, config2) {\n // eslint-disable-next-line no-param-reassign\n config2 = config2 || {};\n var config = {};\n\n utils.forEach(['url', 'method', 'params', 'data'], function valueFromConfig2(prop) {\n if (typeof config2[prop] !== 'undefined') {\n config[prop] = config2[prop];\n }\n });\n\n utils.forEach(['headers', 'auth', 'proxy'], function mergeDeepProperties(prop) {\n if (utils.isObject(config2[prop])) {\n config[prop] = utils.deepMerge(config1[prop], config2[prop]);\n } else if (typeof config2[prop] !== 'undefined') {\n config[prop] = config2[prop];\n } else if (utils.isObject(config1[prop])) {\n config[prop] = utils.deepMerge(config1[prop]);\n } else if (typeof config1[prop] !== 'undefined') {\n config[prop] = config1[prop];\n }\n });\n\n utils.forEach([\n 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',\n 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',\n 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'maxContentLength',\n 'validateStatus', 'maxRedirects', 'httpAgent', 'httpsAgent', 'cancelToken',\n 'socketPath'\n ], function defaultToConfig2(prop) {\n if (typeof config2[prop] !== 'undefined') {\n config[prop] = config2[prop];\n } else if (typeof config1[prop] !== 'undefined') {\n config[prop] = config1[prop];\n }\n });\n\n return config;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/core/mergeConfig.js\n// module id = 22\n// module chunks = 0", + "'use strict';\n\n/**\n * A `Cancel` is an object that is thrown when an operation is canceled.\n *\n * @class\n * @param {string=} message The message.\n */\nfunction Cancel(message) {\n this.message = message;\n}\n\nCancel.prototype.toString = function toString() {\n return 'Cancel' + (this.message ? ': ' + this.message : '');\n};\n\nCancel.prototype.__CANCEL__ = true;\n\nmodule.exports = Cancel;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/cancel/Cancel.js\n// module id = 23\n// module chunks = 0", + "'use strict';\n\nvar Cancel = require('./Cancel');\n\n/**\n * A `CancelToken` is an object that can be used to request cancellation of an operation.\n *\n * @class\n * @param {Function} executor The executor function.\n */\nfunction CancelToken(executor) {\n if (typeof executor !== 'function') {\n throw new TypeError('executor must be a function.');\n }\n\n var resolvePromise;\n this.promise = new Promise(function promiseExecutor(resolve) {\n resolvePromise = resolve;\n });\n\n var token = this;\n executor(function cancel(message) {\n if (token.reason) {\n // Cancellation has already been requested\n return;\n }\n\n token.reason = new Cancel(message);\n resolvePromise(token.reason);\n });\n}\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nCancelToken.prototype.throwIfRequested = function throwIfRequested() {\n if (this.reason) {\n throw this.reason;\n }\n};\n\n/**\n * Returns an object that contains a new `CancelToken` and a function that, when called,\n * cancels the `CancelToken`.\n */\nCancelToken.source = function source() {\n var cancel;\n var token = new CancelToken(function executor(c) {\n cancel = c;\n });\n return {\n token: token,\n cancel: cancel\n };\n};\n\nmodule.exports = CancelToken;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/cancel/CancelToken.js\n// module id = 24\n// module chunks = 0", + "'use strict';\n\n/**\n * Syntactic sugar for invoking a function and expanding an array for arguments.\n *\n * Common use case would be to use `Function.prototype.apply`.\n *\n * ```js\n * function f(x, y, z) {}\n * var args = [1, 2, 3];\n * f.apply(null, args);\n * ```\n *\n * With `spread` this example can be re-written.\n *\n * ```js\n * spread(function(x, y, z) {})([1, 2, 3]);\n * ```\n *\n * @param {Function} callback\n * @returns {Function}\n */\nmodule.exports = function spread(callback) {\n return function wrap(arr) {\n return callback.apply(null, arr);\n };\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./lib/helpers/spread.js\n// module id = 25\n// module chunks = 0" + ], + "sourceRoot": "" +} \ No newline at end of file diff --git a/src/main/resources/static/js/libs/jquery-3.4.1.min.js b/src/main/resources/static/js/libs/jquery-3.4.1.min.js new file mode 100644 index 0000000..a1c07fd --- /dev/null +++ b/src/main/resources/static/js/libs/jquery-3.4.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0 { + const body = document.querySelector("body"); + const errorP = document.querySelector(".error"); + + // Ajoute Axios + const axiosElement = document.createElement("script"); + axiosElement.src = "/js/libs/axios.min.js"; + body.appendChild(axiosElement); + + axiosElement.onload = () => { + // Ajoute le header + axios.get('/header.html') + .then(r => { + const header = document.createElement("header"); + header.innerHTML = r.data; + body.insertBefore(header, document.querySelector("section")); + }) + .catch(e => { + console.log(e); + errorP.innerHTML = "Une erreur est survenue lors de la récupération de l'entête"; + }); + + // Vérifie si les SIMDUTs sont présents + document.querySelectorAll(".materialCode").forEach(e => { + const materialID = e.getAttribute("data-materialID"); + axios.get(`/simdut/${materialID}`) + .catch(err => { + if (err.response.status === 404) { + e.parentElement.classList.add("nosimdut"); + e.parentElement.title = "Aucun fichier SIMDUT trouvé"; + } else { + console.log(e); + } + }); + }); + }; + + document.querySelectorAll(".returnIndex").forEach((e) => { + e.addEventListener("click", () => { + document.location.href = "/"; + }); + }); + + document.querySelectorAll(".materialCode").forEach((e) => { + const materialID = e.getAttribute("data-materialID"); + e.addEventListener("click", () => { + window.open("/simdut/" + materialID, "_blank"); + }); + }); + + document.querySelectorAll("img").forEach((e) => { + e.addEventListener("click", () => { + window.open(e.src, "_blank"); + }); + }); + + document.querySelectorAll(".requireAuth").forEach((e) => { + e.onsubmit = () => { + checkPassword(e); + return false; + }; + }); + + document.querySelectorAll(".requireAuth-remover").forEach((e) => { + e.querySelectorAll(".remover").forEach(elem => { + elem.addEventListener("click", () => { + e.action += elem.getAttribute("data-recipeID"); + checkPassword(e); + }); + }); + }); + + document.querySelectorAll(".companyTabTitle").forEach(e => { + e.addEventListener("click", () => { + const companyName = e.getAttribute("data-companyName"); + const table = document.getElementById("recipes_" + companyName); + + if (table.style.display === "none") { + table.style.display = "table"; + } else { + table.style.display = "none"; + } + }); + }); + + // Ajoute le favicon + let faviconElement = document.createElement("link"); + faviconElement.rel = "icon"; + faviconElement.href = "/favicon.png"; + document.querySelector("head").appendChild(faviconElement); + + function checkPassword(form) { + errorP.innerHTML = ""; + + const password = prompt("Quel est votre mot de passe?"); + + let data = {}; + data.password = password; + + axios.post("/password/valid", data) + .then(r => { + console.log(r); + if (r.data) { + form.submit(); + } else { + errorP.innerHTML = "Votre mot de passe n'est pas valide"; + } + }) + .catch(e => { + errorP.innerHTML = "Une erreur est survenue lors de l'envoie des informations vers le serveur."; + console.log(e); + }); + } +})(); + + +const lTomL = 1000; +const galTomL = 3785.41; + +// Change les unités selon la sélection de l'utilisateur +function changeUnits(unitSelect, quantitiesSelector, unitsSelector) { + document.querySelectorAll(unitsSelector).forEach(e => { + e.innerText = unitSelect.value; + + // Modifie la quantitée + const quantityElem = e.parentElement.parentElement.querySelector(quantitiesSelector); + const originalQuantity = parseInt(quantityElem.dataset.quantityml); + + switch (unitSelect.value) { + case "L": + quantityElem.innerText = originalQuantity / lTomL; + break; + case "gal": + quantityElem.innerText = originalQuantity / galTomL; + break; + default: + quantityElem.innerText = originalQuantity; + break; + } + + // Arrondi à deux décimaux + quantityElem.innerText = Math.round(quantityElem.innerText * 100) / 100; + }); +} \ No newline at end of file diff --git a/src/main/resources/static/js/main.js.bak b/src/main/resources/static/js/main.js.bak new file mode 100644 index 0000000..a5bebd8 --- /dev/null +++ b/src/main/resources/static/js/main.js.bak @@ -0,0 +1,150 @@ +(() => { + + // Ajoute Axios + const axiosElement = document.createElement("script"); + axiosElement.src = "/js/libs/axios.min.js"; + body.appendChild(axiosElement); + + // Ajoute jQuery + const jqueryElement = document.createElement("script"); + jqueryElement.src = "/js/libs/jquery-3.4.1.min.js"; + body.appendChild(jqueryElement); + + const body = $("body"); + const errorP = $(".error"); + + axiosElement.onload = () => { + // Ajoute le header + axios.get('/header.html') + .then(r => { + const header = document.createElement("header"); + header.innerHTML = r.data; + body.insertBefore(header, document.querySelector("section")); + }) + .catch(e => { + console.log(e); + errorP.innerHTML = "Une erreur est survenue lors de la récupération de l'entête"; + }); + + // Vérifie si les SIMDUTs sont présents + document.querySelectorAll(".materialCode").forEach(e => { + const materialID = e.getAttribute("data-materialID"); + axios.get(`/simdut/${materialID}`) + .catch(err => { + if (err.response.status === 404) { + e.parentElement.classList.add("nosimdut"); + e.parentElement.title = "Aucun fichier SIMDUT trouvé"; + } else { + console.log(e); + } + }); + }); + }; + + document.querySelectorAll(".returnIndex").forEach((e) => { + e.addEventListener("click", () => { + document.location.href = "/"; + }); + }); + + document.querySelectorAll(".materialCode").forEach((e) => { + const materialID = e.getAttribute("data-materialID"); + e.addEventListener("click", () => { + window.open("/simdut/" + materialID, "_blank"); + }); + }); + + document.querySelectorAll("img").forEach((e) => { + e.addEventListener("click", () => { + window.open(e.src, "_blank"); + }); + }); + + document.querySelectorAll(".requireAuth").forEach((e) => { + e.onsubmit = () => { + checkPassword(e); + return false; + }; + }); + + document.querySelectorAll(".requireAuth-remover").forEach((e) => { + e.querySelectorAll(".remover").forEach(elem => { + elem.addEventListener("click", () => { + e.action += elem.getAttribute("data-recipeID"); + checkPassword(e); + }); + }); + }); + + document.querySelectorAll(".companyTabTitle").forEach(e => { + e.addEventListener("click", () => { + const companyName = e.getAttribute("data-companyName"); + const table = document.getElementById("recipes_" + companyName); + + if (table.style.display === "none") { + table.style.display = "table"; + } else { + table.style.display = "none"; + } + }); + }); + + // Ajoute le favicon + let faviconElement = document.createElement("link"); + faviconElement.rel = "icon"; + faviconElement.href = "/favicon.png"; + document.querySelector("head").appendChild(faviconElement); + + function checkPassword(form) { + errorP.innerHTML = ""; + + const password = prompt("Quel est votre mot de passe?"); + + let data = {}; + data.password = password; + + axios.post("/password/valid", data) + .then(r => { + console.log(r); + if (r.data) { + form.submit(); + } else { + errorP.innerHTML = "Votre mot de passe n'est pas valide"; + } + }) + .catch(e => { + errorP.innerHTML = "Une erreur est survenue lors de l'envoie des informations vers le serveur."; + console.log(e); + }); + } +})(); + + +const lTomL = 1000; +const galTomL = 3785.41; + +// Change les unités selon la sélection de l'utilisateur +function changeUnits(unitSelect, quantitiesSelector, unitsSelector) { + document.querySelectorAll(unitsSelector).forEach(e => { + e.innerText = unitSelect.value; + + // Modifie la quantitée + const quantityElem = e.parentElement.parentElement.querySelector(quantitiesSelector); + const originalQuantity = parseInt(quantityElem.dataset.quantityml); + + switch (unitSelect.value) { + case "L": + quantityElem.innerText = originalQuantity / lTomL; + break; + case "gal": + quantityElem.innerText = originalQuantity / galTomL; + break; + default: + quantityElem.innerText = originalQuantity; + break; + } + + // Arrondi à deux décimaux + quantityElem.innerText = Math.round(quantityElem.innerText * 100) / 100; + }); +} \ No newline at end of file diff --git a/src/main/resources/static/logo.png b/src/main/resources/static/logo.png new file mode 100644 index 0000000..34c1387 Binary files /dev/null and b/src/main/resources/static/logo.png differ diff --git a/src/main/resources/static/pdf/touchup.pdf b/src/main/resources/static/pdf/touchup.pdf new file mode 100644 index 0000000..4f47c99 Binary files /dev/null and b/src/main/resources/static/pdf/touchup.pdf differ diff --git a/src/main/resources/templates/company/created.html b/src/main/resources/templates/company/created.html new file mode 100644 index 0000000..59c2625 --- /dev/null +++ b/src/main/resources/templates/company/created.html @@ -0,0 +1,19 @@ + + + + + Ajout d'une bannière + + + + + +
+

Ajout d'une bannière

+

+ +
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/company/creator.html b/src/main/resources/templates/company/creator.html new file mode 100644 index 0000000..b52b5ff --- /dev/null +++ b/src/main/resources/templates/company/creator.html @@ -0,0 +1,38 @@ + + + + + Ajout d'une bannière + + + + + + +
+

+

Ajout d'une bannière

+ +
+
+ + + + + + + + + +
+ + + +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/company/remover.html b/src/main/resources/templates/company/remover.html new file mode 100644 index 0000000..d969527 --- /dev/null +++ b/src/main/resources/templates/company/remover.html @@ -0,0 +1,60 @@ + + + + + Supprimer des bannières + + + + + + + + +
+

+

+

+

Supprimer des bannières

+ +
+ + + + + + + + + + + +
Nom de la bannière
+ +
+
+
+ + Aucune bannière n'a été trouvée. + +
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/images/add.html b/src/main/resources/templates/images/add.html new file mode 100644 index 0000000..757abed --- /dev/null +++ b/src/main/resources/templates/images/add.html @@ -0,0 +1,25 @@ + + + + + Ajout une image + + + + + +
+

+ +
+ + + +
+ +
+
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..60f66e4 --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,112 @@ + + + + + Explorateur de recettes de couleur + + + + + + +
+

+ +
+ + +

+ + + + + + + + + + + + + + + + +
CouleurDescriptionÉchantillon
+ +
+
+
+
+ + Aucune bannière n'a été trouvée. + +
+
+ + + + + diff --git a/src/main/resources/templates/inventory.html b/src/main/resources/templates/inventory.html new file mode 100644 index 0000000..c878052 --- /dev/null +++ b/src/main/resources/templates/inventory.html @@ -0,0 +1,177 @@ + + + + + Inventaire + + + + + + + +
+ + + +

Inventaire

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProduitQuantitéType + + +
+ +
+
+ + + +
+
+ + + +
mL + +
+
+ + Aucun produit n'a été trouvé. + +
+ + + + + diff --git a/src/main/resources/templates/material/created.html b/src/main/resources/templates/material/created.html new file mode 100644 index 0000000..df7ec88 --- /dev/null +++ b/src/main/resources/templates/material/created.html @@ -0,0 +1,19 @@ + + + + + Ajout d'un produit + + + + + +
+

Ajout d'un produit

+

+ +
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/material/creator.html b/src/main/resources/templates/material/creator.html new file mode 100644 index 0000000..cf23b10 --- /dev/null +++ b/src/main/resources/templates/material/creator.html @@ -0,0 +1,101 @@ + + + + + Ajout d'un produit + + + + + + +
+

+ +

Ajout d'un produit

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + + +
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/material/edit.html b/src/main/resources/templates/material/edit.html new file mode 100644 index 0000000..f97091c --- /dev/null +++ b/src/main/resources/templates/material/edit.html @@ -0,0 +1,109 @@ + + + + + + + + + + + +
+

+ +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + +
+ + + +
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/material/editor.html b/src/main/resources/templates/material/editor.html new file mode 100644 index 0000000..7ae55d6 --- /dev/null +++ b/src/main/resources/templates/material/editor.html @@ -0,0 +1,68 @@ + + + + + Modifier un produit + + + + + + + +
+

+

+

+

Modifier des produits

+ + + + + + + + + + + + + + +
ProduitType
+ +
+
+ + Aucun produit n'a été trouvé + +
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/material/remover.html b/src/main/resources/templates/material/remover.html new file mode 100644 index 0000000..5bf913c --- /dev/null +++ b/src/main/resources/templates/material/remover.html @@ -0,0 +1,59 @@ + + + + + Supprimer des produits + + + + + + + +
+

+

+

+

Supprimer des produits

+ +
+ + + + + + + + + + + + + +
ProduitType
+ +
+
+
+ + Aucun produit n'a été trouvé. + +
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/material/simdut.html b/src/main/resources/templates/material/simdut.html new file mode 100644 index 0000000..81d4ab2 --- /dev/null +++ b/src/main/resources/templates/material/simdut.html @@ -0,0 +1,31 @@ + + + + + Ajout d'un produit + + + + + + +
+

Ajout d'un produit

+
+
+ + + + + +
+
+ + +
+
+
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/materialType/creator.html b/src/main/resources/templates/materialType/creator.html new file mode 100644 index 0000000..d4a9dc5 --- /dev/null +++ b/src/main/resources/templates/materialType/creator.html @@ -0,0 +1,46 @@ + + + + + Ajout d'un type de produit + + + + + + +
+

+

Ajout d'un type de produit

+ +
+
+ + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/mix/creator.html b/src/main/resources/templates/mix/creator.html new file mode 100644 index 0000000..6c2fdaf --- /dev/null +++ b/src/main/resources/templates/mix/creator.html @@ -0,0 +1,147 @@ + + + + + Ajout d'un mélange + + + + + + + + +
+

+

Ajout d'un mélange pour
la recette

+ +
+
+ + + + + + + + + + + + + +
+ + + + + + + + + + + +
ProduitQuantité + +
+ mL + +
+
+ + + +
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/mix/editor.html b/src/main/resources/templates/mix/editor.html new file mode 100644 index 0000000..68053a7 --- /dev/null +++ b/src/main/resources/templates/mix/editor.html @@ -0,0 +1,195 @@ + + + + + Modifier un mélange + + + + + + + + + +
+

+

Modifier un mélange pour
la recette

+ +
+
+ +
+
+ + + + + + + + + + + +
+ + + + + +
ProduitQuantité + +
+
+ + + +
+
+
+
+ + + +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/created.html b/src/main/resources/templates/recipe/created.html new file mode 100644 index 0000000..894ff7f --- /dev/null +++ b/src/main/resources/templates/recipe/created.html @@ -0,0 +1,21 @@ + + + + + Ajout d'une recette + + + + + +
+

+ +

Ajout d'une recette

+

+ +
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/creator.html b/src/main/resources/templates/recipe/creator.html new file mode 100644 index 0000000..eb9688d --- /dev/null +++ b/src/main/resources/templates/recipe/creator.html @@ -0,0 +1,61 @@ + + + + + Ajout d'une recette + + + + + +
+

+ +

Ajout d'une recette

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+
+
+ + + + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/edit.html b/src/main/resources/templates/recipe/edit.html new file mode 100644 index 0000000..9ea4d3b --- /dev/null +++ b/src/main/resources/templates/recipe/edit.html @@ -0,0 +1,299 @@ + + + + + + + + + + + + + +
+

+ +

+ +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+

+ + + +
+
+ + + + + + + + + + + + + + +
ProduitTypeQuantité
+

+
+
+
+
+
+ +
+
+
+ Étapes + + + + + +
+ +
+
+
+ Image supprimée ou corrompue + + +
+ +
+ + + +
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/recipe/editor.html b/src/main/resources/templates/recipe/editor.html new file mode 100644 index 0000000..882edc1 --- /dev/null +++ b/src/main/resources/templates/recipe/editor.html @@ -0,0 +1,83 @@ + + + + + Modifier une recette + + + + + + + + +
+

+

+ +

Modifier une recette

+ + +

+ + + + + + + + + + + + + + + + +
CouleurDescriptionÉchantillon
+ +
+
+
+
+ + Aucune bannière n'a été trouvée. + +
+ + + + + diff --git a/src/main/resources/templates/recipe/explore.html b/src/main/resources/templates/recipe/explore.html new file mode 100644 index 0000000..ef4895d --- /dev/null +++ b/src/main/resources/templates/recipe/explore.html @@ -0,0 +1,373 @@ + + + + + + + + + + + + + +
+

+

+ +

+ + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Couleur:
+
+
Bannière:
+
+
Description:
+
+
Échantillon:
+
+
Date d'approbation: Non approuvée
+
+
Remarque: +
+
+
Note: + +
+
+ + + + + + + + + + + + + + + + + + +


+
+ + + + + + + + + + + + + + + + + + + + + + +
ProduitTypeQuantité
+

+
+

+
+

mL

+
+
+ +
+
+
+ Étapes: + +
    + +
  1. +
    +
+
+
+ +
+ Image supprimée ou corrompue +
+
+ + + + + diff --git a/src/main/resources/templates/recipe/remover.html b/src/main/resources/templates/recipe/remover.html new file mode 100644 index 0000000..82aa925 --- /dev/null +++ b/src/main/resources/templates/recipe/remover.html @@ -0,0 +1,70 @@ + + + + + Supprimer des recettes + + + + + + + + +
+

+

+ +

Supprimer une recette

+
+ + +

+ + + + + + + + + + + + + + + + +
CouleurDescriptionÉchantillon
+ +
+
+
+
+ + Aucune bannière n'a été trouvée. + +
+
+ + + + diff --git a/src/main/resources/templates/touchup.html b/src/main/resources/templates/touchup.html new file mode 100644 index 0000000..a09f6cd --- /dev/null +++ b/src/main/resources/templates/touchup.html @@ -0,0 +1,24 @@ + + + + + Génération du PDF d'un kit de retouche + + + + + +
+

Génération du PDF d'un kit de retouche

+ +
+ + +
+ + +
+
+ + + \ No newline at end of file diff --git a/src/test/java/fyloz/trial/ColorRecipesExplorer/ColorRecipesExplorerApplicationTests.java b/src/test/java/fyloz/trial/ColorRecipesExplorer/ColorRecipesExplorerApplicationTests.java new file mode 100644 index 0000000..3ae03a8 --- /dev/null +++ b/src/test/java/fyloz/trial/ColorRecipesExplorer/ColorRecipesExplorerApplicationTests.java @@ -0,0 +1,16 @@ +package fyloz.trial.ColorRecipesExplorer; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class ColorRecipesExplorerApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/users b/users new file mode 100644 index 0000000..1f7391f --- /dev/null +++ b/users @@ -0,0 +1 @@ +master diff --git a/workdir.zip b/workdir.zip new file mode 100644 index 0000000..ef2eb56 Binary files /dev/null and b/workdir.zip differ diff --git a/workdir/.directory b/workdir/.directory new file mode 100644 index 0000000..dc1dc9c --- /dev/null +++ b/workdir/.directory @@ -0,0 +1,3 @@ +[Dolphin] +Timestamp=2019,5,9,10,28,30 +Version=4 diff --git a/workdir/images/15_LQ-1-0.jpeg b/workdir/images/15_LQ-1-0.jpeg new file mode 100644 index 0000000..d99ac3d Binary files /dev/null and b/workdir/images/15_LQ-1-0.jpeg differ diff --git a/workdir/passwords.txt b/workdir/passwords.txt new file mode 100644 index 0000000..414243b --- /dev/null +++ b/workdir/passwords.txt @@ -0,0 +1,2 @@ +master +dom diff --git a/workdir/recipes.mv.db b/workdir/recipes.mv.db new file mode 100644 index 0000000..7346774 Binary files /dev/null and b/workdir/recipes.mv.db differ diff --git a/workdir/recipes.trace.db b/workdir/recipes.trace.db new file mode 100644 index 0000000..f3d199b --- /dev/null +++ b/workdir/recipes.trace.db @@ -0,0 +1,271 @@ +2019-06-24 11:41:58 jdbc[13]: exception +org.h2.jdbc.JdbcSQLDataException: Data conversion error converting "'Catalyseur' (MATERIALS: ""MATERIAL_TYPE_MATERIAL_TYPEID"" INTEGER)"; SQL statement: +delete from MATERIAL_TYPE [22018-199] +2019-06-24 11:42:11 jdbc[13]: exception +org.h2.jdbc.JdbcSQLDataException: Data conversion error converting "'Catalyseur' (MATERIALS: ""MATERIAL_TYPE_MATERIAL_TYPEID"" INTEGER)"; SQL statement: +DELETE FROM MATERIAL_TYPE [22018-199] +2019-06-25 20:18:42 database: flush +org.h2.message.DbException: General error: "java.lang.IllegalStateException: The file is locked: nio:/home/william/Dev/Java/ColorRecipesExplorer/workdir/recipes.mv.db [1.4.199/7]" [50000-199] + at org.h2.message.DbException.get(DbException.java:194) + at org.h2.message.DbException.convert(DbException.java:347) + at org.h2.mvstore.db.MVTableEngine$1.uncaughtException(MVTableEngine.java:90) + at org.h2.mvstore.MVStore.handleException(MVStore.java:2787) + at org.h2.mvstore.MVStore.panic(MVStore.java:441) + at org.h2.mvstore.MVStore.(MVStore.java:404) + at org.h2.mvstore.MVStore$Builder.open(MVStore.java:3343) + at org.h2.mvstore.db.MVTableEngine$Store.open(MVTableEngine.java:162) + at org.h2.mvstore.db.MVTableEngine.init(MVTableEngine.java:95) + at org.h2.engine.Database.getPageStore(Database.java:2739) + at org.h2.engine.Database.open(Database.java:769) + at org.h2.engine.Database.openDatabase(Database.java:319) + at org.h2.engine.Database.(Database.java:313) + at org.h2.engine.Engine.openSession(Engine.java:69) + at org.h2.engine.Engine.openSession(Engine.java:201) + at org.h2.engine.Engine.createSessionAndValidate(Engine.java:178) + at org.h2.engine.Engine.createSession(Engine.java:161) + at org.h2.engine.Engine.createSession(Engine.java:31) + at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:336) + at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:169) + at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:148) + at org.h2.Driver.connect(Driver.java:69) + at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:136) + at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:369) + at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:198) + at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:467) + at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:541) + at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:115) + at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112) + at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:157) + at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:115) + at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:78) + at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:319) + at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:356) + at org.springframework.boot.autoconfigure.orm.jpa.DatabaseLookup.getDatabase(DatabaseLookup.java:73) + at org.springframework.boot.autoconfigure.orm.jpa.JpaProperties.determineDatabase(JpaProperties.java:142) + at org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration.jpaVendorAdapter(JpaBaseConfiguration.java:113) + at org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration$$EnhancerBySpringCGLIB$$a4ce5b3d.CGLIB$jpaVendorAdapter$5() + at org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration$$EnhancerBySpringCGLIB$$a4ce5b3d$$FastClassBySpringCGLIB$$480db8e2.invoke() + at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) + at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) + at org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration$$EnhancerBySpringCGLIB$$a4ce5b3d.jpaVendorAdapter() + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) + at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.base/java.lang.reflect.Method.invoke(Method.java:567) + at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) + at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:456) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1247) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:509) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1247) + at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167) + at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857) + at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760) + at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:509) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) + at fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication.main(ColorRecipesExplorerApplication.java:36) +Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "java.lang.IllegalStateException: The file is locked: nio:/home/william/Dev/Java/ColorRecipesExplorer/workdir/recipes.mv.db [1.4.199/7]" [50000-199] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:502) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:427) + ... 95 more +Caused by: java.lang.IllegalStateException: The file is locked: nio:/home/william/Dev/Java/ColorRecipesExplorer/workdir/recipes.mv.db [1.4.199/7] + at org.h2.mvstore.DataUtils.newIllegalStateException(DataUtils.java:883) + at org.h2.mvstore.FileStore.open(FileStore.java:172) + at org.h2.mvstore.MVStore.(MVStore.java:390) + ... 89 more +2019-06-25 20:18:43 database: flush +org.h2.message.DbException: General error: "java.lang.IllegalStateException: The file is locked: nio:/home/william/Dev/Java/ColorRecipesExplorer/workdir/recipes.mv.db [1.4.199/7]" [50000-199] + at org.h2.message.DbException.get(DbException.java:194) + at org.h2.message.DbException.convert(DbException.java:347) + at org.h2.mvstore.db.MVTableEngine$1.uncaughtException(MVTableEngine.java:90) + at org.h2.mvstore.MVStore.handleException(MVStore.java:2787) + at org.h2.mvstore.MVStore.panic(MVStore.java:441) + at org.h2.mvstore.MVStore.(MVStore.java:404) + at org.h2.mvstore.MVStore$Builder.open(MVStore.java:3343) + at org.h2.mvstore.db.MVTableEngine$Store.open(MVTableEngine.java:162) + at org.h2.mvstore.db.MVTableEngine.init(MVTableEngine.java:95) + at org.h2.engine.Database.getPageStore(Database.java:2739) + at org.h2.engine.Database.open(Database.java:769) + at org.h2.engine.Database.openDatabase(Database.java:319) + at org.h2.engine.Database.(Database.java:313) + at org.h2.engine.Engine.openSession(Engine.java:69) + at org.h2.engine.Engine.openSession(Engine.java:201) + at org.h2.engine.Engine.createSessionAndValidate(Engine.java:178) + at org.h2.engine.Engine.createSession(Engine.java:161) + at org.h2.engine.Engine.createSession(Engine.java:31) + at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:336) + at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:169) + at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:148) + at org.h2.Driver.connect(Driver.java:69) + at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:136) + at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:369) + at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:198) + at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:467) + at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:541) + at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:115) + at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112) + at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:180) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:68) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35) + at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:94) + at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263) + at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237) + at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214) + at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:152) + at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286) + at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243) + at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214) + at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.(InFlightMetadataCollectorImpl.java:179) + at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:119) + at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:904) + at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:935) + at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57) + at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) + at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390) + at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377) + at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) + at fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication.main(ColorRecipesExplorerApplication.java:36) +Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "java.lang.IllegalStateException: The file is locked: nio:/home/william/Dev/Java/ColorRecipesExplorer/workdir/recipes.mv.db [1.4.199/7]" [50000-199] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:502) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:427) + ... 68 more +Caused by: java.lang.IllegalStateException: The file is locked: nio:/home/william/Dev/Java/ColorRecipesExplorer/workdir/recipes.mv.db [1.4.199/7] + at org.h2.mvstore.DataUtils.newIllegalStateException(DataUtils.java:883) + at org.h2.mvstore.FileStore.open(FileStore.java:172) + at org.h2.mvstore.MVStore.(MVStore.java:390) + ... 62 more +2019-06-25 20:18:46 database: flush +org.h2.message.DbException: General error: "java.lang.IllegalStateException: The file is locked: nio:/home/william/Dev/Java/ColorRecipesExplorer/workdir/recipes.mv.db [1.4.199/7]" [50000-199] + at org.h2.message.DbException.get(DbException.java:194) + at org.h2.message.DbException.convert(DbException.java:347) + at org.h2.mvstore.db.MVTableEngine$1.uncaughtException(MVTableEngine.java:90) + at org.h2.mvstore.MVStore.handleException(MVStore.java:2787) + at org.h2.mvstore.MVStore.panic(MVStore.java:441) + at org.h2.mvstore.MVStore.(MVStore.java:404) + at org.h2.mvstore.MVStore$Builder.open(MVStore.java:3343) + at org.h2.mvstore.db.MVTableEngine$Store.open(MVTableEngine.java:162) + at org.h2.mvstore.db.MVTableEngine.init(MVTableEngine.java:95) + at org.h2.engine.Database.getPageStore(Database.java:2739) + at org.h2.engine.Database.open(Database.java:769) + at org.h2.engine.Database.openDatabase(Database.java:319) + at org.h2.engine.Database.(Database.java:313) + at org.h2.engine.Engine.openSession(Engine.java:69) + at org.h2.engine.Engine.openSession(Engine.java:201) + at org.h2.engine.Engine.createSessionAndValidate(Engine.java:178) + at org.h2.engine.Engine.createSession(Engine.java:161) + at org.h2.engine.Engine.createSession(Engine.java:31) + at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:336) + at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:169) + at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:148) + at org.h2.Driver.connect(Driver.java:69) + at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:136) + at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:369) + at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:198) + at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:467) + at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:541) + at com.zaxxer.hikari.pool.HikariPool.(HikariPool.java:115) + at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112) + at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) + at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess.obtainConnection(JdbcEnvironmentInitiator.java:180) + at org.hibernate.resource.transaction.backend.jdbc.internal.DdlTransactionIsolatorNonJtaImpl.getIsolatedConnection(DdlTransactionIsolatorNonJtaImpl.java:43) + at org.hibernate.tool.schema.internal.exec.ImprovedExtractionContextImpl.getJdbcConnection(ImprovedExtractionContextImpl.java:60) + at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2DatabaseImpl.extractMetadata(SequenceInformationExtractorH2DatabaseImpl.java:35) + at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.initializeSequences(DatabaseInformationImpl.java:65) + at org.hibernate.tool.schema.extract.internal.DatabaseInformationImpl.(DatabaseInformationImpl.java:59) + at org.hibernate.tool.schema.internal.Helper.buildDatabaseInformation(Helper.java:132) + at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:96) + at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:183) + at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:72) + at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:310) + at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:467) + at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:939) + at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57) + at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) + at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390) + at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377) + at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1837) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1774) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) + at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) + at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) + at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) + at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) + at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) + at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1105) + at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) + at fyloz.trial.ColorRecipesExplorer.ColorRecipesExplorerApplication.main(ColorRecipesExplorerApplication.java:36) +Caused by: org.h2.jdbc.JdbcSQLNonTransientException: General error: "java.lang.IllegalStateException: The file is locked: nio:/home/william/Dev/Java/ColorRecipesExplorer/workdir/recipes.mv.db [1.4.199/7]" [50000-199] + at org.h2.message.DbException.getJdbcSQLException(DbException.java:502) + at org.h2.message.DbException.getJdbcSQLException(DbException.java:427) + ... 66 more +Caused by: java.lang.IllegalStateException: The file is locked: nio:/home/william/Dev/Java/ColorRecipesExplorer/workdir/recipes.mv.db [1.4.199/7] + at org.h2.mvstore.DataUtils.newIllegalStateException(DataUtils.java:883) + at org.h2.mvstore.FileStore.open(FileStore.java:172) + at org.h2.mvstore.MVStore.(MVStore.java:390) + ... 60 more +2019-07-03 22:19:21 jdbc[13]: exception +org.h2.jdbc.JdbcSQLDataException: Data conversion error converting "null"; SQL statement: +select * from materials where MATERIAL_TYPE_MATERIAL_TYPEID = 'null' [22018-199] diff --git a/workdir/simdut/10_844-9955.pdf b/workdir/simdut/10_844-9955.pdf new file mode 100644 index 0000000..1651b3c Binary files /dev/null and b/workdir/simdut/10_844-9955.pdf differ diff --git a/workdir/simdut/11_844-1861.pdf b/workdir/simdut/11_844-1861.pdf new file mode 100644 index 0000000..8605bb8 Binary files /dev/null and b/workdir/simdut/11_844-1861.pdf differ diff --git a/workdir/simdut/12_844-0061.pdf b/workdir/simdut/12_844-0061.pdf new file mode 100644 index 0000000..0209130 Binary files /dev/null and b/workdir/simdut/12_844-0061.pdf differ diff --git a/workdir/simdut/13_844-9451.pdf b/workdir/simdut/13_844-9451.pdf new file mode 100644 index 0000000..3123381 Binary files /dev/null and b/workdir/simdut/13_844-9451.pdf differ diff --git a/workdir/simdut/14_844-0461.pdf b/workdir/simdut/14_844-0461.pdf new file mode 100644 index 0000000..8fa2762 Binary files /dev/null and b/workdir/simdut/14_844-0461.pdf differ diff --git a/workdir/simdut/4_FK50 Noir.pdf b/workdir/simdut/4_FK50 Noir.pdf new file mode 100644 index 0000000..e2a5853 Binary files /dev/null and b/workdir/simdut/4_FK50 Noir.pdf differ diff --git a/workdir/simdut/5_C-51.pdf b/workdir/simdut/5_C-51.pdf new file mode 100644 index 0000000..8be68d2 Binary files /dev/null and b/workdir/simdut/5_C-51.pdf differ diff --git a/workdir/simdut/6_DEDE L.pdf b/workdir/simdut/6_DEDE L.pdf new file mode 100644 index 0000000..b549fe4 Binary files /dev/null and b/workdir/simdut/6_DEDE L.pdf differ diff --git a/workdir/simdut/7_H980/20.pdf b/workdir/simdut/7_H980/20.pdf new file mode 100644 index 0000000..560d5be Binary files /dev/null and b/workdir/simdut/7_H980/20.pdf differ diff --git a/workdir/simdut/9_400-039.pdf b/workdir/simdut/9_400-039.pdf new file mode 100644 index 0000000..5ba8d42 Binary files /dev/null and b/workdir/simdut/9_400-039.pdf differ