Initialisation du repo
This commit is contained in:
commit
bbe436c758
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@ -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/
|
114
.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
114
.mvn/wrapper/MavenWrapperDownloader.java
vendored
Normal file
@ -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();
|
||||
}
|
||||
|
||||
}
|
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
1
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
1
.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@ -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
|
BIN
TestProd.pdf
Normal file
BIN
TestProd.pdf
Normal file
Binary file not shown.
286
mvnw
vendored
Normal file
286
mvnw
vendored
Normal file
@ -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 "$@"
|
161
mvnw.cmd
vendored
Normal file
161
mvnw.cmd
vendored
Normal file
@ -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%
|
80
pom.xml
Normal file
80
pom.xml
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>fyloz.trial</groupId>
|
||||
<artifactId>ColorRecipesExplorer</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>ColorRecipesExplorer</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!--spring-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- H2 -->
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.11</version>
|
||||
</dependency>
|
||||
|
||||
<!-- PDF -->
|
||||
<dependency>
|
||||
<groupId>com.itextpdf</groupId>
|
||||
<artifactId>itextpdf</artifactId>
|
||||
<version>5.5.10</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.pdfbox</groupId>
|
||||
<artifactId>pdfbox</artifactId>
|
||||
<version>2.0.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
BIN
recipes.mv.db
Normal file
BIN
recipes.mv.db
Normal file
Binary file not shown.
@ -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.
|
||||
* <p>
|
||||
* 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<String> 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.");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package fyloz.trial.ColorRecipesExplorer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PasswordValidator {
|
||||
|
||||
private static List<String> passwords = new ArrayList<>();
|
||||
|
||||
public static boolean isValid(String password) {
|
||||
return passwords.contains(password);
|
||||
}
|
||||
|
||||
public static void addPassword(String password) {
|
||||
passwords.add(password);
|
||||
}
|
||||
}
|
@ -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())
|
||||
.<Class>map(MethodParameter::getContainingClass)
|
||||
.orElseGet(() ->
|
||||
ofNullable(ip.getField())
|
||||
.map(Field::getDeclaringClass)
|
||||
.orElseThrow(IllegalArgumentException::new)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<String> existingImages = recipeService.getImageFiles(recipe);
|
||||
|
||||
if (existingImages != null) {
|
||||
List<Integer> 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;
|
||||
}
|
||||
}
|
@ -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<Company, Integer> {
|
||||
|
||||
void deleteByCompanyID(int companyID);
|
||||
|
||||
Company findByCompanyName(String companyName);
|
||||
}
|
@ -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, Integer> {
|
||||
Material findByMaterialID(int materialID);
|
||||
|
||||
Material findByMaterialCode(String materialCode);
|
||||
}
|
@ -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, Integer> {
|
||||
|
||||
MaterialType findByMaterialTypeName(String name);
|
||||
|
||||
}
|
@ -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, Integer> {
|
||||
|
||||
Mix findByMixID(int mixID);
|
||||
|
||||
List<Mix> findAllByRecipe(Recipe recipe);
|
||||
}
|
@ -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, Integer> {
|
||||
MixQuantity findByMixQuantityID(int mixQuantityID);
|
||||
|
||||
List<MixQuantity> findAllByMaterial(Material material);
|
||||
}
|
@ -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, Integer> {
|
||||
MixType findByTypeID(int id);
|
||||
|
||||
MixType findByTypeName(String typeName);
|
||||
|
||||
MixType findByMaterial(Material material);
|
||||
}
|
@ -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<Recipe, Integer> {
|
||||
|
||||
List<Recipe> findAllByCompany(Company company);
|
||||
|
||||
Recipe findByRecipeCode(String recipeCode);
|
||||
|
||||
Recipe findByRecipeID(int recipeID);
|
||||
}
|
@ -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, Integer> {
|
||||
|
||||
RecipeStep findByStepID(int stepID);
|
||||
|
||||
List<RecipeStep> findAllByRecipe(Recipe recipe);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package fyloz.trial.ColorRecipesExplorer.model;
|
||||
|
||||
public abstract class BeanModel {
|
||||
public abstract Integer getID();
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<Material> 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<Material> 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);
|
||||
}
|
||||
}
|
107
src/main/java/fyloz/trial/ColorRecipesExplorer/model/Mix.java
Normal file
107
src/main/java/fyloz/trial/ColorRecipesExplorer/model/Mix.java
Normal file
@ -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<MixQuantity> mixQuantities;
|
||||
|
||||
// Casier
|
||||
private String location;
|
||||
|
||||
public Mix() {
|
||||
|
||||
}
|
||||
|
||||
public Mix(@NotNull Recipe recipe, @NotNull MixType mixType, List<MixQuantity> 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<MixQuantity> getMixQuantities() {
|
||||
return mixQuantities;
|
||||
}
|
||||
|
||||
public void setMixQuantities(List<MixQuantity> 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
176
src/main/java/fyloz/trial/ColorRecipesExplorer/model/Recipe.java
Normal file
176
src/main/java/fyloz/trial/ColorRecipesExplorer/model/Recipe.java
Normal file
@ -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<Mix> recipeMixes;
|
||||
|
||||
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
|
||||
private List<RecipeStep> 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<Mix> getRecipeMixes() {
|
||||
return recipeMixes;
|
||||
}
|
||||
|
||||
public void setRecipeMixes(List<Mix> recipeMixes) {
|
||||
this.recipeMixes = recipeMixes;
|
||||
}
|
||||
|
||||
public List<RecipeStep> getRecipeSteps() {
|
||||
return recipeSteps;
|
||||
}
|
||||
|
||||
public void setRecipeSteps(List<RecipeStep> 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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<Company> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<T extends BeanModel> implements IGenericService<T> {
|
||||
|
||||
protected Logger logger = ColorRecipesExplorerApplication.logger;
|
||||
|
||||
protected JpaRepository<T, Integer> dao;
|
||||
|
||||
public GenericService(JpaRepository<T, Integer> dao) {
|
||||
this.dao = dao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getByID(int id) {
|
||||
return dao.findById(id).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getAll() {
|
||||
return dao.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T save(T entity) {
|
||||
if (isValidForCreation(entity)) {
|
||||
return dao.save(entity);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveAll(List<T> 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<T> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<T extends BeanModel> {
|
||||
|
||||
/**
|
||||
* 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<T> 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<T> entities);
|
||||
|
||||
/**
|
||||
* Met à jour une entité dans la base de données.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<T> entities);
|
||||
|
||||
boolean exists(T entity);
|
||||
}
|
@ -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<Material, Float> 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<Material, Float> 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;
|
||||
}
|
||||
}
|
@ -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<Material> {
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -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<MaterialType> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<MixQuantity> {
|
||||
|
||||
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<MixQuantity> getByMaterial(Material material) {
|
||||
return mixQuantityDao.findAllByMaterial(material);
|
||||
}
|
||||
}
|
@ -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<Mix> {
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* 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<Integer> materials, List<Float> 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<MixQuantity> 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.
|
||||
* <p>
|
||||
* 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<Integer> materials, List<Float> quantities) {
|
||||
List<MixQuantity> 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<MixQuantity> createMixQuantities(Mix mix, List<Integer> materials, List<Float> quantities) {
|
||||
List<MixQuantity> 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;
|
||||
}
|
||||
}
|
@ -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<MixType> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<Recipe> {
|
||||
|
||||
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<Recipe> 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<String, Object> input) {
|
||||
List<RecipeStep> 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<RecipeStep> 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<Mix> 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<Mix> getSortedMixes(Recipe recipe) {
|
||||
List<Mix> 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<Company, List<Recipe>> getRecipesByCompany() {
|
||||
List<Company> companies = companyService.getAll();
|
||||
List<Recipe> recipes = getAll();
|
||||
|
||||
Map<Company, List<Recipe>> 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<String> 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<MixType> getAssociatedMixesTypes(Recipe recipe) {
|
||||
return recipe.getRecipeMixes().stream().map(Mix::getMixType).collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -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<RecipeStep> {
|
||||
|
||||
@Autowired
|
||||
public StepService(StepDao stepDao) {
|
||||
super(stepDao);
|
||||
}
|
||||
}
|
@ -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}";
|
||||
}
|
@ -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/";
|
||||
|
||||
}
|
@ -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<byte[]> 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* La suppression échouera si:
|
||||
* - L'utilisateur n'est pas autorisé à exécuter cette action
|
||||
* - Une erreur est survenue lors de la suppression du fichier
|
||||
* <p>
|
||||
* Réponse de la méthode:
|
||||
* - error: Contient le message d'erreur, s'il y a lieu
|
||||
* <p>
|
||||
* 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<String, String> deleteImage(@RequestBody Map<String, String> form) {
|
||||
Map<String, String> response = new HashMap<>();
|
||||
|
||||
ImageHandler imageHandler = new ImageHandler(form.get("image"), recipeService);
|
||||
if (!imageHandler.deleteFile()) {
|
||||
response.put(RESPONSE_ERROR, ERROR_SAVING_IMAGE);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
@ -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<Company> companies = companyService.getAll();
|
||||
Map<Company, List<Recipe>> 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<String, Object> data) {
|
||||
return PasswordValidator.isValid((String) data.get("password"));
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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<String, String> consumeMaterials(@RequestBody Map<String, Object> form) {
|
||||
Map<String, String> response = new HashMap<>();
|
||||
|
||||
List<Mix> mixes = new ArrayList<>();
|
||||
Map<Mix, Map<Material, Float>> 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<String, String> formMaterials = (Map<String, String>) form.get(mixIDStr);
|
||||
Map<Material, Float> 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;
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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<Mix> 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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<String, String> saveRecipeInformations(@RequestBody Map<String, Object> form) {
|
||||
int recipeID = Integer.parseInt(form.get("recipeID").toString());
|
||||
Map<String, String> location = (Map<String, String>) form.get("locations");
|
||||
String note = form.get("note").toString();
|
||||
|
||||
Map<String, String> 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;
|
||||
}
|
||||
}
|
@ -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<byte[]> 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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* Modèle de la page:
|
||||
* - error: Contient le message d'erreur, s'il y a lieu
|
||||
* - companyName: Contient le nom de la bannière
|
||||
* <p>
|
||||
* 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();
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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éé
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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<MixType> 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<Material> 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* Modèle de la page:
|
||||
* - error: Contient le message d'erreur, s'il y a lieu
|
||||
* <p>
|
||||
* 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<String, String> form) {
|
||||
String mixTypeName = form.getFirst(MIX_TYPE);
|
||||
int recipeID = Integer.parseInt(form.getFirst(RECIPE_ID));
|
||||
|
||||
List<Integer> materials = new ArrayList<>();
|
||||
List<Float> 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));
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* Modèle de la page:
|
||||
* - error: Contient le message d'erreur, s'il y a lieu
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Modèle de la page:
|
||||
* - error: Contient le message d'erreur, s'il y a lieu
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* Modèle de la page:
|
||||
* - error: Contient le message d'erreur, s'il y a lieu
|
||||
* - materialCode: Contient le code du produit mit à jour
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -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<Material> materials = new ArrayList<>();
|
||||
List<MixType> 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* Modèle de la page:
|
||||
* - error: Contient le message d'erreur, s'il y a lieu
|
||||
* <p>
|
||||
* 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<String, String> 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<Integer> materials = new ArrayList<>();
|
||||
List<Float> 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()));
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* Modèle de la page:
|
||||
* - error: Contient le message d'erreur, s'il y a lieu
|
||||
* - recipeCode: Contient la couleur d'une recette
|
||||
* <p>
|
||||
* 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<String, Object> 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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* 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é
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* Modèle de la page:
|
||||
* - error: Contient le message d'erreur, s'il y a lieu
|
||||
* - recipeCode: Contient la couleur de la recette
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
}
|
26
src/main/resources/application.properties
Normal file
26
src/main/resources/application.properties
Normal file
@ -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
|
2
src/main/resources/import.sql
Normal file
2
src/main/resources/import.sql
Normal file
@ -0,0 +1,2 @@
|
||||
INSERT INTO MATERIAL_TYPE
|
||||
VALUES (0, 'Aucun', '', FALSE);
|
8
src/main/resources/static/css/forms.css
Normal file
8
src/main/resources/static/css/forms.css
Normal file
@ -0,0 +1,8 @@
|
||||
.form {
|
||||
display: inline-block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
td:not(.centerTd) {
|
||||
text-align: right;
|
||||
}
|
109
src/main/resources/static/css/main.css
Normal file
109
src/main/resources/static/css/main.css
Normal file
@ -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;
|
||||
}
|
||||
|
BIN
src/main/resources/static/favicon.png
Normal file
BIN
src/main/resources/static/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 252 B |
51
src/main/resources/static/header.html
Normal file
51
src/main/resources/static/header.html
Normal file
@ -0,0 +1,51 @@
|
||||
<nav>
|
||||
<img alt="logo" src="/logo.png"/>
|
||||
<div class="dropdown">
|
||||
<button class="dropbtn" onclick="document.location.href='/'">
|
||||
Liste
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="dropbtn">
|
||||
Ajouter
|
||||
</button>
|
||||
<div class="dropdown-content">
|
||||
<a href="/company/creator">Bannière</a>
|
||||
<a href="/material/creator">Produit</a>
|
||||
<a href="/recipe/creator">Recette</a>
|
||||
<a href="/materialType/creator">Type de produit</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="dropbtn">
|
||||
Modifier
|
||||
</button>
|
||||
<div class="dropdown-content">
|
||||
<a href="/material/editor">Produit</a>
|
||||
<a href="/recipe/editor">Recette</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="dropbtn">
|
||||
Supprimer
|
||||
</button>
|
||||
<div class="dropdown-content">
|
||||
<a href="/company/remover">Bannière</a>
|
||||
<a href="/material/remover">Produit</a>
|
||||
<a href="/recipe/remover">Recette</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="dropbtn" onclick="document.location.href='/inventory'">
|
||||
Inventaire
|
||||
</button>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<button class="dropbtn">
|
||||
Autres
|
||||
</button>
|
||||
<div class="dropdown-content">
|
||||
<a href="/touchup">PDF Kits de retouche</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
9
src/main/resources/static/js/libs/axios.min.js
vendored
Normal file
9
src/main/resources/static/js/libs/axios.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
336
src/main/resources/static/js/libs/axios.min.map
Normal file
336
src/main/resources/static/js/libs/axios.min.map
Normal file
File diff suppressed because one or more lines are too long
2
src/main/resources/static/js/libs/jquery-3.4.1.min.js
vendored
Normal file
2
src/main/resources/static/js/libs/jquery-3.4.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
144
src/main/resources/static/js/main.js
Normal file
144
src/main/resources/static/js/main.js
Normal file
@ -0,0 +1,144 @@
|
||||
(() => {
|
||||
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;
|
||||
});
|
||||
}
|
150
src/main/resources/static/js/main.js.bak
Normal file
150
src/main/resources/static/js/main.js.bak
Normal file
@ -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;
|
||||
});
|
||||
}
|
BIN
src/main/resources/static/logo.png
Normal file
BIN
src/main/resources/static/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
src/main/resources/static/pdf/touchup.pdf
Normal file
BIN
src/main/resources/static/pdf/touchup.pdf
Normal file
Binary file not shown.
19
src/main/resources/templates/company/created.html
Normal file
19
src/main/resources/templates/company/created.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout d'une bannière</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<h1>Ajout d'une bannière</h1>
|
||||
<p th:text="'La bannière ' + ${companyName} + ' à été enregistrée.'"></p>
|
||||
<button class="returnIndex">Retour</button>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
38
src/main/resources/templates/company/creator.html
Normal file
38
src/main/resources/templates/company/creator.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout d'une bannière</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<h1>Ajout d'une bannière</h1>
|
||||
|
||||
<div class="form">
|
||||
<form action="/company/creator" class="requireAuth" method="POST">
|
||||
<table>
|
||||
<tr>
|
||||
<td><b><label for="companyName">Nom de la bannière: </label></b></td>
|
||||
<td><input id="companyName" name="companyName" type="text"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button class="returnIndex" type="button">Retour</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit">Enregistrer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
60
src/main/resources/templates/company/remover.html
Normal file
60
src/main/resources/templates/company/remover.html
Normal file
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Supprimer des bannières</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
table {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
th {
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<p class="success" th:if="${successCompanyName != null}"
|
||||
th:text="'La bannière \'' + ${successCompanyName} + '\' a bien été supprimée.'"></b>
|
||||
</p>
|
||||
<h1>Supprimer des bannières</h1>
|
||||
|
||||
<form action="/company/remover/" class="requireAuth-remover" method="POST">
|
||||
<th:block th:if="${!companies.empty}">
|
||||
<table id="companiesList">
|
||||
<tr>
|
||||
<th>Nom de la bannière</th>
|
||||
</tr>
|
||||
<th:block th:each="company : ${companies}">
|
||||
<tr>
|
||||
<td th:text="${company.companyName}"></td>
|
||||
<td>
|
||||
<button class="remover" th:data-code="${company.companyName}"
|
||||
th:data-recipeID="${company.companyID}"
|
||||
type="button">Supprimer
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</th:block>
|
||||
</form>
|
||||
<th:block th:if="${companies.empty}">
|
||||
<b class="error">Aucune bannière n'a été trouvée.</b>
|
||||
</th:block>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
25
src/main/resources/templates/images/add.html
Normal file
25
src/main/resources/templates/images/add.html
Normal file
@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout une image</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
|
||||
<form action="/images/add" class="requireAuth" enctype="multipart/form-data" method="POST">
|
||||
<input name="recipeID" th:value="${recipeID}" type="hidden"/>
|
||||
|
||||
<input id="image" name="image" required type="file"/>
|
||||
<br/>
|
||||
<input type="submit" value="Sauvegarde"/>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
112
src/main/resources/templates/index.html
Normal file
112
src/main/resources/templates/index.html
Normal file
@ -0,0 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Explorateur de recettes de couleur</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.centerCell {
|
||||
border-left-color: #e6e6e6;
|
||||
border-left-style: solid;
|
||||
border-left-width: 2px;
|
||||
|
||||
border-right-color: #e6e6e6;
|
||||
border-right-style: solid;
|
||||
border-right-width: 2px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
margin: 0px auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
section {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.recipeDescription {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.unapproved {
|
||||
background-color: #fff0b3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
|
||||
<div>
|
||||
<th:block th:if="${!recipes.empty}">
|
||||
<th:block th:each="company : ${recipes.keySet()}">
|
||||
<h1 class="companyTabTitle" th:data-companyName="${company.companyName}"
|
||||
th:text="${company.companyName}"></h1>
|
||||
|
||||
<th:block th:if="${!recipes.get(company).empty}">
|
||||
<table style="display:none" th:id="'recipes_' + ${company.companyName}">
|
||||
<tr>
|
||||
<th>Couleur</th>
|
||||
<th>Description</th>
|
||||
<th>Échantillon</th>
|
||||
</tr>
|
||||
<th:block th:each="recipe : ${recipes.get(company)}">
|
||||
<tr class="recipeRow" th:data-approbationDate="${recipe.approbationDate}">
|
||||
<td th:text="${recipe.recipeCode}"></td>
|
||||
<td class="centerCell recipeDescription" th:text="${recipe.recipeDescription}"></td>
|
||||
<td class="centerCell" th:text="${recipe.sample}"></td>
|
||||
<td>
|
||||
<button class="gotoRecipe" th:data-recipeID="${recipe.recipeID}" type="button">
|
||||
Voir
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<th:block th:if="${recipes.empty}">
|
||||
<b class="error">Aucune bannière n'a été trouvée.</b>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
<script>
|
||||
(() => {
|
||||
document.querySelectorAll(".gotoRecipe").forEach(e => {
|
||||
e.addEventListener("click", () => {
|
||||
const recipeID = e.getAttribute("data-recipeID");
|
||||
|
||||
document.location.href = "/recipe/explore/" + recipeID;
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll(".recipeRow").forEach(e => {
|
||||
const approbationDate = e.getAttribute("data-approbationDate");
|
||||
|
||||
if (approbationDate === null) {
|
||||
e.classList.add("unapproved");
|
||||
e.title = "Cette recette n'est pas approuvée";
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
177
src/main/resources/templates/inventory.html
Normal file
177
src/main/resources/templates/inventory.html
Normal file
@ -0,0 +1,177 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Inventaire</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
td, th {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 50px auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
section {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.materialRow:hover td {
|
||||
background-color: #f0f0f0;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.lowQuantity td {
|
||||
background-color: #fff0b3;
|
||||
}
|
||||
|
||||
.hidden, .hiddenWrongType {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<th:block th:each="materialType : ${materialTypes}"
|
||||
th:if="${materialType.materialTypeName.equalsIgnoreCase('aucun')}">
|
||||
<input id="anyTypeId" th:value="${materialType.materialTypeID}" type="hidden"/>
|
||||
</th:block>
|
||||
<h1>Inventaire</h1>
|
||||
|
||||
<th:block th:if="${!materials.empty}">
|
||||
<table>
|
||||
|
||||
<!--Titres des colonnes et options de recherche-->
|
||||
<tr>
|
||||
<th>Produit</th>
|
||||
<th>Quantité</th>
|
||||
|
||||
<!--Unités de la quantité-->
|
||||
<td></td>
|
||||
|
||||
<th>Type</th>
|
||||
<td></td>
|
||||
|
||||
<!--Options-->
|
||||
<td rowspan="5">
|
||||
<label for="lowQuantity">Quantité faible: </label>
|
||||
<input id="lowQuantity" min="0" onchange="checkLowQuantity(this.value)" step="0.01"
|
||||
style="width: 65px" type="number"
|
||||
value="100"/>
|
||||
<br/>
|
||||
<input id="hideOthers" onchange="hide(this.checked)" type="checkbox"/><label for="hideOthers">Cacher
|
||||
les autres produits</label>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<label for="materialTypeSelect">Voir seulement: </label>
|
||||
<select id="materialTypeSelect" name="materialTypeSelect" onchange="hideMaterialTypes(this)">
|
||||
<th:block th:each="materialType : ${materialTypes}">
|
||||
<option th:text="${materialType.materialTypeName}"
|
||||
th:value="${materialType.materialTypeID}"></option>
|
||||
</th:block>
|
||||
</select>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<label for="units">Unités: </label>
|
||||
<select id="units" name="units"
|
||||
onchange="changeUnits(this, '.inventoryQuantity', '.inventoryQuantityUnits')">
|
||||
<option selected value="mL">Millilitres</option>
|
||||
<option value="L">Litres</option>
|
||||
<option value="gal">Gallons</option>
|
||||
</select>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
<!--Produits-->
|
||||
<th:block th:each="material : ${materials}">
|
||||
<tr class="materialRow"
|
||||
th:data-materialType="${material.materialType.materialTypeID}">
|
||||
<td class="materialCode" th:data-materialID="${material.materialID}"
|
||||
th:text="${material.materialCode}"></td>
|
||||
<td class="inventoryQuantity" th:data-quantityML="${material.inventoryQuantity}"
|
||||
th:text="${material.inventoryQuantity}"></td>
|
||||
<td><span class="inventoryQuantityUnits">mL</span></td>
|
||||
<td class="materialType" th:text="${material.materialType.materialTypeName}"></td>
|
||||
<td>
|
||||
<button class="modifyMaterial" th:data-materialID="${material.materialID}" type="button">
|
||||
Modifier
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:if="${materials.empty}">
|
||||
<b class="error">Aucun produit n'a été trouvé.</b>
|
||||
</th:block>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
|
||||
// Rajoute un événement sur les boutons pour modifier les produits pour rediriger l'utilisateur vers la page de modification
|
||||
document.querySelectorAll(".modifyMaterial").forEach(e => {
|
||||
e.addEventListener("click", () => {
|
||||
document.location.href = `/material/editor/${e.dataset.materialid}`;
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
// Ajoute la classe "lowQuantity" au produits qui ont une quantité en inventaire inférieur à la quantité limite définie
|
||||
function checkLowQuantity(value) {
|
||||
document.querySelectorAll(".materialRow").forEach(e => {
|
||||
|
||||
if (parseFloat(e.querySelector(".inventoryQuantity").innerHTML) < value) {
|
||||
e.classList.add("lowQuantity");
|
||||
} else {
|
||||
e.classList.remove("lowQuantity");
|
||||
}
|
||||
|
||||
hide(document.querySelector("#hideOthers").checked);
|
||||
});
|
||||
}
|
||||
|
||||
// Cache ou dévoile les produits qui ont la classe "lowQuantity", dépendamment du paramètre "checked"
|
||||
function hide(checked) {
|
||||
if (checked) {
|
||||
document.querySelectorAll(".materialRow").forEach(e => {
|
||||
if (!e.classList.contains("lowQuantity")) {
|
||||
e.classList.add("hidden");
|
||||
} else {
|
||||
e.classList.remove("hidden");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
document.querySelectorAll(".hidden").forEach(e => {
|
||||
e.classList.remove("hidden");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Cache les produits qui ne sont pas du type de produit spécifié dans le paramètre "select", si le type de produit n'est pas "Aucun"
|
||||
function hideMaterialTypes(select) {
|
||||
const value = select.value;
|
||||
const anyValue = document.querySelector("#anyTypeId").value;
|
||||
|
||||
document.querySelectorAll(".materialRow").forEach(e => {
|
||||
if (value !== anyValue && e.dataset.materialtype !== value) {
|
||||
e.classList.add("hiddenWrongType");
|
||||
} else {
|
||||
e.classList.remove("hiddenWrongType");
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
19
src/main/resources/templates/material/created.html
Normal file
19
src/main/resources/templates/material/created.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout d'un produit</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<h1>Ajout d'un produit</h1>
|
||||
<p th:text="'Le produit ' + ${materialCode} + ' a été enregistré.'"></p>
|
||||
<button class="returnIndex">Retour</button>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
101
src/main/resources/templates/material/creator.html
Normal file
101
src/main/resources/templates/material/creator.html
Normal file
@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout d'un produit</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
|
||||
<h1>Ajout d'un produit</h1>
|
||||
<div class="form">
|
||||
<form action="/material/creator" class="requireAuth" enctype="multipart/form-data" method="POST">
|
||||
<table>
|
||||
|
||||
<tr>
|
||||
<td><b><label for="materialCode">Code du produit: </label></b></td>
|
||||
<td><input id="materialCode" name="materialCode" type="text"/></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><b><label for="inventoryQuantity">Quantité en inventaire: </label></b></td>
|
||||
<td><input data-unit="mL" id="quantity" min="0" name="quantity"
|
||||
onchange="calculateMilliliters(this.value, this.dataset.unit)" step="0.01" type="number">
|
||||
</td>
|
||||
|
||||
<!--Contient la quantité en millilitres-->
|
||||
<input id="inventoryQuantity" name="inventoryQuantity" type="hidden"/>
|
||||
|
||||
<td>
|
||||
<select id="units" name="units"
|
||||
onchange="switchUnits(this)">
|
||||
<option selected value="mL">Millilitres</option>
|
||||
<option value="L">Litres</option>
|
||||
<option value="gal">Gallons</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><b><label for="materialType">Type de produit: </label></b></td>
|
||||
<td>
|
||||
<select id="materialType" name="materialType">
|
||||
<th:block th:each="materialType : ${materialTypes}">
|
||||
<option th:text="${materialType.materialTypeName}"
|
||||
th:value="${materialType.materialTypeID}"></option>
|
||||
</th:block>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><b><label for="simdut">Fichier SIMDUT: </label></b></td>
|
||||
<td><input id="simdut" name="simdut" type="file"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<button class="returnIndex" type="button">Retour</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit">Enregistrer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
<script>
|
||||
function switchUnits(unitSelect) {
|
||||
const quantityElem = document.querySelector("#quantity");
|
||||
quantityElem.dataset.unit = unitSelect.value;
|
||||
calculateMilliliters(quantityElem.value, unitSelect.value);
|
||||
}
|
||||
|
||||
function calculateMilliliters(quantity, unit) {
|
||||
let convertedQuantity = quantity;
|
||||
|
||||
switch (unit) {
|
||||
case "L":
|
||||
convertedQuantity = quantity * lTomL;
|
||||
break;
|
||||
case "gal":
|
||||
convertedQuantity = quantity * galTomL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
document.querySelector("#inventoryQuantity").value = convertedQuantity;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
109
src/main/resources/templates/material/edit.html
Normal file
109
src/main/resources/templates/material/edit.html
Normal file
@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title th:text="'Modification de ' + ${material.materialCode}"></title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
|
||||
<h1 class="materialCode" th:text="'Modification de ' + ${material.materialCode}"></h1>
|
||||
<div class="form">
|
||||
<form action="/material/editor" class="requireAuth" method="POST">
|
||||
<input id="materialID" name="materialID" th:value="${material.materialID}" type="hidden"/>
|
||||
<input name="materialCode" th:value="${material.materialCode}" type="hidden"/>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><b><label for="inventoryQuantity">Quantité en inventaire: </label></b></td>
|
||||
<td><input data-unit="mL" id="quantity" min="0" name="quantity"
|
||||
onchange="calculateMilliliters(this.value, this.dataset.unit)" step="0.01"
|
||||
th:value="${material.inventoryQuantity}" type="number"></td>
|
||||
|
||||
<!--Contient la quantité en millilitres-->
|
||||
<input id="inventoryQuantity" name="inventoryQuantity" th:value="${material.inventoryQuantity}"
|
||||
type="hidden"/>
|
||||
<td>
|
||||
<select id="units" name="units"
|
||||
onchange="switchUnits(this)">
|
||||
<option selected value="mL">Millilitres</option>
|
||||
<option value="L">Litres</option>
|
||||
<option value="gal">Gallons</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="materialType">Type de produit: </label></td>
|
||||
<td><select id="materialType" name="materialType">
|
||||
<th:block th:each="materialType : ${materialTypes}">
|
||||
<option th:attrappend="selected=${materialType.materialTypeName.equalsIgnoreCase('aucun') || materialType.materialTypeID == material.materialType.materialTypeID}"
|
||||
th:text="${materialType.materialTypeName}"
|
||||
th:value="${materialType.materialTypeID}"></option>
|
||||
</th:block>
|
||||
</select></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button id="showSIMDUT" type="button">Fichier SIMDUT</button>
|
||||
</td>
|
||||
<td>
|
||||
<button id="editSIMDUT" type="button">Modifier</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button class="returnIndex" type="button">Retour</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit">Enregistrer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
<script>
|
||||
(() => {
|
||||
const materialID = document.querySelector("#materialID").value;
|
||||
|
||||
document.querySelector("#showSIMDUT").addEventListener("click", () => {
|
||||
window.open(`/simdut/${materialID}`);
|
||||
});
|
||||
|
||||
document.querySelector("#editSIMDUT").addEventListener("click", () => {
|
||||
document.location.href = `/material/simdut/${materialID}`;
|
||||
});
|
||||
})();
|
||||
|
||||
function switchUnits(unitSelect) {
|
||||
const quantityElem = document.querySelector("#quantity");
|
||||
quantityElem.dataset.unit = unitSelect.value;
|
||||
calculateMilliliters(quantityElem.value, unitSelect.value);
|
||||
}
|
||||
|
||||
function calculateMilliliters(quantity, unit) {
|
||||
let convertedQuantity = quantity;
|
||||
|
||||
switch (unit) {
|
||||
case "L":
|
||||
convertedQuantity = quantity * lTomL;
|
||||
break;
|
||||
case "gal":
|
||||
convertedQuantity = quantity * galTomL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
document.querySelector("#inventoryQuantity").value = convertedQuantity;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
68
src/main/resources/templates/material/editor.html
Normal file
68
src/main/resources/templates/material/editor.html
Normal file
@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Modifier un produit</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
table {
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th, td {
|
||||
min-width: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<p class="success" th:if="${materialCode != null}"
|
||||
th:text="'Le produit \'' + ${materialCode} + '\' a bien été sauvegardé.'"></b>
|
||||
</p>
|
||||
<h1>Modifier des produits</h1>
|
||||
|
||||
<th:block th:if="${!materials.empty}">
|
||||
<table>
|
||||
<tr>
|
||||
<th>Produit</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
<th:block th:each="material : ${materials}">
|
||||
<tr>
|
||||
<td class="materialCode" th:data-materialID="${material.materialID}"
|
||||
th:text="${material.materialCode}"></td>
|
||||
<td th:text="${material.materialType.materialTypeName}"></td>
|
||||
<td>
|
||||
<button class="editor" th:data-materialID="${material.materialID}" type="button">Modifier
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:if="${materials.empty}">
|
||||
<b class="error">Aucun produit n'a été trouvé</b>
|
||||
</th:block>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
<script>
|
||||
/*<![CDATA[*/
|
||||
(() => {
|
||||
document.querySelectorAll(".editor").forEach((e) => {
|
||||
e.addEventListener("click", () => {
|
||||
const materialID = e.getAttribute("data-materialID");
|
||||
|
||||
document.location.href = "/material/editor/" + materialID;
|
||||
});
|
||||
});
|
||||
})();
|
||||
/*]]>*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
59
src/main/resources/templates/material/remover.html
Normal file
59
src/main/resources/templates/material/remover.html
Normal file
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Supprimer des produits</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
table {
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td {
|
||||
min-width: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<p class="success" th:if="${materialCode != null}"
|
||||
th:text="'Le produit \'' + ${materialCode} + '\' a bien été supprimée.'"></b>
|
||||
</p>
|
||||
<h1>Supprimer des produits</h1>
|
||||
|
||||
<form action="/material/remover/" class="requireAuth-remover" method="POST">
|
||||
<th:block th:if="${!materials.empty}">
|
||||
<table id="materialsList">
|
||||
<tr>
|
||||
<th>Produit</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
<th:block th:each="material : ${materials}">
|
||||
<tr>
|
||||
<td class="materialCode" th:data-materialID="${material.materialID}"
|
||||
th:text="${material.materialCode}"></td>
|
||||
<td th:text="${material.materialType.materialTypeName}"></td>
|
||||
<td>
|
||||
<button class="remover" th:data-code="${material.materialCode}"
|
||||
th:data-recipeID="${material.materialID}"
|
||||
type="button">Supprimer
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</th:block>
|
||||
</form>
|
||||
<th:block th:if="${materials.empty}">
|
||||
<b class="error">Aucun produit n'a été trouvé.</b>
|
||||
</th:block>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
31
src/main/resources/templates/material/simdut.html
Normal file
31
src/main/resources/templates/material/simdut.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout d'un produit</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<h1>Ajout d'un produit</h1>
|
||||
<div class="form">
|
||||
<form action="/material/simdut" class="requireAuth" enctype="multipart/form-data" method="POST">
|
||||
<input name="materialID" th:value="${materialID}" type="hidden"/>
|
||||
|
||||
<label for="simdut">Choisissez le fichier SIMDUT du produit:</label>
|
||||
<input id="simdut" name="simdut" type="file"/>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<input type="submit" value="Enregistrer"/>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
46
src/main/resources/templates/materialType/creator.html
Normal file
46
src/main/resources/templates/materialType/creator.html
Normal file
@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout d'un type de produit</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<h1>Ajout d'un type de produit</h1>
|
||||
|
||||
<div class="form">
|
||||
<form action="/materialType/creator" class="requireAuth" method="POST">
|
||||
<table>
|
||||
<tr>
|
||||
<td><label for="materialTypeName">Nom du type de produit: </label></td>
|
||||
<td><input id="materialTypeName" name="materialTypeName" required type="text"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="prefix">Préfixe (3 caractères): </label></td>
|
||||
<td><input id="prefix" maxlength="3" minlength="3" name="prefix" required type="text"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="usePercentages">Utiliser les pourcentages: </label></td>
|
||||
<td><input id="usePercentages" name="usePercentages" type="checkbox"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button class="returnIndex" type="button">Retour</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit">Enregistrer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
147
src/main/resources/templates/mix/creator.html
Normal file
147
src/main/resources/templates/mix/creator.html
Normal file
@ -0,0 +1,147 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout d'un mélange</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
table {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<h1>Ajout d'un mélange pour<br>la recette <span th:text="${recipe.recipeCode}"></span></h1>
|
||||
|
||||
<div class="form">
|
||||
<form action="/mix/creator" class="requireAuth" method="POST">
|
||||
<label for="mixType">Type de mélange: </label><input id="mixType" name="mixType" placeholder="ex: Teinture"
|
||||
required type="text"/>
|
||||
|
||||
<!-- Information nécessaire à la création des mélanges -->
|
||||
<input id="recipeID" name="recipeID" th:value="${recipe.recipeID}" type="hidden"/>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<!-- Produits -->
|
||||
<table id="products">
|
||||
<tr>
|
||||
<th>Produit</th>
|
||||
<th>Quantité
|
||||
<button type="button">+</button>
|
||||
</th>
|
||||
</tr>
|
||||
<tr id="row_0">
|
||||
<td><select class="materialList" name="product_0">
|
||||
<th:block th:each="material : ${materials}">
|
||||
<option th:class="'material_' + ${material.materialID}"
|
||||
th:classappend="${material.materialType.getUsePercentages()} ? usePercents"
|
||||
th:text="${!material.materialType.materialTypeName.equalsIgnoreCase('aucun')} ? '[' + ${material.materialType.prefix} + '] ' + ${material.materialCode} : ${material.materialCode}"
|
||||
th:value="${material.materialID}">
|
||||
</option>
|
||||
</th:block>
|
||||
</select></td>
|
||||
<td><input class="quantity" min="0" name="userQuantity" step="0.01" type="number"/>
|
||||
<span class="quantityUnit">mL</span></td>
|
||||
<td>
|
||||
<button data-remove="0" onclick="removeRow(this)" type="button">Retirer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button class="returnIndex" type="button">Retour</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit">Enregistrer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
<script>
|
||||
/*<![CDATA[*/
|
||||
(() => {
|
||||
// Compteurs des produits
|
||||
let productNbr = 1;
|
||||
|
||||
// Récupère les matériaux sous forme JSON depuis le thymeleaf et le valide.
|
||||
const productsText = "[[${materialsJson}]]";
|
||||
const products = JSON.parse(productsText.replace(/"/g, '"'));
|
||||
|
||||
document.querySelector("#products button").addEventListener("click", () => {
|
||||
let select = document.createElement("select");
|
||||
select.name = `product_${productNbr}`;
|
||||
|
||||
products.forEach(p => {
|
||||
let option = document.createElement("option");
|
||||
option.value = p.materialID;
|
||||
// Rajoute le préfixe du type de produit si le type de produit n'est pas <<Aucun>>
|
||||
option.innerHTML = (p.materialType.materialTypeName.toLowerCase() !== 'aucun' ? `[${p.materialType.prefix}] ` : "") + p.materialCode;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
let productSelection = document.createElement("td");
|
||||
productSelection.appendChild(select);
|
||||
|
||||
let input = document.createElement("input");
|
||||
input.type = "number";
|
||||
input.name = `quantity_${productNbr}`;
|
||||
input.step = "0.01";
|
||||
|
||||
let quantity = document.createElement("td");
|
||||
quantity.appendChild(input);
|
||||
|
||||
let removeButton = document.createElement("button");
|
||||
removeButton.type = "button";
|
||||
removeButton.dataset.remove = productNbr;
|
||||
removeButton.innerHTML = "Retirer";
|
||||
removeButton.onclick = removeRow(removeButton);
|
||||
|
||||
let removeColumn = document.createElement("td");
|
||||
removeColumn.appendChild(removeButton);
|
||||
|
||||
let row = document.createElement("tr");
|
||||
row.id = `row_${productNbr}`;
|
||||
row.appendChild(productSelection);
|
||||
row.appendChild(quantity);
|
||||
row.appendChild(removeColumn);
|
||||
|
||||
document.querySelector("#products tbody").appendChild(row);
|
||||
|
||||
productNbr++;
|
||||
});
|
||||
|
||||
document.querySelectorAll(".materialList").forEach(e => {
|
||||
e.addEventListener("change", () => {
|
||||
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
function removeRow(e) {
|
||||
e.addEventListener("click", () => {
|
||||
document.querySelector(`#row_${e.dataset.remove}`).remove();
|
||||
});
|
||||
}
|
||||
|
||||
function switchUnits(unitSelect) {
|
||||
document.querySelectorAll(".")
|
||||
}
|
||||
|
||||
/*]]*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
195
src/main/resources/templates/mix/editor.html
Normal file
195
src/main/resources/templates/mix/editor.html
Normal file
@ -0,0 +1,195 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Modifier un mélange</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
table {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body th:with="nbrProducts = 0">
|
||||
<th:block th:with="nbrSteps = 0">
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<h1>Modifier un mélange pour<br>la recette <span
|
||||
th:text="${recipeCode} + ' (' + ${mix.mixType.typeName} + ')'"></span></h1>
|
||||
<button id="removeMix" type="button">Supprimer</button>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div class="form">
|
||||
<form action="/mix/editor" class="requireAuth" method="POST">
|
||||
<!-- Information nécessaire à la création des mélanges -->
|
||||
<input name="mixID" th:value="${mix.mixID}" type="hidden"/>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<table id="products">
|
||||
<tr>
|
||||
<th>Produit</th>
|
||||
<th>Quantité
|
||||
<button type="button">+</button>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button class="returnIndex" type="button">Retour</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit">Enregistrer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
<script>
|
||||
/*<![CDATA[*/
|
||||
(() => {
|
||||
// Compteurs des produits et des étapes
|
||||
let productNbr = 0;
|
||||
|
||||
const mixText = "[[${mixJson}]]";
|
||||
const mix = JSON.parse(mixText.replace(/"/g, '"'));
|
||||
// Récupère les matériaux sous forme JSON depuis le thymeleaf et le valide.
|
||||
const materialsText = "[[${materialsJson}]]";
|
||||
const materials = JSON.parse(materialsText.replace(/"/g, '"').replace(/'/g, '\'').replace(/\r\n/g, ""));
|
||||
|
||||
// Ajoute les produits déjà présents dans la recette
|
||||
mix.mixQuantities.forEach((q) => {
|
||||
let select = document.createElement("select");
|
||||
select.name = `product_${productNbr}`;
|
||||
|
||||
materials.forEach(p => {
|
||||
const materialID = p.materialID;
|
||||
let option = document.createElement("option");
|
||||
option.value = materialID;
|
||||
option.selected = q.material.materialID === materialID;
|
||||
// Rajoute le préfixe du type de produit si le type de produit n'est pas <<Aucun>>
|
||||
option.innerHTML = (p.materialType.materialTypeName.toLowerCase() !== 'aucun' ? `[${p.materialType.prefix}] ` : "") + p.materialCode;
|
||||
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
let input = document.createElement("input");
|
||||
input.type = "number";
|
||||
input.name = `quantity_${productNbr}`;
|
||||
input.value = q.quantity;
|
||||
input.step = 0.01;
|
||||
|
||||
let product = document.createElement("td");
|
||||
product.appendChild(select);
|
||||
|
||||
let quantity = document.createElement("td");
|
||||
quantity.appendChild(input);
|
||||
|
||||
let removeButton = document.createElement("button");
|
||||
removeButton.type = "button";
|
||||
removeButton.dataset.remove = productNbr;
|
||||
removeButton.innerHTML = "Retirer";
|
||||
removeButton.onclick = removeRow(removeButton);
|
||||
|
||||
let removeColumn = document.createElement("td");
|
||||
removeColumn.appendChild(removeButton);
|
||||
|
||||
let row = document.createElement("tr");
|
||||
row.id = `row_${productNbr}`;
|
||||
row.appendChild(product);
|
||||
row.appendChild(quantity);
|
||||
row.appendChild(removeColumn);
|
||||
|
||||
document.querySelector("#products tbody").appendChild(row);
|
||||
|
||||
productNbr++;
|
||||
});
|
||||
|
||||
document.querySelector("#products button").addEventListener("click", () => {
|
||||
let select = document.createElement("select");
|
||||
select.name = `product_${productNbr}`;
|
||||
|
||||
materials.forEach(p => {
|
||||
let option = document.createElement("option");
|
||||
option.value = p.materialID;
|
||||
// Rajoute le préfixe du type de produit si le type de produit n'est pas <<Aucun>>
|
||||
option.innerHTML = (p.materialType.materialTypeName.toLowerCase() !== 'aucun' ? `[${p.materialType.prefix}] ` : "") + p.materialCode;
|
||||
select.appendChild(option);
|
||||
});
|
||||
|
||||
let input = document.createElement("input");
|
||||
input.type = "number";
|
||||
input.name = `quantity_${productNbr}`;
|
||||
input.step = 0.01;
|
||||
|
||||
let product = document.createElement("td");
|
||||
product.appendChild(select);
|
||||
|
||||
let quantity = document.createElement("td");
|
||||
quantity.appendChild(input);
|
||||
|
||||
let removeButton = document.createElement("button");
|
||||
removeButton.type = "button";
|
||||
removeButton.dataset.remove = productNbr;
|
||||
removeButton.innerHTML = "Retirer";
|
||||
removeButton.onclick = removeRow(removeButton);
|
||||
|
||||
let removeColumn = document.createElement("td");
|
||||
removeColumn.appendChild(removeButton);
|
||||
|
||||
let row = document.createElement("tr");
|
||||
row.appendChild(product);
|
||||
row.appendChild(quantity);
|
||||
row.appendChild(removeButton);
|
||||
|
||||
document.querySelector("#products tbody").appendChild(row);
|
||||
|
||||
productNbr++;
|
||||
});
|
||||
|
||||
document.querySelector("#removeMix").addEventListener("click", () => {
|
||||
let errorP = document.querySelector(".error");
|
||||
errorP.innerHTML = "";
|
||||
|
||||
const password = prompt("Quel est votre mot de passe?");
|
||||
|
||||
let data = {};
|
||||
data.password = password;
|
||||
|
||||
axios.post("/password/valid", data)
|
||||
.then(r => {
|
||||
if (r.data) {
|
||||
document.location.href = `/mix/remover/${mix.mixID}`;
|
||||
} 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);
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
function removeRow(e) {
|
||||
e.addEventListener("click", () => {
|
||||
document.querySelector(`#row_${e.dataset.remove}`).remove();
|
||||
});
|
||||
}
|
||||
|
||||
/*]]*/
|
||||
</script>
|
||||
</th:block>
|
||||
</body>
|
||||
</html>
|
21
src/main/resources/templates/recipe/created.html
Normal file
21
src/main/resources/templates/recipe/created.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout d'une recette</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
|
||||
<h1>Ajout d'une recette</h1>
|
||||
<p th:text="'La recette ' + ${recipeCode} + ' à été enregistrée. Vous pouvez maintenant ajouter les ingrédients.'"></p>
|
||||
<button th:onclick="'document.location.href=\'/recipe/editor/' + ${recipeID} + '\''">Continuer</button>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
61
src/main/resources/templates/recipe/creator.html
Normal file
61
src/main/resources/templates/recipe/creator.html
Normal file
@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ajout d'une recette</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
|
||||
<h1>Ajout d'une recette</h1>
|
||||
<div class="form">
|
||||
<form action="/recipe/creator" class="requireAuth" id="recipe-form" method="POST">
|
||||
<table>
|
||||
<tr>
|
||||
<td><label for="recipeCode">Couleur: </label></td>
|
||||
<td><input id="recipeCode" name="recipeCode" required type="text"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="company">Bannière: </label></td>
|
||||
<td><select id="company" name="company">
|
||||
<th:block th:each="company : ${companies}">
|
||||
<option th:text="${company.companyName}" th:value="${company.companyID}"></option>
|
||||
</th:block>
|
||||
</select></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="recipeDescription">Courte description: </label></td>
|
||||
<td><input id="recipeDescription" name="recipeDescription" required type="text"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="sample">Échantillon: </label></td>
|
||||
<td><input id="sample" name="sample" required type="number"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="approbationDate">Date d'approbation: </label></td>
|
||||
<td><input id="approbationDate" name="approbationDate" type="date"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="remark">Remarque: </label></td>
|
||||
<td><textarea cols="30" form="recipe-form" id="remark" name="remark" rows="10"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<button class="returnIndex" type="button">Retour</button>
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit">Enregistrer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
299
src/main/resources/templates/recipe/edit.html
Normal file
299
src/main/resources/templates/recipe/edit.html
Normal file
@ -0,0 +1,299 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title th:text="'Modification de ' + ${recipe.recipeCode}"></title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/forms.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.recipe table {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.recipe td, .recipe th {
|
||||
min-width: 100px;
|
||||
text-align: center;
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
|
||||
<h1 th:text="'Modification de ' + ${recipe.recipeCode}"></h1>
|
||||
<button id="gotoRecipe" type="button">Voir</button>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<div class="form">
|
||||
<form action="/recipe/editor" class="requireAuth" method="POST">
|
||||
<input id="recipeID" name="recipeID" th:value="${recipe.recipeID}" type="hidden"/>
|
||||
<input name="recipeCode" th:value="${recipe.recipeCode}" type="hidden"/>
|
||||
|
||||
<table class="mainTable">
|
||||
<tr>
|
||||
<td>
|
||||
<table>
|
||||
<tr>
|
||||
<td><label for="company">Bannière: </label></td>
|
||||
<td>
|
||||
<select id="company" name="company">
|
||||
<th:block th:each="company : ${companies}">
|
||||
<option th:selected="${recipe.company.equals(company)}"
|
||||
th:text="${company.companyName}"
|
||||
th:value="${company.companyID}"></option>
|
||||
</th:block>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="recipeDescription">Description: </label></td>
|
||||
<td><input id="recipeDescription" name="recipeDescription"
|
||||
th:value="${recipe.recipeDescription}"
|
||||
type="text"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="sample">Échantillon: </label></td>
|
||||
<td><input id="sample" name="sample" th:value="${recipe.sample}" type="number"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="approbationDate">Date d'approbation: </label></td>
|
||||
<td><input id="approbationDate" name="approbationDate"
|
||||
th:value="${recipe.approbationDate}"
|
||||
type="date"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="remark">Remarque: </label></td>
|
||||
<td><textarea cols="30" id="remark" name="remark" rows="10"
|
||||
th:text="${recipe.remark}"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<table>
|
||||
<th:block th:each="mix : ${mixes}">
|
||||
<tr>
|
||||
<td>
|
||||
<p th:text="${mix.mixType.typeName} + ':'"></p>
|
||||
<th:block th:if="${mix != null}">
|
||||
<button class="mixEditor" th:data-mixID="${mix.mixID}" type="button">
|
||||
Modifier
|
||||
</button>
|
||||
</th:block>
|
||||
</td>
|
||||
<td>
|
||||
<div class="recipe">
|
||||
<table style="margin-left: 50px">
|
||||
<!-- Produits -->
|
||||
<tr>
|
||||
<th>Produit</th>
|
||||
<th>Type</th>
|
||||
<th>Quantité</th>
|
||||
</tr>
|
||||
<th:block th:each="mixQuantity : ${mix.mixQuantities}"
|
||||
th:with="material = ${mixQuantity.material}">
|
||||
<tr>
|
||||
<td th:classappend="${material.isMixType()} ? '' : materialCode"
|
||||
th:data-materialID="${material.materialID}"
|
||||
th:text="${material.materialCode}"></td>
|
||||
<td>
|
||||
<p th:text="${material.materialType.materialTypeName}"></p>
|
||||
</td>
|
||||
<td th:text="${mixQuantity.quantity}"></td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button id="newMix" type="button">Ajouter un mélange</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Étapes
|
||||
</td>
|
||||
<td>
|
||||
<table id="steps">
|
||||
<tr>
|
||||
<th>
|
||||
<button id="addStep" type="button">+</button>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Images -->
|
||||
<th:block th:each="image : ${images}">
|
||||
<tr>
|
||||
<td class="centerTD">
|
||||
<img alt="Image supprimée ou corrompue" th:src="'/images/' + ${image}" width="400px"/>
|
||||
</td>
|
||||
<td>
|
||||
<button class="deleteImg" th:data-image="${image}" type="button">Supprimer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
<tr>
|
||||
<td>
|
||||
<button id="addImg" th:data-recipeID="${recipe.recipeID}" type="button">Ajouter une image
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="mainTableEndButtons">
|
||||
<td class="centerTd">
|
||||
<button class="returnIndex" type="button">Retour</button>
|
||||
</td>
|
||||
<td class="centerTd">
|
||||
<button type="submit">Enregistrer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
<script>
|
||||
/*[CDATA[*/
|
||||
(() => {
|
||||
const errorP = document.querySelector(".error");
|
||||
|
||||
document.querySelector("#gotoRecipe").addEventListener("click", () => {
|
||||
window.open("/recipe/explore/" + document.querySelector("#recipeID").value, "_blank");
|
||||
});
|
||||
|
||||
document.querySelectorAll(".mixEditor").forEach(e => {
|
||||
e.addEventListener("click", () => {
|
||||
const mixID = e.getAttribute("data-mixID");
|
||||
|
||||
document.location.href = "/mix/editor/" + mixID;
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelector("#newMix").addEventListener("click", () => {
|
||||
const recipeID = "[[${recipe.recipeID}]]";
|
||||
|
||||
document.location.href = "/mix/creator/" + recipeID;
|
||||
});
|
||||
|
||||
document.querySelectorAll(".deleteImg").forEach(e => {
|
||||
e.addEventListener("click", () => {
|
||||
let data = {};
|
||||
|
||||
data['image'] = e.getAttribute("data-image");
|
||||
data['password'] = prompt("Quel est votre mot de passe?");
|
||||
|
||||
errorP.innerHTML = "";
|
||||
|
||||
axios.post("/images/delete", data)
|
||||
.then(r => {
|
||||
const data = r.data;
|
||||
|
||||
if (data['error'] !== undefined) {
|
||||
errorP.innerHTML = data['error'];
|
||||
} else {
|
||||
document.location.reload();
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
errorP.innerHTML = "Une erreur est survenue lors de l'envoie des informations vers le serveur.";
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelector("#addImg").addEventListener("click", () => {
|
||||
document.location.href = "/images/add/[[${recipe.recipeID}]]";
|
||||
});
|
||||
|
||||
let stepNbr = 0;
|
||||
|
||||
const recipeText = "[[${recipeJSON}]]";
|
||||
const recipeJSON = JSON.parse(recipeText.replace(/"/g, '"').replace(/'/g, '\'').replace(/\r\n/g, ""));
|
||||
|
||||
// Ajoute les étapes déjà présentes dans la recette
|
||||
recipeJSON.recipeSteps.forEach((s) => {
|
||||
let input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.name = `step_${stepNbr}`;
|
||||
input.value = s.stepMessage;
|
||||
|
||||
let column = document.createElement("td");
|
||||
column.appendChild(input);
|
||||
|
||||
let row = document.createElement("tr");
|
||||
row.appendChild(column);
|
||||
|
||||
document.querySelector("#steps tbody").appendChild(row);
|
||||
|
||||
stepNbr++;
|
||||
});
|
||||
|
||||
document.querySelector("#addStep").addEventListener("click", () => {
|
||||
let input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.name = `step_${stepNbr}`;
|
||||
|
||||
let column = document.createElement("td");
|
||||
column.appendChild(input);
|
||||
|
||||
let row = document.createElement("tr");
|
||||
row.appendChild(column);
|
||||
|
||||
document.querySelector("#steps tbody").appendChild(row);
|
||||
|
||||
stepNbr++;
|
||||
});
|
||||
})();
|
||||
/*]]*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
83
src/main/resources/templates/recipe/editor.html
Normal file
83
src/main/resources/templates/recipe/editor.html
Normal file
@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Modifier une recette</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/index.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
table {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
th {
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.companyTabTitle {
|
||||
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<p class="success" th:if="${recipeCode != null}"
|
||||
th:text="'La recette pour la couleur ' + ${recipeCode} + ' a bien été sauvegardée.'"></p>
|
||||
|
||||
<h1>Modifier une recette</h1>
|
||||
<th:block th:if="${!recipes.empty}">
|
||||
<th:block th:each="company : ${recipes.keySet()}">
|
||||
<h2 class="companyTabTitle" th:data-companyName="${company.companyName}"
|
||||
th:text="${company.companyName}"></h2>
|
||||
|
||||
<th:block th:if="${!recipes.get(company).empty}">
|
||||
<table style="display:none" th:id="'recipes_' + ${company.companyName}">
|
||||
<tr>
|
||||
<th>Couleur</th>
|
||||
<th>Description</th>
|
||||
<th>Échantillon</th>
|
||||
</tr>
|
||||
<th:block th:each="recipe : ${recipes.get(company)}">
|
||||
<tr class="recipeRow" th:data-approbationDate="${recipe.approbationDate}">
|
||||
<td th:text="${recipe.recipeCode}"></td>
|
||||
<td class="centerCell recipeDescription" th:text="${recipe.recipeDescription}"></td>
|
||||
<td class="centerCell" th:text="${recipe.sample}"></td>
|
||||
<td>
|
||||
<button class="editRecipe" th:data-code="${recipe.recipeCode}"
|
||||
th:data-recipeID="${recipe.recipeID}" type="button">
|
||||
Modifier
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<th:block th:if="${recipes.empty}">
|
||||
<b class="error">Aucune bannière n'a été trouvée.</b>
|
||||
</th:block>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
<script>
|
||||
(() => {
|
||||
document.querySelectorAll(".editRecipe").forEach((e) => {
|
||||
e.addEventListener("click", () => {
|
||||
const recipeID = e.getAttribute("data-recipeID");
|
||||
|
||||
document.location.href = "/recipe/editor/" + recipeID;
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
373
src/main/resources/templates/recipe/explore.html
Normal file
373
src/main/resources/templates/recipe/explore.html
Normal file
@ -0,0 +1,373 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title th:text="'Couleur ' + ${recipe.recipeCode}"></title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/index.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
td, th {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
section {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.mixes {
|
||||
margin: 0;
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.mixes td, .mixes th {
|
||||
border: 1px solid black;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.recipeLocation {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.notEnough td {
|
||||
background-color: #ffb3b3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<p class="success" th:text="${success}"></p>
|
||||
|
||||
<h1 th:text="'Couleur ' + ${recipe.recipeCode}"></h1>
|
||||
<button id="modifyRecipe" type="button">Modifier</button>
|
||||
<button id="useSubmit" type="button">Utiliser</button>
|
||||
<br/>
|
||||
<select id="unitsSelect" onchange="changeUnits(this, '.inventoryQuantity', '.inventoryQuantityUnits')">
|
||||
<option selected value="mL">Millilitres</option>
|
||||
<option value="L">Litres</option>
|
||||
<option value="gal">Gallons</option>
|
||||
</select>
|
||||
|
||||
<input id="recipeID" name="recipeID" th:value="${recipe.recipeID}" type="hidden"/>
|
||||
|
||||
<table class="mainTable">
|
||||
<tr>
|
||||
<td>
|
||||
<table>
|
||||
<tr>
|
||||
<td><b>Couleur: </b></td>
|
||||
<td th:text="${recipe.recipeCode}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Bannière: </b></td>
|
||||
<td th:text="${recipe.company.companyName}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Description: </b></td>
|
||||
<td th:text="${recipe.recipeDescription}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Échantillon: </b></td>
|
||||
<td th:text="${recipe.sample}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Date d'approbation: </b></td>
|
||||
<th:block th:if="${recipe.approbationDate != ''}">
|
||||
<td th:text="${recipe.approbationDate}"></td>
|
||||
</th:block>
|
||||
<th:block th:if="${recipe.approbationDate == ''}">
|
||||
<td>Non approuvée</td>
|
||||
</th:block>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Remarque: </b></td>
|
||||
<td th:text="${recipe.remark}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Note: </b></td>
|
||||
<td>
|
||||
<textarea cols="30" id="note" name="note" rows="10" th:text="${recipe.note}"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<table style="border-spacing: 20px;">
|
||||
<th:block th:each="mix : ${mixes}">
|
||||
<tr>
|
||||
<td><b th:text="${mix.mixType.typeName} + ':'"></b><br><br>
|
||||
<label for="location"> Position: </label><input class="recipeLocation" id="location"
|
||||
name="location"
|
||||
th:data-mixID="${mix.mixID}"
|
||||
th:value="${mix.location}"
|
||||
type="text"/></td>
|
||||
<td>
|
||||
<table class="mixes" th:id="'recipe-' + ${mix.mixID}">
|
||||
<tr>
|
||||
<th>Produit</th>
|
||||
<th>Type</th>
|
||||
<th>Quantité</th>
|
||||
<!-- Unités -->
|
||||
<td></td>
|
||||
<!-- Changement des quantités -->
|
||||
<td></td>
|
||||
</tr>
|
||||
<!-- Produits -->
|
||||
<th:block th:each="mixQuantity : ${mix.mixQuantities}"
|
||||
th:with="material = ${mixQuantity.material}">
|
||||
<tr th:id="'material-' + ${material.materialID}">
|
||||
<td th:classappend="${material.isMixType()} ? '' : materialCode"
|
||||
th:data-materialID="${material.materialID}"
|
||||
th:text="${material.materialCode}"></td>
|
||||
<td>
|
||||
<p th:text="${material.materialType.materialTypeName}"></p>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inventoryQuantity"
|
||||
th:data-quantityML="${mixQuantity.quantity}"
|
||||
th:text="${mixQuantity.quantity}"></p>
|
||||
</td>
|
||||
<td>
|
||||
<p class="inventoryQuantityUnits">mL</p>
|
||||
</td>
|
||||
<th:block th:if="${!material.isMixType()}">
|
||||
<td><input class="quantityCustomizer" min="0" step="0.01"
|
||||
th:data-materialID="${material.materialID}"
|
||||
th:data-mixID="${mix.mixID}"
|
||||
th:value="${mixQuantity.quantity}" type="number"/></td>
|
||||
</th:block>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</td>
|
||||
<td>
|
||||
<button class="useMixSubmit" type="button">Utiliser</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr/>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
<!-- Étapes -->
|
||||
<th:block th:if="${!recipe.recipeSteps.isEmpty()}">
|
||||
<tr>
|
||||
<td>
|
||||
<b>Étapes: </b>
|
||||
</td>
|
||||
<td>
|
||||
<ol>
|
||||
<th:block th:each="step : ${recipe.recipeSteps}">
|
||||
<li th:text="${step.stepMessage}"></li>
|
||||
</th:block>
|
||||
</ol>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="mainTableEndButtons">
|
||||
<td>
|
||||
<button id="formSubmit" type="button">Sauvegarder</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Images -->
|
||||
<th:block th:each="image : ${images}">
|
||||
<tr>
|
||||
<td colspan="2" style="text-align: center;">
|
||||
<img alt="Image supprimée ou corrompue" th:src="'/images/' + ${image}" width="400px"/>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
<script>
|
||||
(() => {
|
||||
document.querySelector("#modifyRecipe").addEventListener("click", () => {
|
||||
const recipeID = document.querySelector("#recipeID").value;
|
||||
document.location.href = `/recipe/editor/${recipeID}`;
|
||||
});
|
||||
|
||||
document.querySelectorAll(".quantityCustomizer").forEach((e) => {
|
||||
// Modifie les quantités de tous les produits
|
||||
e.addEventListener("change", () => {
|
||||
const value = e.valueAsNumber;
|
||||
const oldValue = e.defaultValue;
|
||||
|
||||
const mixID = e.dataset.mixid;
|
||||
|
||||
document.querySelectorAll(".quantityCustomizer").forEach((elem) => {
|
||||
if (elem.dataset.mixid === mixID) {
|
||||
const defaultValue = elem.defaultValue;
|
||||
const newValue = (defaultValue * value) / oldValue;
|
||||
|
||||
elem.value = Math.round(newValue * 100) / 100;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const mixes = document.querySelectorAll(".mixes");
|
||||
let maxWidth = 0;
|
||||
mixes.forEach((e) => {
|
||||
const width = e.style.width;
|
||||
|
||||
if (width > maxWidth) {
|
||||
maxWidth = width;
|
||||
}
|
||||
});
|
||||
mixes.forEach((e) => {
|
||||
e.style.width = maxWidth;
|
||||
});
|
||||
|
||||
document.querySelector("#formSubmit").addEventListener("click", () => {
|
||||
let formData = {};
|
||||
|
||||
// Identifiant de la recette
|
||||
formData['recipeID'] = document.querySelector("#recipeID").value;
|
||||
|
||||
// Position
|
||||
formData['locations'] = {};
|
||||
document.querySelectorAll(".recipeLocation").forEach((e) => {
|
||||
formData['locations'][e.dataset.mixid] = e.value;
|
||||
});
|
||||
|
||||
formData['note'] = document.querySelector("#note").value;
|
||||
|
||||
sendPost(formData, "/recipe/explore");
|
||||
});
|
||||
|
||||
document.querySelector("#useSubmit").addEventListener("click", () => {
|
||||
let formData = {};
|
||||
|
||||
document.querySelectorAll(".quantityCustomizer").forEach(e => {
|
||||
const materialID = e.dataset.materialid;
|
||||
const mixID = e.dataset.mixid;
|
||||
|
||||
if (formData[mixID] === undefined) {
|
||||
formData[mixID] = {};
|
||||
}
|
||||
|
||||
formData[mixID][materialID] = e.value;
|
||||
});
|
||||
|
||||
document.querySelectorAll(".notEnough").forEach(e => {
|
||||
e.classList.remove("notEnough");
|
||||
});
|
||||
|
||||
sendPost(formData, "/inventory/use", r => {
|
||||
const splitReason = r.split("-");
|
||||
const mixID = splitReason[0];
|
||||
const materialID = splitReason[1];
|
||||
|
||||
document.querySelector(`#recipe-${mixID}`).querySelector(`#material-${materialID}`).classList.add("notEnough");
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll(".useMixSubmit").forEach(e => {
|
||||
e.addEventListener("click", () => {
|
||||
let formData = {};
|
||||
|
||||
e.parentElement.parentElement.querySelectorAll(".quantityCustomizer").forEach(elem => {
|
||||
const materialID = elem.getAttribute("data-materialID");
|
||||
const mixID = elem.getAttribute("data-mixID");
|
||||
|
||||
if (formData[mixID] === undefined) {
|
||||
formData[mixID] = {};
|
||||
}
|
||||
|
||||
formData[mixID][materialID] = elem.value;
|
||||
});
|
||||
|
||||
document.querySelectorAll(".notEnough").forEach(elem => {
|
||||
elem.classList.remove("notEnough");
|
||||
});
|
||||
|
||||
sendPost(formData, "/inventory/use", r => {
|
||||
const splitReason = r.split("-");
|
||||
const mixID = splitReason[0];
|
||||
const materialID = splitReason[1];
|
||||
|
||||
document.querySelector(`#recipe-${mixID}`).querySelector(`#material-${materialID}`).classList.add("notEnough");
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
function sendPost(data, url, errorCallback) {
|
||||
const successP = document.querySelector(".success");
|
||||
const errorP = document.querySelector(".error");
|
||||
|
||||
successP.innerHTML = "";
|
||||
errorP.innerHTML = "";
|
||||
|
||||
axios.post(url, data)
|
||||
.then(r => {
|
||||
const data = r.data;
|
||||
|
||||
if (data['success'] !== undefined) {
|
||||
successP.innerHTML = data['success'];
|
||||
} else if (data['error'] !== undefined) {
|
||||
errorP.innerHTML = data['error'];
|
||||
|
||||
if (typeof errorCallback !== 'undefined') {
|
||||
errorCallback(data['reason']);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
errorP.innerHTML = "Une erreur est survenue lors de l'envoie des informations vers le serveur.";
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
70
src/main/resources/templates/recipe/remover.html
Normal file
70
src/main/resources/templates/recipe/remover.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Supprimer des recettes</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/index.css" rel="stylesheet"/>
|
||||
|
||||
<style>
|
||||
table {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
th {
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section>
|
||||
<p class="error" th:text="${error}"></p>
|
||||
<p class="success" th:if="${recipeCode != null}"
|
||||
th:text="'La recette pour la couleur ' + ${recipeCode} + ' a bien été supprimée.'"></p>
|
||||
|
||||
<h1>Supprimer une recette</h1>
|
||||
<form action="/recipe/remover/" class="requireAuth-remover" method="POST">
|
||||
<th:block th:if="${!recipes.empty}">
|
||||
<th:block th:each="company : ${recipes.keySet()}">
|
||||
<h2 class="companyTabTitle" th:data-companyName="${company.companyName}"
|
||||
th:text="${company.companyName}"></h2>
|
||||
|
||||
<th:block th:if="${!recipes.get(company).empty}">
|
||||
<table style="display:none" th:id="'recipes_' + ${company.companyName}">
|
||||
<tr>
|
||||
<th>Couleur</th>
|
||||
<th>Description</th>
|
||||
<th>Échantillon</th>
|
||||
</tr>
|
||||
<th:block th:each="recipe : ${recipes.get(company)}">
|
||||
<tr class="recipeRow" th:data-approbationDate="${recipe.approbationDate}">
|
||||
<td th:text="${recipe.recipeCode}"></td>
|
||||
<td class="centerCell recipeDescription" th:text="${recipe.recipeDescription}"></td>
|
||||
<td class="centerCell" th:text="${recipe.sample}"></td>
|
||||
<td>
|
||||
<button class="remover" th:data-code="${recipe.recipeCode}"
|
||||
th:data-recipeID="${recipe.recipeID}" type="button">
|
||||
Supprimer
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</th:block>
|
||||
</table>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<th:block th:if="${recipes.empty}">
|
||||
<b class="error">Aucune bannière n'a été trouvée.</b>
|
||||
</th:block>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
24
src/main/resources/templates/touchup.html
Normal file
24
src/main/resources/templates/touchup.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Génération du PDF d'un kit de retouche</title>
|
||||
|
||||
<link href="/css/main.css" rel="stylesheet"/>
|
||||
<link href="/css/index.css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body>
|
||||
<section>
|
||||
<h1>Génération du PDF d'un kit de retouche</h1>
|
||||
|
||||
<form action="/touchup" method="POST">
|
||||
<label for="kitName"><b>Nom du kit de retouche</b></label>
|
||||
<input id="kitName" name="kitName" type="text"/>
|
||||
<br/>
|
||||
|
||||
<input type="submit" value="Générer"/>
|
||||
</form>
|
||||
</section>
|
||||
<script src="/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user