v1.2.0_beta

This commit is contained in:
William Nolin 2020-01-23 08:47:31 -05:00
parent 6fb6a4829c
commit 4f7336e982
63 changed files with 2026 additions and 2018 deletions

View File

@ -25,17 +25,18 @@ public class InitialDataLoader implements ApplicationListener<ApplicationReadyEv
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
if (!materialTypeService.getByName(MaterialType.DEFAULT_MATERIAL_TYPE_NAME).isPresent()) {
createDefaultMaterialType();
}
if (!materialTypeService.getByName(MaterialType.DEFAULT_MATERIAL_TYPE_NAME).isPresent())
createInitialMaterialType(MaterialType.DEFAULT_MATERIAL_TYPE_NAME);
if (!materialTypeService.getByName(MaterialType.BASE_MATERIAL_TYPE_NAME).isPresent())
createInitialMaterialType(MaterialType.BASE_MATERIAL_TYPE_NAME);
}
private void createDefaultMaterialType() {
MaterialType defaultMaterialType = new MaterialType(MaterialType.DEFAULT_MATERIAL_TYPE_NAME, "", false);
private void createInitialMaterialType(String name) {
MaterialType defaultMaterialType = new MaterialType(name, "", false);
Optional<MaterialType> optionalSavedMaterialType = materialTypeService.save(defaultMaterialType);
if (!optionalSavedMaterialType.isPresent()) {
ColorRecipesExplorerApplication.LOGGER.warn("Échec de la création du type de produit par défaut.");
ColorRecipesExplorerApplication.LOGGER.warn(String.format("Échec de la création du type de produit par défaut '%s'.", name));
}
}
}

View File

@ -27,6 +27,7 @@ public enum ResponseCode {
FILE_NOT_IMAGE(24, ResponseCodeType.ERROR, 0),
RECIPE_NOT_FOUND_NO_PARAMS(25, ResponseCodeType.ERROR, 0),
MATERIAL_NOT_FOUND_BY_NAME(26, ResponseCodeType.ERROR, 1),
SUCCESS_DELETING_COMPANY(27, ResponseCodeType.SUCCESS, 1),
// HTTP Errors
_500(100, ResponseCodeType.ERROR, 0),

View File

@ -1,73 +1,32 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import lombok.*;
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")
@Data
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
@NoArgsConstructor
public class Company extends BeanModel implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private int companyID;
@NonNull
@NotNull
@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);
}
@Override
public int hashCode() {
return Objects.hash(companyID, companyName);
}
}

View File

@ -1,115 +1,53 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import lombok.*;
import org.hibernate.annotations.ColumnDefault;
import javax.persistence.*;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Objects;
@Entity
@Table(name = "materials")
@Data
@RequiredArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class Material extends BeanModel implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer materialID = 0;
@NonNull
@NotNull
@NotEmpty
@Column(unique = true)
private String materialCode;
@NonNull
@NotNull
private float inventoryQuantity = 0;
@ColumnDefault("0")
private float inventoryQuantity;
@NonNull
@NotNull
@ColumnDefault("false")
private boolean isMixType = false;
private boolean isMixType;
@NonNull
@NotNull
@ManyToOne
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 +
'}';
public boolean isMixType() {
return isMixType;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Material material = (Material) o;
return Objects.equals(materialID, material.materialID) &&
Objects.equals(materialCode, material.materialCode);
}
@Override
public int hashCode() {
return Objects.hash(materialID, materialCode, inventoryQuantity, isMixType, materialType);
}
}

View File

@ -1,111 +1,48 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import lombok.*;
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
@Data
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
@NoArgsConstructor
public class MaterialType extends BeanModel implements Serializable {
public static final String DEFAULT_MATERIAL_TYPE_NAME = "Aucun";
public static final String BASE_MATERIAL_TYPE_NAME = "Base";
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer materialTypeID;
@Column(unique = true)
@NonNull
@NotNull
@Column(unique = true)
private String materialTypeName;
@Column(unique = true)
@NonNull
@NotNull
@Column(unique = true)
private String prefix;
@NonNull
@NotNull
@ColumnDefault("false")
private Boolean usePercentages = false;
private Boolean usePercentages;
@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);
}
@Override
public int hashCode() {
return Objects.hash(materialTypeID, materialTypeName, prefix, usePercentages, materials);
}
}

View File

@ -1,13 +1,18 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import lombok.*;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
@Entity
@Table(name = "mixes")
@Data
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
@NoArgsConstructor
public class Mix extends BeanModel implements Serializable {
@Id
@ -15,10 +20,13 @@ public class Mix extends BeanModel implements Serializable {
@Basic
private Integer mixID = 0;
@NonNull
@ToString.Exclude
@NotNull
@ManyToOne
private Recipe recipe;
@NonNull
@NotNull
@ManyToOne
private MixType mixType;
@ -29,84 +37,9 @@ public class Mix extends BeanModel implements Serializable {
// 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);
}
@Override
public int hashCode() {
return Objects.hash(mixID, recipe, mixType, mixQuantities, location);
}
}

View File

@ -1,14 +1,18 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Objects;
@Entity
@Table(name = "mixQuantities")
@Data
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
@NoArgsConstructor
public class MixQuantity extends BeanModel implements Serializable {
@Id
@ -16,86 +20,24 @@ public class MixQuantity extends BeanModel implements Serializable {
@Basic
private Integer mixQuantityID = 0;
@NonNull
@ToString.Exclude
@NotNull
@JsonIgnore
@ManyToOne
private Mix mix;
@NonNull
@NotNull
@ManyToOne
private Material material;
@NonNull
@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 Objects.equals(mixQuantityID, that.mixQuantityID) &&
Objects.equals(mix, that.mix) &&
Objects.equals(material, that.material) &&
Objects.equals(quantity, that.quantity);
}
@Override
public int hashCode() {
return Objects.hash(mixQuantityID, mix, material, quantity);
}
}

View File

@ -1,84 +1,35 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import lombok.*;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Objects;
@Entity
@Table(name = "mixTypes") // TODO retirer @Table
@Table(name = "mixTypes")
@Data
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
@NoArgsConstructor
public class MixType extends BeanModel implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private int typeID;
@NonNull
@NotNull
@Column(unique = true)
private String typeName;
@NonNull
@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);
}
@Override
public int hashCode() {
return Objects.hash(typeID, typeName, material);
}
}

View File

@ -1,34 +1,41 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import org.hibernate.validator.constraints.Length;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Entity
@Table(name = "recipes")
@Data
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
@NoArgsConstructor
public class Recipe extends BeanModel implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer recipeID = 0;
@Length(min = 2)
@NonNull
@NotNull
@Length(min = 2)
private String recipeCode;
@ManyToOne
@NonNull
@NotNull
@ManyToOne
private Company company;
@NonNull
@NotNull
private String recipeDescription;
@NonNull
@NotNull
private int sample;
@ -45,138 +52,32 @@ public class Recipe extends BeanModel implements Serializable {
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
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 == null ? new ArrayList<>() : 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 +
'}';
}
public Material getBase() {
if (recipeMixes.isEmpty() || recipeMixes.stream().allMatch(m -> m.getMixQuantities().isEmpty())) return null;
@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);
}
Material base = recipeMixes
.stream()
.map(mix -> mix
.getMixQuantities()
.stream()
.filter(mq -> mq.getMaterial().getMaterialType().getMaterialTypeName().equals(MaterialType.BASE_MATERIAL_TYPE_NAME))
.findFirst().get()
)
.findFirst().orElse(recipeMixes
.stream()
.filter(m -> !m.getMixQuantities().isEmpty())
.findFirst()
.get()
.getMixQuantities()
.stream()
.findFirst()
.get()).getMaterial();
@Override
public int hashCode() {
return Objects.hash(recipeID, recipeCode, company, recipeDescription, sample, approbationDate, remark, note, recipeMixes, recipeSteps);
return base;
}
}

View File

@ -1,85 +1,36 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Objects;
@Entity
@Table(name = "steps")
@Data
@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
@NoArgsConstructor
public class RecipeStep extends BeanModel implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private int stepID;
@NonNull
@ToString.Exclude
@ManyToOne(fetch = FetchType.LAZY)
@JsonIgnore
private Recipe recipe;
@NonNull
@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);
}
@Override
public int hashCode() {
return Objects.hash(stepID, recipe, stepMessage);
}
}

View File

@ -1,5 +1,6 @@
package dev.fyloz.trial.colorrecipesexplorer.core.model.dto;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MaterialType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import lombok.Data;
@ -12,6 +13,8 @@ public class MixCreationFormDto {
private String mixTypeName;
private MaterialType materialType;
private List<String> materials;
private List<Float> quantities;

View File

@ -46,7 +46,7 @@ public class MixService extends GenericService<Mix, MixDao> {
materials.add(found.get());
}
Optional<MixType> optionalMixType = mixTypeService.createByName(formDto.getMixTypeName());
Optional<MixType> optionalMixType = mixTypeService.createByName(formDto.getMixTypeName(), formDto.getMaterialType());
if (!optionalMixType.isPresent()) return modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
MixType mixType = optionalMixType.get();
@ -54,7 +54,7 @@ public class MixService extends GenericService<Mix, MixDao> {
return modelResponseBuilder.addResponseCode(ResponseCode.MIX_TYPE_ALREADY_USED, mixType.getTypeName());
// 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);
Mix mix = new Mix(recipe, mixType);
Optional<Mix> savedMix = save(mix);
if (savedMix.isPresent()) {
@ -75,6 +75,7 @@ public class MixService extends GenericService<Mix, MixDao> {
@Transactional
public ModelResponseBuilder edit(Mix mix, MixCreationFormDto formDto) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder();
Material material = mix.getMixType().getMaterial();
List<Material> materials = new ArrayList<>();
for (String materialCode : formDto.getMaterials()) {
@ -86,7 +87,9 @@ public class MixService extends GenericService<Mix, MixDao> {
materials.add(found.get());
}
mix.getMixType().getMaterial().setMaterialType(formDto.getMaterialType());
mix.getMixType().setTypeName(formDto.getMixTypeName());
material.setMaterialCode(formDto.getMixTypeName());
List<MixQuantity> mixQuantities = createMixQuantities(mix, materials, formDto.getQuantities());
@ -96,7 +99,7 @@ public class MixService extends GenericService<Mix, MixDao> {
}
mix.setMixQuantities(mixQuantities);
if (update(mix).isPresent()) return null;
if (materialService.update(material).isPresent() && update(mix).isPresent()) return null;
else return modelResponseBuilder.addResponseCode(ResponseCode.ERROR_SAVING);
}

View File

@ -29,16 +29,8 @@ public class MixTypeService extends GenericService<MixType, MixTypeDao> {
return dao.findByMaterial(material);
}
public Optional<MixType> createByName(String name) {
Optional<MaterialType> defaultType = materialTypeService.getDefaultMaterialType();
if (!defaultType.isPresent()) return Optional.empty();
Optional<MixType> mixTypeByName = getByName(name);
if (mixTypeByName.isPresent()) {
return mixTypeByName;
}
Material mixTypeMaterial = new Material(name, 0, true, defaultType.get());
public Optional<MixType> createByName(String name, MaterialType type) {
Material mixTypeMaterial = new Material(name, 0f, true, type);
MixType mixType = new MixType(name, mixTypeMaterial);
return save(mixType);

View File

@ -17,7 +17,7 @@ import javax.validation.Valid;
import java.util.Optional;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.CREATOR_COMPANY;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.CREATOR_COMPANY_SUCCESS;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.INDEX;
@Controller
public class CompanyCreatorController {
@ -56,7 +56,7 @@ public class CompanyCreatorController {
*/
@PostMapping(value = CREATOR_COMPANY, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView createCompany(@ModelAttribute @Valid Company company) {
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder(CREATOR_COMPANY_SUCCESS);
ModelResponseBuilder modelResponseBuilder = new ModelResponseBuilder().withRedirect(INDEX);
if (companyService.isValidForCreation(company)) {
Optional<Company> savedCompany = companyService.save(company);

View File

@ -7,10 +7,7 @@ import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Recipe;
import dev.fyloz.trial.colorrecipesexplorer.core.model.dto.MixCreationFormDto;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixTypeService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.*;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
@ -36,13 +33,15 @@ public class MixCreatorController {
private RecipeService recipeService;
private MaterialService materialService;
private MixTypeService mixTypeService;
private MaterialTypeService materialTypeService;
@Autowired
public MixCreatorController(MixService mixService, RecipeService recipeService, MaterialService materialService, MixTypeService mixTypeService) {
public MixCreatorController(MixService mixService, RecipeService recipeService, MaterialService materialService, MixTypeService mixTypeService, MaterialTypeService materialTypeService) {
this.mixService = mixService;
this.recipeService = recipeService;
this.materialService = materialService;
this.mixTypeService = mixTypeService;
this.materialTypeService = materialTypeService;
}
/**
@ -79,6 +78,7 @@ public class MixCreatorController {
ModelResponseBuilder responseBuilder = modelResponseBuilder
.addResponseData(ResponseDataType.RECIPE, recipe)
.addResponseData(ResponseDataType.MATERIAL_TYPES, materialTypeService.getAll())
.addAttribute("materialsJson", materialService.asJson(materials));
if (materialService.getAll().isEmpty()) {

View File

@ -7,10 +7,7 @@ import dev.fyloz.trial.colorrecipesexplorer.core.model.Material;
import dev.fyloz.trial.colorrecipesexplorer.core.model.Mix;
import dev.fyloz.trial.colorrecipesexplorer.core.model.MixType;
import dev.fyloz.trial.colorrecipesexplorer.core.model.dto.MixCreationFormDto;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MaterialService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.MixTypeService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.RecipeService;
import dev.fyloz.trial.colorrecipesexplorer.core.services.model.*;
import dev.fyloz.trial.colorrecipesexplorer.core.utils.ControllerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
@ -28,7 +25,7 @@ import java.util.Optional;
import java.util.stream.Collectors;
import static dev.fyloz.trial.colorrecipesexplorer.web.PagesPaths.*;
import static dev.fyloz.trial.colorrecipesexplorer.web.StringBank.MIX_ID;
import static dev.fyloz.trial.colorrecipesexplorer.web.StringBank.*;
@Controller
public class MixEditorController {
@ -37,13 +34,15 @@ public class MixEditorController {
private MaterialService materialService;
private RecipeService recipeService;
private MixTypeService mixTypeService;
private MaterialTypeService materialTypeService;
@Autowired
public MixEditorController(MixService mixService, MaterialService materialService, RecipeService recipeService, MixTypeService mixTypeService) {
public MixEditorController(MixService mixService, MaterialService materialService, RecipeService recipeService, MixTypeService mixTypeService, MaterialTypeService materialTypeService) {
this.mixService = mixService;
this.materialService = materialService;
this.recipeService = recipeService;
this.mixTypeService = mixTypeService;
this.materialTypeService = materialTypeService;
}
/**
@ -79,7 +78,9 @@ public class MixEditorController {
return modelResponseBuilder
.addResponseData(ResponseDataType.MIX, mix)
.addResponseData(ResponseDataType.RECIPE_CODE, mix.getRecipe().getRecipeCode())
.addResponseData(ResponseDataType.RECIPE, mix.getRecipe())
.addAttribute(MATERIAL_TYPE, mix.getMixType().getMaterial().getMaterialType())
.addAttribute(MATERIAL_TYPES, materialTypeService.getAll())
.addAttribute("mixJson", materialService.asJson(mix))
.addAttribute("materialsJson", materialService.asJson(materials))
.build();

View File

@ -71,11 +71,9 @@ public class CompanyRemoverController {
if (optionalCompany.isPresent()) {
Company company = optionalCompany.get();
if (companyService.deleteIfNotLinked(company)) {
modelResponseBuilder.addResponseData(ResponseDataType.COMPANY_NAME, company.getCompanyName());
} else {
modelResponseBuilder.addResponseCode(ResponseCode.COMPANY_LINKED, company.getCompanyName());
}
if (companyService.deleteIfNotLinked(company))
modelResponseBuilder.addResponseCode(ResponseCode.SUCCESS_DELETING_COMPANY, company.getCompanyName());
else modelResponseBuilder.addResponseCode(ResponseCode.COMPANY_LINKED, company.getCompanyName());
} else {
modelResponseBuilder.addResponseCode(ResponseCode.COMPANY_NOT_FOUND, companyID);
}

View File

@ -18,10 +18,14 @@ spring:
h2:
console:
enabled: true
path: /dbconsole
settings:
trace: true
web-allow-others: true
server:
port: 9090
error:
whitelabel:
enabled: false
response:
useport: true
useport: true

View File

@ -1,5 +1,5 @@
company.form.companyName=Banner name
menu.list=List
menu.explore=Explore
menu.add=Add
menu.edit=Edit
menu.delete=Delete
@ -100,3 +100,6 @@ recipe.warning.exportAll=Do you really want to export all the colors? This can t
warning.noResult=Nothing corresponding the the research was found
inventory.askUseMix=Do you really want to deduct this mix from the inventory?
inventory.askUseRecipe=Do you really want to deduct this recipe from the inventory?
keyword.images=Images
warning.askChangePage=Are you sure you want to continue? Unsaved changes will be lost.
keyword.print=Print

View File

@ -1,4 +1,4 @@
menu.list=Listes
menu.explore=Explorer
menu.add=Ajouter
menu.edit=Modifier
menu.delete=Supprimer
@ -100,4 +100,7 @@ recipe.warning.exportAll=Voulez-vous vraiment exporter toutes les couleurs? Cela
warning.noResult=Rien correspondant à la recherche n'a été trouvé
inventory.askUseMix=Êtes-vous certain de vouloir déduire ce mélange de l'inventaire?
inventory.askUseRecipe=Êtes-vous certain de vouloir déduire cette recette de l'inventaire?
keyword.images=Images
warning.askChangePage=Êtes-vous sûr de vouloir continuer? Les modifications non enregistrées seront perdues.
keyword.print=Imprimer

View File

@ -26,4 +26,5 @@ response.22=No companies were found
response.23=No materials were found.
response.24=This file need to be an image
response.25=The recipe was not found
response.26=The material with the code {0} was not found
response.26=The material with the code {0} was not found
response.27=The banner {0} has been delete

View File

@ -26,4 +26,5 @@ response.22=Aucune compagnie n'a été trouvée
response.23=Aucun produit n'a été trouvée.
response.24=Ce fichier doit être une image
response.25=La recette n'a pas été trouvée
response.26=Le produit ayant le code {0} n''a pas été trouvé
response.26=Le produit ayant le code {0} n''a pas été trouvé
response.27=La bannière {0} a bien été supprimée

View File

@ -0,0 +1,168 @@
.mix {
width: 600px;
}
p {
display: inline;
}
.mixContainer {
display: flex;
padding: 20px 0;
border-style: solid;
border-width: 1px 0;
border-color: #555555;
}
.mixContainer:first-child {
border-top-color: white;
}
.mixContainer:last-child {
border-bottom-color: white;
}
.mixContainer .mixInfo {
width: 130px;
text-align: left;
}
.mixContainer .mix {
max-width: 600px;
}
.mixContainer .mixActionsContainer {
margin-left: 15px;
display: flex;
flex-direction: column;
}
.mix td, .mix th {
padding: 0 5px;
}
.notEnough td {
background-color: #ffb3b3;
}
.quantityColumn, .mix input {
width: 50px;
}
.totalQuantityLabel {
padding-right: 10px;
text-align: right;
vertical-align: middle;
}
.inventoryQuantity {
display: none;
}
.inventoryQuantityColumn {
min-width: auto !important;
width: auto !important;
}
.unitsColumn {
width: 25px !important;
min-width: 0 !important;
max-width: 25px !important;
}
.calculationColumn {
max-width: 125px !important;
min-width: 100px;
}
.calculation {
color: dimgrey;
}
.calculation span {
color: darkgreen;
}
.materialCodeColumn {
min-width: 100px;
}
.recipeLocation {
width: 55px;
}
.recipeDescription {
display: flex;
flex-direction: column;
text-align: left;
}
.recipeDescription div {
border-color: #555555;
border-width: 1px 0;
border-style: solid;
padding: 10px 0;
}
.recipeDescription div:first-child {
border-top-color: white;
}
.recipeDescription div:nth-last-child(2) {
border-bottom-color: white;
}
.recipeDescription div b {
float: left;
}
.recipeDescription div :not(b) {
float: right;
margin: 0 0 0 20px;
max-width: 350px;
}
.steps {
text-align: left;
}
.steps li {
padding: 5px;
}
.steps li:nth-child(even) {
background-color: #f5f5f5;
margin: 0;
}
.steps li:nth-child(odd) {
background-color: #fafafa;
}
.imagesContainer {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
padding: 20px;
}
.imagesContainer img {
margin: 10px;
border: 1px solid #7a7a7a;
transition: all 0.5s;
}
.imagesContainer .recipeImage:hover {
width: 420px;
}
.imagesContainer.hovering .recipeImage:not(:hover) {
opacity: 0.5;
}
@media only screen and (max-width: 1900px) {
.imagesContainer {
width: 50%;
}
}

View File

@ -0,0 +1,14 @@
.content {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
margin-left: 50px;
margin-right: 50px;
}
.content .flexContent {
padding: 20px;
}

View File

@ -0,0 +1,57 @@
label {
font-weight: bold;
}
.content {
flex-direction: column;
}
.formWrap, .formEndButtons {
display: flex;
flex-direction: row;
justify-content: center;
}
.formEndButtons button[type="button"] {
margin-right: 75px;
}
.formEndButtons button[type="submit"] {
margin-left: 75px;
}
.formColumn {
display: flex;
flex-direction: column;
text-align: left;
}
.formColumn div {
min-height: 25px;
line-height: 25px;
}
.formColumn select, .formColumn input, .formColumn button {
float: right;
}
.formColumn input[type="text"], .formColumn input[type="number"], .formColumn input[type="date"], .formColumn select {
width: 145px;
}
.formColumn:first-child {
margin-right: 20px;
}
.formColumn:last-child {
margin-left: 20px;
}
.rawInput:not(textarea):not(.noStyle) {
border-bottom-color: #7a7a7a;
color: #454545;
}
.rawInput:hover:not(textarea):not(.noStyle) {
border-bottom-color: #7a7a7a;
}

View File

@ -1,8 +0,0 @@
.form {
display: inline-block;
margin-top: 10px;
}
td:not(.centerTd) {
text-align: right;
}

View File

@ -1,61 +1,18 @@
body {
margin: 0;
font-family: 'Open Sans', sans-serif;
}
td {
vertical-align: top;
}
th {
background-color: black;
color: white;
font-weight: normal;
font-size: large;
overflow-x: hidden;
}
h1 {
text-decoration: underline;
}
header, footer {
background-color: black;
height: 70px;
text-align: center;
width: 100%;
color: white;
}
footer {
position: fixed;
height: 5%;
bottom: 0;
padding-top: 10px;
}
header img {
float: right;
}
section {
text-align: center;
margin-bottom: 50px;
}
nav {
overflow: hidden;
background-color: black;
}
nav a {
float: left;
font-size: 18px;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
footer a {
color: white;
text-decoration: none;
@ -109,85 +66,44 @@ input[type=number] {
-moz-appearance: textfield;
}
table {
border-collapse: collapse;
}
textarea {
font-family: 'Open Sans', sans-serif;
background-color: #fafafa;
border-style: solid;
border-color: #7a7a7a;
border-width: 1px;
font-family: inherit;
border: 1px solid #7a7a7a;
margin-left: 20px;
padding: 10px;
}
.dropdown {
margin-top: 22px;
float: left;
overflow: hidden;
/*.error {*/
/* color: red;*/
/*}*/
/*.success {*/
/* color: green;*/
/*}*/
/*.error, .success {*/
/* font-weight: bold;*/
/*}*/
table {
border-collapse: collapse;
}
.dropdown .dropbtn {
font-size: 16px;
border: none;
color: white;
outline: none;
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;
table:not(.noStyle) {
background-color: #f5f5f5;
border: 1px solid #7a7a7a;
text-align: left;
}
.dropdown-content a:hover {
background-color: #1a1a1a;
table:not(.noStyle) th {
background-color: black;
color: white;
font-weight: normal;
font-size: large;
}
.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;
table:not(.noStyle) tr:nth-child(odd) {
background-color: #fafafa;
}
.nosimdut td {
@ -199,7 +115,7 @@ nav a:hover, .dropdown:hover .dropbtn {
}
.unapproved {
background-color: #fff0b3;
background-color: #fff0b3 !important;
}
#researchBoxContainer {
@ -254,28 +170,38 @@ nav a:hover, .dropdown:hover .dropbtn {
.messageBox p {
display: inline-block;
/*padding: 3px 0;*/
margin: 0 0 0 16px;
line-height: 24px;
}
/*.errorBox div {*/
/* height: 24px;*/
/* !*padding: 20px;*!*/
/* margin: auto;*/
/* vertical-align: middle;*/
/*}*/
.subtitle {
padding: 0 !important;
text-align: center;
}
/*.errorBox img {*/
/* display: inline-block;*/
/* float: left;*/
/* height: 25px;*/
/*}*/
.rawInput:not(.noStyle) {
border-color: white;
color: #7a7a7a;
text-align: right;
}
/*.errorBox p {*/
/* !*float: left;*!*/
/* display: inline-block;*/
/* font-size: 18px;*/
/* margin: auto;*/
/* padding: 2px 0;*/
/*}*/
.rawInput:hover:not(.noStyle) {
border-color: #e6e6e6;
}
.rawInput:focus:not(.noStyle) {
border-color: #7a7a7a;
color: black;
}
.formEndButtons {
margin-top: 10px;
}
.returnButton {
float: left;
}
.submitButton {
float: right;
}

View File

@ -0,0 +1,87 @@
header, footer {
background-color: black;
height: 70px;
text-align: center;
width: 100%;
color: white;
}
footer {
position: fixed;
height: 5%;
bottom: 0;
padding-top: 10px;
}
nav img {
margin-left: auto;
}
nav {
display: flex;
flex-wrap: wrap;
overflow: visible;
background-color: inherit;
}
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;
background-color: inherit;
}
.dropdown .dropbtn {
font-size: 16px;
border: none;
color: white;
outline: none;
padding: 14px 16px;
background-color: 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;
}
@media only screen and (max-width: 702px) {
nav img {
display: none;
}
}

View File

@ -4,6 +4,10 @@
width: 150px;
}
.materialSelector {
position: relative;
}
.materialSelector .materialList {
border-color: #7a7a7a;
border-width: 1px;
@ -12,9 +16,10 @@
max-height: 300px;
overflow: auto;
overflow-x: hidden;
position: fixed;
width: 150px;
z-index: 99;
position: absolute;
top: 100%;
width: 200px;
z-index: 10;
}
.materialSelector .materialList.show {
@ -36,6 +41,70 @@
background-color: #e6e6e6;
}
.unitsColumn {
vertical-align: middle;
.materialListWrap {
display: flex;
flex-direction: column;
}
.mixMaterialListTitle {
background-color: black;
color: white;
}
.mixMaterialListTitle div {
display: block !important;
margin: auto;
}
.materialListWrap div {
display: flex;
flex-direction: row;
}
.mixMaterialList {
flex-direction: column !important;
background-color: #f5f5f5;
border-style: solid;
border-width: 1px;
border-color: #7a7a7a;
}
.materialListRow:nth-child(odd) {
background-color: #fafafa;
}
.material, .material *, .quantity, .quantity * {
width: 200px;
}
.material input {
text-align: left;
padding: 0 10px;
}
.quantity input {
text-align: right;
padding: 0 10px;
}
.units, .units * {
width: 40px;
}
.removeButton, .removeButton * {
width: 100px;
}
.positionButtons {
display: flex !important;
flex-direction: column !important;
width: 50px;
padding: 1px 3px 1px 1px;
/*border-bottom: solid 1px #7a7a7a;*/
}
.positionButtons button {
margin: 1px;
width: inherit;
height: 50%;
}

View File

@ -0,0 +1,43 @@
h1 {
margin-bottom: 0;
}
.content {
flex-direction: column;
}
.content .recipesList {
border-collapse: collapse;
margin: 0 auto;
text-align: center;
}
h1, h2 {
margin: 0 0 10px;
}
h1 {
margin-top: 10px;
}
.recipesList th {
padding: 0 15px;
}
.recipesList td {
padding: 0 20px;
}
.recipeDescription {
max-width: 400px;
}
.descriptionCell {
border-right-color: #e6e6e6;
border-right-style: solid;
border-right-width: 2px;
}
.researchEnabled .companyTab:not(.researchResult), .researchEnabled .recipeRow:not(.researchResult) {
display: none;
}

View File

@ -6,30 +6,24 @@ const warningMsgBoxText = warningMsgBox.querySelector("p");
const successMsgBox = document.querySelector(".successBox");
const successMsgBoxText = successMsgBox.querySelector("p");
// Ne fonctionne pas dans window#load
(() => {
// Ajoute Axios
const axiosElement = document.createElement("script");
axiosElement.src = "/js/libs/axios.min.js";
body.appendChild(axiosElement);
$(() => {
$('.materialCode').each(function () {
const row = $(this);
const materialID = row.data("materialid");
axiosElement.onload = () => {
// Vérifie si les SIMDUTs sont présents
document.querySelectorAll(".materialCode").forEach(e => {
const materialID = e.getAttribute("data-materialID");
axios.post(`/simdut/${materialID}`)
.catch(err => {
if (err.response.status === 404) {
e.parentElement.classList.add("nosimdut");
e.parentElement.title = simdutNotFoundText;
}
});
});
};
axios.post(`/simdut/${materialID}`)
.catch(function (err) {
if (err.response.status === 404) {
row.parent().addClass("nosimdut");
row.parent().title = simdutNotFoundText;
}
});
});
// Placé ici pour un chargement plus rapide
document.querySelectorAll(".messageBox").forEach(e => checkMessageBoxesDisplay(e));
})();
$(".messageBox").each(function () {
checkMessageBoxesDisplay($(this)[0])
});
});
window.addEventListener("load", () => {
@ -64,9 +58,7 @@ window.addEventListener("load", () => {
e.querySelectorAll(".remover").forEach(elem => {
elem.addEventListener("click", () => {
e.action += elem.getAttribute("data-entityID");
e.onsubmit = () => {
return checkPassword(e);
}
checkPassword(e);
});
});
});
@ -84,6 +76,10 @@ window.addEventListener("load", () => {
});
});
document.querySelectorAll('input[type="text"], input[type="number"], input[type="date"], textarea').forEach(e => e.classList.add("rawInput"))
document.querySelectorAll(".rawInput").forEach(e => e.placeholder = "N/A");
window.addEventListener("keyup", e => {
if (e.target) {
if (e.target.classList.contains("toSave")) {
@ -108,32 +104,33 @@ function checkPassword(form, callback) {
hideElement(errorMsgBox);
const password = prompt(askPasswordText);
if (!password) return false;
let data = {};
data.password = password;
axios.post("/password/valid", data)
.then(r => {
if (r.data) {
if (r.data === true) {
if (form != null) form.submit();
if (callback != null) callback();
return true;
} else {
errorMsgBoxText.innerText = invalidPasswordText;
showElement(errorMsgBox);
return false;
}
})
.catch(e => {
errorMsgBoxText.innerText = generalErrorText;
showElement(errorMsgBox);
console.log(e);
return false;
});
return false;
}
function checkMessageBoxesDisplay(element) {
if (!element.querySelector("p").innerText) hideElement(element);
if (!element.querySelector("p").textContent) hideElement(element);
else showElement(element);
}
@ -142,7 +139,7 @@ function hideElement(element) {
}
function showElement(element) {
element.style.display = "inline-block";
element.style.display = "";
}
const lTomL = 1000;
@ -151,17 +148,19 @@ const galTomL = 3785.41;
let currentUnit = "mL";
// Change les unités selon la sélection de l'utilisateur
function changeUnits(unitSelect, quantitiesSelector, unitsSelector) {
function changeUnits(unitSelect, quantitiesSelector, unitsDisplay) {
currentUnit = unitSelect.value;
document.querySelectorAll(unitsSelector).forEach(e => {
document.querySelectorAll(unitsDisplay).forEach(e => {
e.innerText = currentUnit;
// Modifie la quantitée
const quantityElem = e.parentElement.parentElement.querySelector(quantitiesSelector);
const originalQuantity = parseInt(quantityElem.dataset.quantityml);
if (quantityElem) {
const originalQuantity = parseInt(quantityElem.dataset.quantityml);
quantityElem.innerText = convertMlToUnit(originalQuantity);
quantityElem.innerText = convertMlToUnit(originalQuantity);
}
});
}

View File

@ -7,7 +7,7 @@ let removeText;
let materialSelectorHtml;
let recipeID;
window.addEventListener("load", () => {
$(() => {
recipeID = document.querySelector("#recipeID").value;
axios.get(`/mix/selector/${recipeID}/-1`)
@ -16,65 +16,115 @@ window.addEventListener("load", () => {
init();
})
.catch(e => {
console.log(e.status);
errorMsgBoxText.innerText = generalErrorText;
showElement(errorMsgBox);
console.log(e);
});
});
window.addEventListener("click", e => {
if (e.target) {
if (e.target.classList.contains("rowRemover")) {
document.querySelector(`#row_${e.target.dataset.remove}`).remove();
return;
}
const materialSelector = e.target.parentElement;
if (materialSelector == null || materialSelector.parentElement == null) {
hideMaterialList();
return;
}
if (!materialSelector.classList.contains("materialSelector") && !materialSelector.parentElement.classList.contains("materialSelector")) hideMaterialList();
}
$(document).on("click", function () {
hideMaterialList();
});
document.querySelector("#materials button").addEventListener("click", () => {
$(document).on("click", ".materialSelector", function (event) {
event.stopPropagation();
showMaterialList($(this)[0]);
});
$(document).on("click", ".removeMaterial", function () {
const id = $(this).data("removerow");
if ($(".materialListRow").length > 1) $(`#${id}`).remove();
});
$(document).on("click", ".upRow", function () {
const row = $(this).parent().parent();
row.insertBefore(row.prev());
});
$(document).on("click", ".downRow", function () {
const row = $(this).parent().parent();
row.insertBefore(row.next());
});
document.querySelector(".mixMaterialListTitle button").addEventListener("click", () => {
addMaterial(null, null);
});
function addMaterial(materialCode, quantity) {
let row = addRow();
const row = addRow(materialNbr);
const positionColumn = addColumn("positionButtons", row);
const materialColumn = addColumn("material", row);
const quantityColumn = addColumn("quantity", row);
const unitsColumn = addColumn("units", row);
const removeButtonColumn = addColumn("removeButton", row);
let materialSelectionColumn = addColumn(row, null);
let quantityInputColumn = addColumn(row, null);
let unitsColumn = addColumn(row, "unitsColumn");
let removeButtonColumn = addColumn(row, null);
addPositionButtons(positionColumn);
addQuantityInput(quantity, quantityColumn);
addUnits(unitsColumn);
addRemoveButton(materialNbr, removeButtonColumn);
addInput(quantityInputColumn, quantity);
addSpan(unitsColumn);
addButton(removeButtonColumn);
materialSelectionColumn.innerHTML = materialSelectorHtml;
const input = materialSelectionColumn.querySelector("input");
materialColumn.innerHTML = materialSelectorHtml;
const materialSelector = materialColumn.querySelector("input");
if (materialCode) {
const material = materialSelectionColumn.querySelector(`.materialList p[data-materialcode="${materialCode}"]`);
const material = materialColumn.querySelector(`.materialList p[data-materialcode="${materialCode}"]`);
if (material) {
input.value = material.dataset.materialcode;
input.dataset.usepercentages = material.dataset.usepercentages;
materialSelector.value = material.dataset.materialcode;
materialSelector.dataset.usepercentages = material.dataset.usepercentages;
}
}
document.querySelector("#materials tbody").appendChild(row);
materialNbr++;
row.appendChild(materialColumn);
row.appendChild(quantityColumn);
row.appendChild(unitsColumn);
row.appendChild(removeButtonColumn);
checkUnits(input, row);
document.querySelector(".mixMaterialList").appendChild(row);
checkUnits(materialSelector, row);
materialNbr++;
}
function addInput(parent, quantity) {
function addRow(index) {
const row = document.createElement("div");
row.classList.add("materialListRow");
row.id = `material_${index}`;
return row;
}
function addColumn(type, parent) {
const column = document.createElement("div");
column.classList.add(type);
parent.appendChild(column);
return column;
}
function addUnits(parent) {
const units = document.createElement("p");
units.classList.add("quantityUnits");
units.textContent = "mL";
parent.appendChild(units);
return units;
}
function addRemoveButton(index, parent) {
const button = document.createElement("button");
button.type = "button";
button.textContent = removeText;
button.dataset.removerow = `material_${index}`;
button.classList.add("removeMaterial");
parent.appendChild(button);
return button;
}
function addQuantityInput(quantity, parent) {
let input = document.createElement("input");
if (quantity === null) quantity = 1;
if (quantity === undefined || quantity === null) quantity = 1;
input.type = "number";
input.name = "quantities";
@ -87,41 +137,18 @@ function addInput(parent, quantity) {
return input;
}
function addSpan(parent) {
let span = document.createElement("span");
function addPositionButtons(parent) {
const up = document.createElement("button");
up.classList.add("upRow");
up.type = "button";
up.textContent = "↑";
parent.appendChild(up);
span.className = "quantityUnit";
span.innerText = "mL";
parent.appendChild(span);
return span;
}
function addButton(parent) {
let button = document.createElement("button");
button.type = "button";
button.dataset.remove = materialNbr;
button.className = "rowRemover";
button.innerText = removeText;
parent.appendChild(button);
return button;
}
function addRow() {
let row = document.createElement("tr");
row.id = `row_${materialNbr}`;
return row;
}
function addColumn(parent, className) {
let column = document.createElement("td");
if (className != null) column.className = className;
parent.appendChild(column);
return column;
const down = document.createElement("button");
down.classList.add("downRow");
down.type = "button";
down.textContent = "↓";
parent.appendChild(down);
}
function showMaterialList(input) {
@ -152,7 +179,7 @@ function searchMaterial(input) {
materials = input.parentElement.querySelectorAll(".materialList p");
materials.forEach(e => {
if (searchIn(filter, e.textContent) || searchIn(filter, e.dataset.materialType)) e.style.display = "";
if (searchIn(filter, e.textContent) || searchIn(filter, e.dataset.materialtype)) e.style.display = "";
else e.style.display = "none";
});
@ -163,7 +190,7 @@ function searchMaterial(input) {
}
function checkUnits(materialSelector, row) {
const quantityUnits = row.querySelector(".quantityUnit");
const quantityUnits = row.querySelector(".quantityUnits");
if (materialSelector.dataset.usepercentages === "true") quantityUnits.innerText = "%";
else quantityUnits.innerText = "mL";
}

View File

@ -0,0 +1,72 @@
(() => {
document.querySelectorAll(".recipeRow").forEach(e => {
if (e.dataset.approbationdate === undefined) {
e.classList.add("unapproved");
e.title = recipeNotApproved;
}
});
})();
function closeTabs() {
document.querySelectorAll(".recipesList").forEach(l => l.style.display = "none");
}
function openTabs() {
document.querySelectorAll(".recipesList").forEach(l => l.style.display = "");
}
const companyRows = document.querySelectorAll(".companyTab");
function performSearch(searchString) {
let found = false;
let emptySearch = false;
const recipesContainer = document.querySelector(".recipesContainer:not(.researchEnabled)");
if (recipesContainer !== undefined && recipesContainer !== null) {
recipesContainer.classList.add("researchEnabled");
}
document.querySelectorAll(".researchResult").forEach(t => {
t.classList.remove("researchResult");
});
companyRows.forEach(c => {
const recipeRows = c.querySelectorAll(".recipeRow");
hideElement(warningMsgBox);
if (!searchString || !searchString.replace(/\s/g, '').length) {
c.classList.add("researchResult");
recipeRows.forEach(r => {
r.classList.add("researchResult");
});
found = true;
emptySearch = true;
} else if (searchIn(searchString, c.querySelector("h2").dataset.companyname)) {
c.classList.add("researchResult");
recipeRows.forEach(r => r.classList.add("researchResult"));
found = true;
} else {
recipeRows.forEach(r => {
r.querySelectorAll(".descriptionCell").forEach(d => {
if (searchIn(searchString, d.textContent)) {
r.classList.add("researchResult");
c.classList.add("researchResult");
found = true;
}
});
});
}
if (!found) {
warningMsgBoxText.innerText = researchNotFound;
showElement(warningMsgBox);
}
if (emptySearch) closeTabs();
else openTabs();
}
);
}

View File

@ -1,23 +0,0 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{company.add.title})"></th:block>
<link href="/css/main.css" rel="stylesheet"/>
</head>
<body>
<!-- Fragment de l'entête -->
<header th:include="fragments.html :: header"></header>
<!-- Corps de la page -->
<section>
<h1 th:text="#{company.add.title}"></h1>
<p th:text="#{company.success.created(${companyName})}"></p>
<button class="returnButton" th:text="#{keyword.back}"></button>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
</body>
</html>

View File

@ -1,10 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{company.add.title})"></th:block>
<link href="/css/main.css" rel="stylesheet"/>
<link href="/css/forms.css" rel="stylesheet"/>
<th:block th:include="fragments.html :: head(#{company.add.title}, 'form')"></th:block>
</head>
<body>
@ -15,22 +12,29 @@
<div th:include="fragments.html :: messages"></div>
<h1 th:text="#{company.add.title}"></h1>
<div class="form">
<form th:action="@{/company/creator}" th:object="${company}" class="requireAuth" method="POST">
<table>
<tr>
<td><b><label th:for="${#ids.next('companyName')}"
th:text="#{company.form.companyName} + ':'"></label></b></td>
<td><input type="text" th:field="*{companyName}" required /></td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>
<form th:action="@{/company/creator}"
th:object="${company}"
class="requireAuth"
method="POST">
<div class="content">
<div class="formWrap">
<div class="formColumn">
<label th:for="${#ids.next('companyName')}"
th:text="#{company.form.companyName} + ':'"></label>
</div>
<div class="formColumn">
<input class="rawInput"
required
th:field="*{companyName}"
type="text">
</div>
</div>
<div th:include="fragments.html :: formEndButtons"></div>
</div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
</body>
</html>

View File

@ -1,9 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{company.delete.title})"></th:block>
<!-- <link href="/css/forms.css" rel="stylesheet"/>-->
<th:block th:include="fragments.html :: head(#{company.delete.title}, null)"></th:block>
<style>
table {
@ -58,6 +56,5 @@
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer('/company/remover', true)"></footer>
</body>
</html>

View File

@ -1,29 +1,22 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{app.title})"></th:block>
<th:block th:include="fragments.html :: head(#{app.title}, null)"></th:block>
</head>
<body>
<!-- Fragment de l'entête -->
<header th:include="fragments.html :: header"></header>
<section>
<h2 class="error" style="margin-top: 100px" th:text="#{${error}}"></h2>
<button id="backButton" type="button">Retour</button>
<div th:include="fragments.html :: messages"></div>
<br/>
<div class="content">
<button class="returnButton" type="button" th:text="#{keyword.back}"></button>
</div>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, false)"></footer>
<script>
/*<![CDATA[*/
(() => {
document.querySelector("#backButton").addEventListener("click", () => {
document.location.href = '[[${referer}]]';
});
})();
/*]]>*/
</script>
</body>
</html>

View File

@ -1,17 +1,20 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:fragment="head(title)">
<head th:fragment="head(title, styleName)">
<title th:text="${title}"></title>
<meta charset="UTF-8"/>
<link rel="stylesheet" th:href="@{|${baseUrl}/css/main.css|}"/>
<link rel="stylesheet" th:href="@{|${baseUrl}/css/menu.css|}"/>
<link rel="stylesheet" th:href="@{|${baseUrl}/css/flex.css|}"/>
<link rel="stylesheet" th:if="${styleName != null}" th:href="@{|${baseUrl}/css/${styleName}.css|}">
</head>
<body>
<div th:fragment="header">
<nav>
<img alt="logo" id="logo" th:src="@{|${baseUrl}/logo.png|}" onclick="location.href = '/'"/>
<div class="dropdown">
<button class="dropbtn" onclick="document.location.href='/'" th:text="#{menu.list}">
<button class="dropbtn" onclick="document.location.href='/'" th:text="#{menu.explore}">
</button>
</div>
<div class="dropdown">
@ -52,6 +55,7 @@
<a th:href="@{|${baseUrl}/updates|}" th:text="#{keyword.updates}"></a>
</div>
</div>
<img alt="logo" id="logo" th:src="@{|${baseUrl}/logo.png|}" onclick="location.href = '/'"/>
</nav>
</div>
@ -75,7 +79,8 @@
<div class="messageBox successBox">
<div class="messageBoxContainer">
<img th:src="@{|${baseUrl}/icons/success.svg|}" alt="success icon"/>
<p th:if="${success != null && !success.isEmpty()}" th:text="#{${success}}"></p>
<p th:if="${success != null && !success.isEmpty()}"
th:text="#{${success}(${responseArg1}, ${responseArg2})}"></p>
<p th:if="${success == null || success.isEmpty()}"></p>
</div>
</div>
@ -85,7 +90,8 @@
<div class="messageBox warningBox">
<div class="messageBoxContainer">
<img th:src="@{|${baseUrl}/icons/warning.svg|}" alt="warning icon"/>
<p th:if="${warning != null && !warning.isEmpty()}" th:text="#{${warning}}"></p>
<p th:if="${warning != null && !warning.isEmpty()}"
th:text="#{${warning}(${responseArg1}, ${responseArg2})}"></p>
<p th:if="${warning == null || warning.isEmpty()}"></p>
</div>
</div>
@ -105,7 +111,9 @@
th:text="#{footer.lang}"></a>
</th:block>
<script src="/js/main.js"></script>
<script th:src="@{|${baseUrl}/js/libs/jquery-3.4.1.min.js|}"></script>
<script th:src="@{|${baseUrl}/js/libs/axios.min.js|}"></script>
<script th:src="@{|${baseUrl}/js/main.js|}"></script>
<script>
/*<![CDATA[*/
const simdutNotFoundText = "[[#{material.simdutFile.notFound}]]".replace("&#39;", "'");
@ -115,6 +123,8 @@
const invalidPasswordText = "[[#{password.notValid}]]".replace("&#39;", "'");
const generalErrorText = "[[#{error.serverError}]]".replace("&#39;", "'");
const researchNotFound = "[[#{warning.noResult}]]".replace("&#39;", "'");
const recipeNotApproved = "[[#{recipe.warning.notApproved}]]".replace("&#39;", "'");
const askChangePage = "[[#{warning.askChangePage}]]".replace("&#39;", "'");
const referer = "[[${referer}]]";
/*]]>*/
@ -130,5 +140,12 @@
</td>
</div>
<div th:fragment="formEndButtons">
<div class="formEndButtons">
<button class="returnButton" type="button" th:text="#{keyword.back}"></button>
<button class="submitButton" type="submit" th:text="#{keyword.save}"></button>
</div>
</div>
</body>
</html>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{image.add.title})"></th:block>
<th:block th:include="fragments.html :: head(#{image.add.title}, null)"></th:block>
</head>
<body>

View File

@ -1,46 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{app.title})"></th:block>
<style>
h1 {
margin-bottom: 0;
}
th {
padding: 0 15px;
}
td {
padding: 0 20px;
}
.descriptionCell {
border-right-color: #e6e6e6;
border-right-style: solid;
border-right-width: 2px;
}
table {
border-collapse: separate;
border-spacing: 0 10px;
margin: 0 auto;
text-align: center;
}
section {
text-align: center;
}
.recipeDescription {
max-width: 400px;
}
.researchEnabled .companyTab:not(.researchResult), .researchEnabled .recipeRow:not(.researchResult) {
display: none;
}
</style>
<th:block th:include="fragments.html :: head(#{app.title}, 'recipesList')"></th:block>
</head>
<body>
<!-- Fragment de l'entête -->
@ -48,45 +9,49 @@
<!-- Corps de la page -->
<section>
<div id="researchBoxContainer">
<input id="researchBox" type="text" th:placeholder="#{keyword.search}" onkeyup="performSearch(this.value)"/>
<input id="researchBox" class="noStyle" type="text" th:placeholder="#{keyword.search}"
onkeyup="performSearch(this.value)"/>
</div>
<div th:include="fragments.html :: messages"></div>
<div class="recipesContainer">
<h1 th:text="#{menu.explore}"></h1>
<div class="content recipesContainer">
<div class="companyTab" th:if="${!recipeMap.empty}" th:each="company : ${recipeMap.keySet()}"
th:data-companyID="${company.companyID}">
<h1 class="companyTabTitle" th:data-companyName="${company.companyName}"
th:text="${company.companyName}"></h1>
<h2 class="companyTabTitle" th:data-companyName="${company.companyName}"
th:text="${company.companyName}"></h2>
<table class="recipesList" th:if="${!recipeMap.get(company).empty}" style="display:none"
th:id="'recipes_' + ${company.companyName}">
<table style="display:none" th:id="'recipes_' + ${company.companyName}" class="recipesList"
th:if="${!recipeMap.get(company).empty}">
<tr>
<th th:text="#{recipe.color}"></th>
<th th:text="#{recipe.description}"></th>
<th th:text="#{recipe.sample}"></th>
<th></th>
</tr>
<tr th:each="recipe : ${recipeMap.get(company)}" class="recipeRow"
<tr class="recipeRow" th:each="recipe : ${recipeMap.get(company)}"
th:data-approbationDate="${recipe.approbationDate}" th:data-recipeID="${recipe.recipeID}">
<td class="descriptionCell" th:text="${recipe.recipeCode}"></td>
<td class="descriptionCell recipeDescription" th:text="${recipe.recipeDescription}"></td>
<td class="descriptionCell" th:text="'#' + ${recipe.sample}"></td>
<td>
<button class="gotoRecipe" th:data-recipeID="${recipe.recipeID}" type="button"
<button class="gotoRecipe" th:data-recipeid="${recipe.recipeID}" type="button"
th:text="#{keyword.see}"></button>
</td>
</tr>
</table>
</div>
<b class="error" th:if="${recipeMap.empty}" th:text="#{company.error.anyFound}"></b>
<b th:if="${recipeMap.empty}" class="error" th:text="#{recipe.error.anyFound}"></b>
</div>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
<script th:inline="javascript">
/*<![CDATA[*/
<script th:src="@{|${baseUrl}/js/recipeResearch.js|}"></script>
<script>
(() => {
document.querySelectorAll(".gotoRecipe").forEach(e => {
e.addEventListener("click", () => {
@ -95,82 +60,7 @@
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 = [[#{recipe.warning.notApproved}]];
}
});
})();
const companyRows = document.querySelectorAll(".companyTab");
function performSearch(searchString) {
let found = false;
let emptySearch = false;
const recipesContainer = document.querySelector(".recipesContainer:not(.researchEnabled)");
if (recipesContainer !== undefined && recipesContainer !== null) {
recipesContainer.classList.add("researchEnabled");
}
document.querySelectorAll(".researchResult").forEach(t => {
t.classList.remove("researchResult");
});
companyRows.forEach(c => {
const recipeRows = c.querySelectorAll(".recipeRow");
hideElement(warningMsgBox);
if (!searchString || !searchString.replace(/\s/g, '').length) {
c.classList.add("researchResult");
recipeRows.forEach(r => {
r.classList.add("researchResult");
});
found = true;
emptySearch = true;
} else if (searchIn(searchString, c.querySelector("h1").dataset.companyname)) {
c.classList.add("researchResult");
recipeRows.forEach(r => r.classList.add("researchResult"));
found = true;
} else {
recipeRows.forEach(r => {
r.querySelectorAll(".descriptionCell").forEach(d => {
if (searchIn(searchString, d.textContent)) {
r.classList.add("researchResult");
c.classList.add("researchResult");
found = true;
}
});
});
}
if (!found) {
warningMsgBoxText.innerText = researchNotFound;
showElement(warningMsgBox);
}
if (emptySearch) closeTabs();
else openTabs();
}
);
}
function closeTabs() {
document.querySelectorAll(".recipesList").forEach(l => l.style.display = "none");
}
function openTabs() {
document.querySelectorAll(".recipesList").forEach(l => l.style.display = "");
}
/*]]>*/
</script>
</body>
</html>

View File

@ -2,7 +2,7 @@
<html xmlns:th="http://www.thymeleaf.org">
<head>
<!-- Fragment du head -->
<th:block th:include="fragments.html :: head(#{menu.inventory})"></th:block>
<th:block th:include="fragments.html :: head(#{menu.inventory}, null)"></th:block>
<!-- Style de la page -->
<style>
@ -43,6 +43,15 @@
.researchEnabled .materialRow:not(.researchResult) {
display: none;
}
.options {
padding-top: 80px !important;
text-align: left;
}
.options div {
padding: 20px 0;
}
</style>
</head>
<body>
@ -59,36 +68,58 @@
<h1 th:text="#{menu.inventory}"></h1>
<div id="researchBoxContainer">
<input type="text" id="researchBox" th:placeholder="#{keyword.search}" onkeyup="performSearch(this.value)"/>
<input type="text" id="researchBox" class="noStyle" th:placeholder="#{keyword.search}"
onkeyup="performSearch(this.value)"/>
</div>
<div th:include="fragments.html :: messages"></div>
<table class="materialsContainer" th:if="${!materials.empty}">
<div class="content">
<div class="flexContent">
<table class="materialsContainer" th:if="${!materials.empty}">
<!--Titres des colonnes et options de recherche-->
<tr>
<th th:text="#{keyword.material}"></th>
<th th:text="#{keyword.quantity}"></th>
<!--Titres des colonnes et options de recherche-->
<tr>
<th th:text="#{keyword.material}"></th>
<th th:text="#{keyword.quantity}"></th>
<!--Unités de la quantité-->
<th th:text="#{keyword.units}"></th>
<!--Unités de la quantité-->
<th th:text="#{keyword.units}"></th>
<th th:text="#{keyword.type}"></th>
<th></th>
<th th:text="#{keyword.type}"></th>
<th></th>
</tr>
<!--Options-->
<td rowspan="5" style="padding-left : 20px">
<!--Produits-->
<tr class="materialRow" th:each="material : ${materials}"
th:data-materialType="${material.materialType.materialTypeID}"
th:data-materialID="${material.materialID}">
<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"
th:text="#{keyword.edit}"></button>
</td>
</tr>
</table>
<b th:if="${materials.empty}" class="error" th:text="#{material.error.anyFound}"></b>
</div>
<div class="flexContent options" style="display: flex; flex-direction: column">
<div>
<label for="lowQuantity" th:text="#{inventory.lowQuantity} + ':'"></label>
<input id="lowQuantity" min="0" onchange="checkLowQuantity(this.value)" step="0.01"
style="width: 65px" type="number"
value="100"/>
<input id="lowQuantity" min="0" onkeyup="checkLowQuantity(this.value)" step="0.01"
style="width: 65px" type="number"/>
<br/>
<input id="hideOthers" onchange="hide(this.checked)" type="checkbox"/><label for="hideOthers"
th:text="#{inventory.hideOthers}"></label>
<br/>
<br/>
</div>
<div>
<label for="materialTypeSelect" th:text="#{inventory.showOnly} + ':'"> </label>
<select id="materialTypeSelect" name="materialTypeSelect" onchange="hideMaterialTypes(this)">
<th:block th:each="materialType : ${materialTypes}">
@ -96,9 +127,9 @@
th:value="${materialType.materialTypeID}"></option>
</th:block>
</select>
<br/>
<br/>
</div>
<div>
<label for="units" th:text="#{keyword.units} + ':'"></label>
<select id="units" name="units"
onchange="changeUnits(this, '.inventoryQuantity', '.inventoryQuantityUnits')">
@ -106,28 +137,11 @@
<option value="L" th:text="#{units.liters}"></option>
<option value="gal" th:text="#{units.gallons}"></option>
</select>
</td>
</div>
</div>
</div>
</tr>
<!--Produits-->
<tr class="materialRow" th:each="material : ${materials}"
th:data-materialType="${material.materialType.materialTypeID}"
th:data-materialID="${material.materialID}">
<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"
th:text="#{keyword.edit}"></button>
</td>
</tr>
</table>
<b th:if="${materials.empty}" class="error" th:text="#{material.error.anyFound}"></b>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
@ -135,6 +149,8 @@
<script>
/*<![CDATA[*/
const defaultLowQuantity = 100;
(() => {
// Rajoute un événement sur les boutons pour modifier les produits pour rediriger l'utilisateur vers la page de modification
@ -144,6 +160,9 @@
});
});
document.querySelector("#lowQuantity").value = defaultLowQuantity;
checkLowQuantity(defaultLowQuantity)
})();
// Ajoute la classe "lowQuantity" au produits qui ont une quantité en inventaire inférieur à la quantité limite définie

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{material.add.title})"></th:block>
<th:block th:include="fragments.html :: head(#{material.add.title}, null)"></th:block>
<link href="/css/main.css" rel="stylesheet"/>
</head>

View File

@ -1,10 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{material.add.title})"></th:block>
<link href="/css/main.css" rel="stylesheet"/>
<link href="/css/forms.css" rel="stylesheet"/>
<th:block th:include="fragments.html :: head(#{material.add.title}, 'form')"></th:block>
</head>
<body>
@ -16,62 +13,78 @@
<div th:include="fragments.html :: messages"></div>
<h1 th:text="#{material.add.title}"></h1>
<div class="form">
<form th:action="@{/material/creator}" th:object="${material}" class="requireAuth" enctype="multipart/form-data"
method="POST">
<table>
<tr>
<td><b><label th:for="${#ids.next('materialCode')}" th:text="#{material.code} + ':'"></label></b>
</td>
<td><input type="text" th:field="*{materialCode}" required /></td>
<td></td>
</tr>
<tr>
<td><b><label for="quantity" th:text="#{material.inventoryQuantity} + ':'"></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"
th:value="${material.inventoryQuantity}" required>
<form th:action="@{/material/creator}" th:object="${material}" class="requireAuth" enctype="multipart/form-data"
method="POST">
<div class="content">
<div class="formWrap">
<div class="formColumn">
<div>
<label th:for="${#ids.next('materialCode')}" th:text="#{material.code} + ':'"></label>
</div>
<div>
<label for="quantity" th:text="#{material.inventoryQuantity} + ':'"></label>
</div>
<div>
<label th:for="${#ids.next('materialType')}" th:text="#{material.type} + ':'"></label>
</div>
<div>
<label for="simdut" th:text="#{material.simdutFile} + ':'"></label>
</div>
</div>
<div class="formColumn">
<div>
<input type="text"
class="rawInput"
th:field="*{materialCode}"
style="width: 145px"
required/>
</div>
<div style="display: flex; flex-direction: row">
<input class="rawInput"
data-unit="mL"
id="quantity"
min="0"
name="quantity"
onchange="calculateMilliliters(this.value, this.dataset.unit)"
required
step="0.001"
th:value="${material.inventoryQuantity}"
type="number"
style="margin-right: 10px; width: 50px">
<!--Contient la quantité en millilitres-->
<input type="hidden" th:field="*{inventoryQuantity}"/>
</td>
<input type="hidden"
th:field="*{inventoryQuantity}"/>
<td>
<select id="units" name="units"
<select id="units"
name="units"
onchange="switchUnits(this)">
<option selected value="mL" th:text="#{units.milliliters}"></option>
<option value="L" th:text="#{units.liters}"></option>
<option value="gal" th:text="#{units.gallons}"></option>
</select>
</td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('materialType')}" th:text="#{material.type} + ':'"></label></b>
</td>
<td>
</div>
<div>
<select th:field="*{materialType}" required>
<option th:each="materialType : ${materialTypes}" th:value="${materialType.materialTypeID}"
th:text="${materialType.materialTypeName}"></option>
</select>
</td>
</tr>
<tr>
<td><b><label for="simdut" th:text="#{material.simdutFile} + ':'"></label></b></td>
<td><input id="simdut" name="simdut" type="file"/></td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>
</div>
<div>
<input id="simdut"
class="rawInput"
name="simdut"
type="file"/>
</div>
</div>
</div>
</div>
<div th:include="fragments.html :: formEndButtons"></div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
<script>
function switchUnits(unitSelect) {
const quantityElem = document.querySelector("#quantity");

View File

@ -1,7 +1,8 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{material.editing.title(${material.materialCode})})"></th:block>
<th:block
th:include="fragments.html :: head(#{material.editing.title(${material.materialCode})}, 'form')"></th:block>
<link th:href="@{|${baseUrl}/css/forms.css|}" rel="stylesheet"/>
</head>
@ -14,58 +15,67 @@
<div th:include="fragments.html :: messages"></div>
<h1 class="materialCode" th:text="#{material.editing.title(${material.materialCode})}"></h1>
<div class="form">
<form th:action="@{/material/editor}" th:object="${material}" class="requireAuth" method="POST">
<input type="hidden" th:field="*{materialID}"/>
<table>
<tr>
<td><b th:text="#{keyword.id} + ':'"></b></td>
<td th:text="${material.materialID}"></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('materialCode')}" th:text="#{material.code} + ':'"></label></b>
</td>
<td><input type="text" th:field="*{materialCode}"></td>
</tr>
<tr>
<td><b><label for="quantity" th:text="#{material.inventoryQuantity}"></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>
<form th:action="@{/material/editor}" th:object="${material}" class="requireAuth" method="POST">
<input type="hidden" th:field="*{materialID}"/>
<!--Contient la quantité en millilitres-->
<input type="hidden" th:field="*{inventoryQuantity}"/>
<td>
<select id="units" name="units"
<div class="content">
<div class="formWrap">
<div class="formColumn">
<div>
<label th:for="${#ids.next('materialID')}" th:text="#{keyword.id} + ':'"></label>
</div>
<div>
<label th:for="${#ids.next('materialCode')}" th:text="#{material.code} + ':'"></label>
</div>
<div>
<label for="quantity" th:text="#{material.inventoryQuantity}"></label>
</div>
<div>
<label th:for="${#ids.next('materialType')}">Type de produit: </label>
</div>
</div>
<div class="formColumn">
<div>
<input type="number" th:field="*{materialID}" required disabled/>
</div>
<div>
<input type="text" th:field="*{materialCode}"/>
</div>
<div style="display: flex; flex-direction: row">
<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"
style="margin-right: 10px; width: 50px">
<!--Contient la quantité en millilitres-->
<input type="hidden" th:field="*{inventoryQuantity}"/>
<select id="units"
name="units"
onchange="switchUnits(this)">
<option selected value="mL" th:text="#{units.milliliters}"></option>
<option value="L" th:text="#{units.liters}"></option>
<option value="gal" th:text="#{units.gallons}"></option>
</select>
</td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('materialType')}">Type de produit: </label></b></td>
<td><select th:field="*{materialType}">
<option th:each="mType : ${materialTypes}"
th:attrappend="selected=${mType.materialTypeID == material.materialType.materialTypeID}"
th:text="${mType.materialTypeName}"
th:value="${mType.materialTypeID}"></option>
</select></td>
</tr>
<tr>
<td>
<button id="showSIMDUT" type="button" th:text="#{material.simdutFile}">Fichier SIMDUT</button>
</td>
<td>
<button id="editSIMDUT" type="button" th:text="#{keyword.edit}"></button>
</td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>
</div>
<div>
<select th:field="*{materialType}">
<option th:each="mType : ${materialTypes}"
th:attrappend="selected=${mType.materialTypeID == material.materialType.materialTypeID}"
th:text="${mType.materialTypeName}"
th:value="${mType.materialTypeID}"></option>
</select>
</div>
</div>
</div>
<div th:include="fragments.html :: formEndButtons"></div>
</div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{material.edit.title})"></th:block>
<th:block th:include="fragments.html :: head(#{material.edit.title}, null)"></th:block>
<style>
table {

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{material.delete.title})"></th:block>
<th:block th:include="fragments.html :: head(#{material.delete.title}, null)"></th:block>
<style>
table {

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{material.add.title})"></th:block>
<th:block th:include="fragments.html :: head(#{material.add.title}, null)"></th:block>
</head>
<body>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{materialType.add.title})"></th:block>
<th:block th:include="fragments.html :: head(#{materialType.add.title}, 'form')"></th:block>
<link rel="stylesheet" th:href="@{|${baseUrl}/css/forms.css|}"/>
</head>
@ -14,30 +14,46 @@
<div th:include="fragments.html :: messages"></div>
<h1 th:text="#{materialType.add.title}"></h1>
<div class="form">
<form th:action="@{/materialType/creator}" th:object="${materialType}" class="requireAuth" method="POST">
<table>
<tr>
<td><b><label th:for="${#ids.next('materialTypeName')}"
th:text="#{materialType.name} + ':'"></label></b>
</td>
<td><input type="text" th:field="*{materialTypeName}" required></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('prefix')}"
th:text="#{materialType.prefix} + ' (3 ' + #{keyword.characters} + '):'"></label></b>
</td>
<td><input maxlength="3" minlength="3" type="text" th:field="*{prefix}" required></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('usePercentages')}"
th:text="#{materialType.usePercents} + ':'"></label></b></td>
<td><input type="checkbox" th:field="*{usePercentages}"></td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>
<form th:action="@{/materialType/creator}" th:object="${materialType}" class="requireAuth" method="POST">
<div class="content">
<div class="formWrap">
<div class="formColumn">
<div>
<label th:for="${#ids.next('materialTypeName')}"
th:text="#{materialType.name} + ':'"></label>
</div>
<div><label th:for="${#ids.next('prefix')}"
th:text="#{materialType.prefix} + ' (3 ' + #{keyword.characters} + '):'"></label>
</div>
<div>
<label th:for="${#ids.next('usePercentages')}"
th:text="#{materialType.usePercents} + ':'"></label>
</div>
</div>
<div class="formColumn">
<div>
<input required
class="rawInput"
th:field="*{materialTypeName}"
type="text"/>
</div>
<div>
<input maxlength="3"
minlength="3"
class="rawInput"
required
th:field="*{prefix}"
type="text"/>
</div>
<div>
<input type="checkbox"
th:field="*{usePercentages}"/>
</div>
</div>
</div>
<div th:include="fragments.html :: formEndButtons"></div>
</div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>

View File

@ -2,9 +2,7 @@
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block
th:include="fragments.html :: head(#{materialType.editing.title(${materialType.materialTypeName})})"></th:block>
<link th:href="@{|${baseUrl}/css/forms.css|}" rel="stylesheet"/>
th:include="fragments.html :: head(#{materialType.editing.title(${materialType.materialTypeName})}, 'form')"></th:block>
</head>
<body>
@ -15,35 +13,44 @@
<div th:include="fragments.html :: messages"></div>
<h1 class="materialCode" th:text="#{materialType.editing.title(${materialType.materialTypeName})}"></h1>
<div class="form">
<form th:action="@{/materialType/editor}" th:object="${materialType}" class="requireAuth" method="POST">
<input type="hidden" th:field="*{materialTypeID}">
<table>
<tr>
<td><b th:text="#{keyword.id} + ':'"></b></td>
<td th:text="${materialType.materialTypeID}"></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('materialTypeName')}"
th:text="#{materialType.name} + ':'"></label></b></td>
<td><input type="text" th:field="*{materialTypeName}" required></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('prefix')}"
th:text="#{materialType.prefix} + ' (3 ' + #{keyword.characters} + '):'"></label></b>
</td>
<td><input type="text" minlength="3" maxlength="3" th:field="*{prefix}" required></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('usePercentages')}"
th:text="#{materialType.usePercents} + ':'"></label></b></td>
<td><input type="checkbox" th:field="*{usePercentages}"></td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>
<form th:action="@{/materialType/editor}" th:object="${materialType}" class="requireAuth" method="POST">
<div class="content">
<div class="formWrap">
<div class="formColumn">
<div>
<label th:for="${#ids.next('materialTypeID')}" th:text="#{material.type}"></label>
</div>
<div>
<label th:for="${#ids.next('materialTypeName')}" th:text="#{materialType.name} + ':'"></label>
</div>
<div>
<label th:for="${#ids.next('prefix')}"
th:text="#{materialType.prefix} + ' (3 ' + #{keyword.characters} + '):'"></label>
</div>
<div>
<label th:for="${#ids.next('usePercentages')}"
th:text="#{materialType.usePercents} + ':'"></label>
</div>
</div>
<div class="formColumn">
<div>
<input type="number" th:field="*{materialTypeID}" required disabled/>
</div>
<div>
<input type="text" th:field="*{materialTypeName}" required/>
</div>
<div>
<input type="text" minlength="3" maxlength="3" th:field="*{prefix}" required/>
</div>
<div>
<input type="checkbox" th:field="*{usePercentages}"/>
</div>
</div>
</div>
<div th:include="fragments.html :: formEndButtons"></div>
</div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{materialType.editor.title})"></th:block>
<th:block th:include="fragments.html :: head(#{materialType.editor.title}, null)"></th:block>
<style>
table {

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{materialType.delete.title})"></th:block>
<th:block th:include="fragments.html :: head(#{materialType.delete.title}, null)"></th:block>
<style>
table {

View File

@ -1,16 +1,9 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{mix.add.title})"></th:block>
<th:block th:include="fragments.html :: head(#{mix.add.title}, 'mix')"></th:block>
<link href="/css/forms.css" rel="stylesheet"/>
<link href="/css/mix.css" rel="stylesheet"/>
<style>
table {
text-align: center;
}
</style>
<link rel="stylesheet" href="/css/form.css">
</head>
<body>
@ -23,39 +16,58 @@
<br/>
<br/>
<div class="form">
<form th:action="@{/mix/creator}" class="requireAuth" method="POST">
<label for="mixType" th:text="#{mix.mixType} + ':'"></label><input type="text" id="mixType"
name="mixTypeName"
placeholder="ex: Teinture" required/>
<form th:action="@{/mix/creator}" class="requireAuth" method="POST">
<!-- Information nécessaire à la création des mélanges -->
<input id="recipeID" name="recipe" th:value="${recipe.recipeID}" type="hidden"/>
<!-- Information nécessaire à la création des mélanges -->
<input id="recipeID" name="recipe" th:value="${recipe.recipeID}" type="hidden"/>
<table>
<tr>
<td>
<!-- Produits -->
<table id="materials">
<tr>
<th th:text="#{keyword.material}"></th>
<th>
<span th:text="#{keyword.quantity}"></span>
<button type="button">+</button>
</th>
</tr>
</table>
</td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>
<div class="content">
<div class="flexContent formWrap">
<div class="formColumn">
<div>
<label for="mixType" th:text="#{mix.mixType} + ':'"></label>
</div>
<div>
<label for="materialType" th:text="#{material.type} + ':'"></label>
</div>
</div>
<div class="formColumn">
<div>
<input type="text" id="mixType"
name="mixTypeName"
placeholder="ex: Teinture"
required/>
</div>
<div>
<select name="materialType" id="materialType">
<option th:each="materialType : ${materialTypes}" th:value="${materialType.materialTypeID}"
th:text="${materialType.materialTypeName}"></option>
</select></div>
</div>
</div>
<div class="flexContent materialListWrap" style="margin: auto">
<div class="mixMaterialListTitle">
<div class="positionButtons">
</div>
<div class="material">
<h3 th:text="#{keyword.material}"></h3>
</div>
<div class="quantity">
<h3 th:text="#{keyword.quantity}"></h3>
</div>
<div class="units">
<button type="button">+</button>
</div>
<div class="removeButton"></div>
</div>
<div class="mixMaterialList"></div>
</div>
<div th:include="fragments.html :: formEndButtons"></div>
</div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
<script src="/js/mix.js"></script>
<script>
/*<![CDATA[*/
@ -67,7 +79,7 @@
});
function init() {
addMaterial(null, null);
addMaterial();
}
/*]]*/

View File

@ -1,16 +1,9 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{mix.edit.title})"></th:block>
<th:block th:include="fragments.html :: head(#{mix.edit.title}, 'mix')"></th:block>
<link href="/css/forms.css" rel="stylesheet"/>
<link href="/css/mix.css" rel="stylesheet"/>
<style>
table {
text-align: center;
}
</style>
<link href="/css/form.css" rel="stylesheet"/>
</head>
<body th:with="nbrMaterials = 0">
<!-- Fragment de l'entête -->
@ -18,47 +11,69 @@
<!-- Corps de la page -->
<section>
<div th:include="fragments.html :: messages"></div>
<h1 th:text="#{mix.edit.subtitle(${recipeCode}, ${mix.mixType.typeName})}"></h1>
<button id="removeMix" type="button" th:text="#{keyword.delete}"></button>
<h1 th:text="#{mix.edit.subtitle(${recipe.recipeCode}, ${mix.mixType.typeName})}"></h1>
<br/>
<br/>
<div class="form">
<form th: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"/>
<input name="recipeID" id="recipeID" th:value="${mix.recipe.recipeID}" type="hidden"/>
<form th:action="@{/mix/editor/}" class="requireAuth" method="POST">
<!-- Information nécessaire à la création des mélanges -->
<input id="recipeID" name="recipe" th:value="${recipe.recipeID}" type="hidden"/>
<input id="mixID" name="mixID" th:value="${mix.mixID}" type="hidden"/>
<label for="mixType" th:text="#{mix.mixType} + ':'"></label>
<input type="text"
id="mixType"
name="mixTypeName"
th:value="${mix.mixType.typeName}"
required/>
<table>
<tr>
<td colspan="2">
<table id="materials">
<tr>
<th th:text="#{keyword.material}"></th>
<th>
<span th:text="#{keyword.quantity}"></span>
<button type="button">+</button>
</th>
</tr>
</table>
</td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>
<div class="content">
<div class="flexContent formWrap">
<div class="formColumn">
<div>
<label for="mixType" th:text="#{mix.mixType} + ':'"></label>
</div>
<div>
<label for="materialType" th:text="#{material.type} + ':'"></label>
</div>
</div>
<div class="formColumn">
<div>
<input type="text" id="mixType"
name="mixTypeName"
placeholder="ex: Teinture"
th:value="${mix.mixType.typeName}"
required/>
</div>
<div>
<select name="materialType" id="materialType">
<option th:each="currentMaterialType : ${materialTypes}"
th:value="${currentMaterialType.materialTypeID}"
th:text="${currentMaterialType.materialTypeName}"
th:selected="${currentMaterialType.materialTypeID == materialType.materialTypeID}"></option>
</select></div>
</div>
</div>
<div class="flexContent materialListWrap" style="margin: auto">
<div class="mixMaterialListTitle">
<div class="positionButtons">
</div>
<div class="material">
<h3 th:text="#{keyword.material}"></h3>
</div>
<div class="quantity">
<h3 th:text="#{keyword.quantity}"></h3>
</div>
<div class="units">
<button type="button">+</button>
</div>
<div class="removeButton"></div>
</div>
<div class="mixMaterialList"></div>
</div>
<div th:include="fragments.html :: formEndButtons"></div>
<div>
<button type="button" id="removeMix" th:text="#{keyword.delete}"></button>
</div>
</div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
<script src="/js/mix.js"></script>
<script>
/*<![CDATA[*/
@ -72,7 +87,7 @@
document.querySelector("#removeMix").addEventListener("click", () => {
showElement(errorMsgBox);
checkPassword(null, () => document.location.href = `/mix/remover/${mix.mixID}`);
checkPassword(null, () => document.location.href = `/mix/remover/[[${mix.mixID}]]`);
});
})();

View File

@ -13,17 +13,15 @@
autocomplete="off"
data-usepercentages="false"
required/>
<div class="materialList">
<p
th:each="material : ${materials}"
th:text="${material.materialType.materialTypeName == 'Aucun' ? '' : '[' + material.materialType.prefix + ']'} + ' ' + ${material.materialCode}"
th:data-materialcode="${material.materialCode}"
th:data-materialtype="${material.materialType.materialTypeName}"
th:data-usepercentages="${material.materialType.usePercentages}"
onclick="selectMaterial(this)"></p>
th:data-usepercentages="${material.materialType.usePercentages}"></p>
</div>
</div>
</body>
</html>
</html>

View File

@ -0,0 +1,155 @@
<div class="form">
<form th:action="@{/recipe/editor}" th:object="${recipe}" class="requireAuth" method="POST">
<input type="hidden" th:field="*{recipeID}"/>
<table class="mainTable">
<tr>
<td>
<table>
<tr>
<td><b th:text="#{keyword.id} + ':'"></b></td>
<td th:text="${recipe.recipeID}"></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('recipeCode')}"
th:text="#{recipe.color} + ':'"></label></b></td>
<td><input type="text" th:field="*{recipeCode}"></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('company')}"
th:text="#{keyword.company} + ':'"></label></b>
</td>
<td>
<select th:field="*{company}">
<option th:each="company : ${companies}"
th:selected="${recipe.company.equals(company)}"
th:text="${company.companyName}"
th:value="${company.companyID}"></option>
</select>
</td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('recipeDescription')}"
th:text="#{recipe.description} + ':'"></label></b></td>
<td><input type="text" th:field="*{recipeDescription}"/></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('sample')}" th:text="#{recipe.sample} + ':'"></label></b>
</td>
<td><input type="number" th:field="*{sample}"/></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('approbationDate')}"
th:text="#{recipe.approbationDate} + ':'"></label></b></td>
<td><input type="date" th:field="*{approbationDate}"/></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('remark')}" th:text="#{recipe.remark} + ':'"></label></b>
</td>
<td><textarea cols="20" rows="2" th:field="*{remark}"></textarea>
</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td colspan="2" style="height: 40px; text-align: right">
<button id="newMix" type="button" th:text="#{recipe.edit.addMix}"></button>
</td>
</tr>
<th:block th:each="mix : ${mixes}">
<tr>
<td class="mixNameColumn">
<b th:text="${mix.mixType.typeName} + ':'"></b>
<br/>
<th:block th:if="${mix != null}">
<button class="mixEditor" th:data-mixID="${mix.mixID}" type="button"
th:text="#{keyword.edit}"></button>
</th:block>
</td>
<td>
<div class="recipe">
<table style="margin-left: 50px">
<!-- Produits -->
<tr>
<th th:text="#{keyword.material}"></th>
<th th:text="#{keyword.type}"></th>
<th th:text="#{keyword.quantity}"></th>
</tr>
<tr th:each="mixQuantity : ${mix.mixQuantities}"
th:with="material = ${mixQuantity.material}">
<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} + ' ' + ${material.materialType.usePercentages ? '%' : 'mL'}"></td>
</tr>
</table>
</div>
</td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
</th:block>
</table>
</td>
</tr>
<tr>
<td>
<!-- Images -->
<table>
<tr th:each="image : ${images}">
<td class="centerTD">
<img alt="Image supprimée ou corrompue" th:src="'/images/' + ${image}"
height="250px"/>
</td>
<td style="text-align: left; padding-left: 50px">
<button class="deleteImg" th:data-image="${image}" type="button"
style="height: 250px">
<span th:text="#{keyword.delete}"
style="display: inline-block; transform: rotate(90deg); font-weight: bolder"></span>
</button>
</td>
</tr>
</table>
</td>
<td>
<!-- Étapes -->
<table>
<tr>
<td style="text-align: center">
<h3 th:text="#{recipe.steps}"></h3>
</td>
<td style="text-align: left; vertical-align: middle; padding-left: 20px">
<button id="addStep" type="button">+</button>
</td>
<td style="width: 100%"></td>
</tr>
<tr>
<td colspan="3">
<table id="steps" style="width:100%">
<tbody></tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<button id="addImg" th:data-recipeID="${recipe.recipeID}" type="button"
th:text="#{recipe.edit.addImage}"></button>
</td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{recipe.add.title})"></th:block>
<th:block th:include="fragments.html :: head(#{recipe.add.title}, null)"></th:block>
</head>
<body>

View File

@ -1,10 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{recipe.add.title})"></th:block>
<link href="/css/main.css" rel="stylesheet"/>
<link href="/css/forms.css" rel="stylesheet"/>
<th:block th:include="fragments.html :: head(#{recipe.add.title}, 'form')"></th:block>
</head>
<body>
<!-- Fragment de l'entête -->
@ -14,42 +11,58 @@
<div th:include="fragments.html :: messages"></div>
<h1 th:text="#{recipe.add.title}"></h1>
<div class="form">
<form th:action="@{/recipe/creator}" th:object="${recipe}" class="requireAuth" id="recipe-form" method="POST">
<table>
<tr>
<td><b><label th:for="${#ids.next('recipeCode')}" th:text="#{recipe.color} + ':'"></label></b></td>
<td><input type="text" th:field="*{recipeCode}" required/></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('company')}" th:text="#{keyword.company} + ':'"></label></b></td>
<td><select th:field="*{company}">
<option th:each="company : ${companies}" th:text="${company.companyName}"
th:value="${company.companyID}"></option>
</select></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('recipeDescription')}"
th:text="#{recipe.description} + ':'"></label></b></td>
<td><input type="text" th:field="*{recipeDescription}" required/></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('sample')}" th:text="#{recipe.sample} + ':'"></label></b></td>
<td><input type="number" th:field="*{sample}" required/></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('approbationDate')}"
th:text="#{recipe.approbationDate} + ':'"></label></b></td>
<td><input type="date" th:field="*{approbationDate}"/></td>
</tr>
<tr>
<td><b><label th:for="${#ids.next('remark')}" th:text="#{recipe.remark}"></label></b></td>
<td><textarea cols="20" form="recipe-form" rows="2" th:field="*{remark}"></textarea></td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>
<form th:action="@{/recipe/creator}" th:object="${recipe}" class="requireAuth" id="recipe-form" method="POST">
<div class="content">
<div class="formWrap">
<div class="formColumn">
<div>
<label th:for="${#ids.next('recipeCode')}" th:text="#{recipe.color} + ':'"></label>
</div>
<div>
<label th:for="${#ids.next('company')}" th:text="#{keyword.company} + ':'"></label>
</div>
<div>
<label th:for="${#ids.next('recipeDescription')}" th:text="#{recipe.description} + ':'"></label>
</div>
<div>
<label th:for="${#ids.next('sample')}" th:text="#{recipe.sample} + ':'"></label>
</div>
<div>
<label th:for="${#ids.next('approbationDate')}"
th:text="#{recipe.approbationDate} + ':'"></label>
</div>
<div>
<label th:for="${#ids.next('remark')}" th:text="#{recipe.remark} + ':'"></label>
</div>
</div>
<div class="formColumn">
<div>
<input type="text" th:field="*{recipeCode}" required/>
</div>
<div>
<select th:field="*{company}">
<option th:each="company : ${companies}" th:text="${company.companyName}"
th:value="${company.companyID}"></option>
</select>
</div>
<div>
<input type="text" th:field="*{recipeDescription}" required/>
</div>
<div>
<input type="number" th:field="*{sample}" required/>
</div>
<div>
<input type="date" th:field="*{approbationDate}"/>
</div>
<div>
<textarea cols="20" form="recipe-form" rows="2" th:field="*{remark}"></textarea>
</div>
</div>
</div>
<div th:include="fragments.html :: formEndButtons"></div>
</div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>

View File

@ -1,42 +1,69 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{recipe.editing.title(${recipe.recipeCode})})"></th:block>
<th:block th:include="fragments.html :: head(#{recipe.editing.title(${recipe.recipeCode})}, 'form')"></th:block>
<link href="/css/forms.css" rel="stylesheet"/>
<link href="/css/explore.css" rel="stylesheet"/>
<style>
p {
display: inline;
/*p {*/
/* display: inline;*/
/*}*/
/*.recipe table {*/
/* background-color: #fafafa;*/
/* border: 1px solid #7a7a7a;*/
/* border-collapse: collapse !important;*/
/*}*/
/*.recipe td, .recipe th {*/
/* min-width: 100px;*/
/* text-align: center;*/
/*}*/
/*.recipe tr:nth-child(odd) {*/
/* background-color: #f5f5f5;*/
/*}*/
/*.mixNameColumn {*/
/* display: inline-block;*/
/*}*/
.content {
flex-direction: row;
}
table {
border-collapse: separate !important;
.mix {
display: flex;
flex-direction: row;
width: 368px;
}
.recipe table {
background-color: #fafafa;
border: 1px solid #7a7a7a;
border-collapse: collapse !important;
}
.recipe td, .recipe th {
min-width: 100px;
text-align: center;
}
.recipe tr:nth-child(odd) {
background-color: #f5f5f5;
}
.mixNameColumn {
.mixInfo {
display: flex;
flex-direction: column;
min-width: 150px;
max-width: 200px;
text-align: left;
}
.mixNameColumn {
display: inline-block;
min-width: 150px;
.materialCodeColumn, .materialTypeColumn, .materialQuantityColumn {
min-width: 112px;
}
.imageContainer {
display: flex;
flex-direction: column;
padding: 10px;
}
#steps input {
min-width: 300px;
text-align: left;
}
#steps label {
vertical-align: middle;
}
</style>
</head>
@ -50,164 +77,135 @@
<h1 th:text="#{recipe.editing.title(${recipe.recipeCode})}"></h1>
<button id="gotoRecipe" type="button" th:text="#{keyword.see}"></button>
<br/>
<br/>
<div class="form">
<form th:action="@{/recipe/editor}" th:object="${recipe}" class="requireAuth" method="POST">
<input type="hidden" th:field="*{recipeID}"/>
<form th:action="@{/recipe/editor}" th:object="${recipe}" class="requireAuth" method="POST">
<div class="content">
<!-- Informations -->
<div class="flexContent" style="padding-top: 40px">
<input type="hidden" th:field="*{recipeID}"/>
<div class="formWrap">
<div class="formColumn">
<div>
<label th:for="${#ids.next('recipeID')}" th:text="#{keyword.id}"></label>
</div>
<div>
<label th:for="${#ids.next('recipeCode')}" th:text="#{recipe.color}"></label>
</div>
<div>
<label th:for="${#ids.next('banner')}" th:text="#{keyword.company}"></label>
</div>
<div>
<label th:for="${#ids.next('recipeDescription')}" th:text="#{recipe.description}"></label>
</div>
<div>
<label th:for="${#ids.next('sample')}" th:text="#{recipe.sample}"></label>
</div>
<div>
<label th:for="${#ids.next('approbationDate')}" th:text="#{recipe.approbationDate}"></label>
</div>
<div>
<label th:for="${#ids.next('remark')}" th:text="#{recipe.remark}"></label>
</div>
</div>
<div class="formColumn">
<div>
<input type="number" th:field="*{recipeID}" required disabled/>
</div>
<div>
<input type="text" th:field="*{recipeCode}" required/>
</div>
<div>
<select th:field="*{company}">
<option th:each="company : ${companies}"
th:selected="${recipe.company.equals(company)}"
th:text="${company.companyName}"
th:value="${company.companyID}"></option>
</select>
</div>
<div>
<input type="text" th:field="*{recipeDescription}"/>
</div>
<div>
<input type="number" th:field="*{sample}"/>
</div>
<div>
<input type="date" th:field="*{approbationDate}"/>
</div>
<div>
<textarea cols="20" rows="2" th:field="*{remark}"></textarea>
</div>
</div>
</div>
</div>
<!-- Mélanges -->
<div class="flexContent mixes">
<div style="text-align: right">
<button id="newMix" type="button" th:text="#{recipe.edit.addMix}"></button>
</div>
<div th:each="mix : ${mixes}" class="mixContainer">
<div class="mixInfo">
<b th:text="${mix.mixType.typeName} + ':'"></b>
<button class="mixEditor" th:data-mixID="${mix.mixID}" type="button"
th:text="#{keyword.edit}" style="width: 75%"></button>
</div>
<div>
<table class="mix"
th:id="'mix-' + ${mix.mixID}">
<tr>
<th th:text="#{keyword.material}"></th>
<th th:text="#{keyword.type}"></th>
<th th:text="#{keyword.quantity}"></th>
</tr>
<!-- Produits -->
<tr th:each="mixQuantity : ${mix.mixQuantities}"
th:with="material = ${mixQuantity.material}"
th:id="'material-' + ${material.materialID}">
<td class="materialCodeColumn"
th:classappend="${material.isMixType()} ? '' : materialCode"
th:data-materialID="${material.materialID}"
th:text="${material.materialCode}"></td>
<td class="materialTypeColumn"
th:text="${material.materialType.materialTypeName}"></td>
<td class="materialQuantityColumn"
th:text="${mixQuantity.quantity} + ' ' + ${material.materialType.usePercentages ? '%' : 'mL'}"></td>
</tr>
</table>
</div>
<div class="mixActionsContainer">
<button class="useMixSubmit" type="button" th:text="#{keyword.use}"></button>
</div>
</div>
</div>
<!-- Étapes -->
<div class="flexContent" id="steps" style="display: flex; flex-direction: column">
<div style="display: flex; flex-direction: row; justify-content: center">
<h3 th:text="#{recipe.steps}"></h3>
<button id="addStep" type="button" style="width: 20px; height: 20px; margin:20px">+</button>
</div>
<table class="mainTable">
<tr>
<td>
<table>
<tr>
<td><b th:text="#{keyword.id} + ':'"></b></td>
<td th:text="${recipe.recipeID}"></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('recipeCode')}"
th:text="#{recipe.color} + ':'"></label></b></td>
<td><input type="text" th:field="*{recipeCode}"></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('company')}"
th:text="#{keyword.company} + ':'"></label></b>
</td>
<td>
<select th:field="*{company}">
<option th:each="company : ${companies}"
th:selected="${recipe.company.equals(company)}"
th:text="${company.companyName}"
th:value="${company.companyID}"></option>
</select>
</td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('recipeDescription')}"
th:text="#{recipe.description} + ':'"></label></b></td>
<td><input type="text" th:field="*{recipeDescription}"/></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('sample')}" th:text="#{recipe.sample} + ':'"></label></b>
</td>
<td><input type="number" th:field="*{sample}"/></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('approbationDate')}"
th:text="#{recipe.approbationDate} + ':'"></label></b></td>
<td><input type="date" th:field="*{approbationDate}"/></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b><label th:for="${#ids.next('remark')}" th:text="#{recipe.remark} + ':'"></label></b>
</td>
<td><textarea cols="20" rows="2" th:field="*{remark}"></textarea>
</td>
</tr>
</table>
</td>
<td>
<table>
<tr>
<td colspan="2" style="height: 40px; text-align: right">
<button id="newMix" type="button" th:text="#{recipe.edit.addMix}"></button>
</td>
</tr>
<th:block th:each="mix : ${mixes}">
<tr>
<td class="mixNameColumn">
<b th:text="${mix.mixType.typeName} + ':'"></b>
<br/>
<th:block th:if="${mix != null}">
<button class="mixEditor" th:data-mixID="${mix.mixID}" type="button"
th:text="#{keyword.edit}"></button>
</th:block>
</td>
<td>
<div class="recipe">
<table style="margin-left: 50px">
<!-- Produits -->
<tr>
<th th:text="#{keyword.material}"></th>
<th th:text="#{keyword.type}"></th>
<th th:text="#{keyword.quantity}"></th>
</tr>
<tr th:each="mixQuantity : ${mix.mixQuantities}"
th:with="material = ${mixQuantity.material}">
<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} + ' ' + ${material.materialType.usePercentages ? '%' : 'mL'}"></td>
</tr>
</table>
</div>
</td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
</th:block>
</table>
</td>
</tr>
<tr>
<td>
<!-- Images -->
<table>
<tr th:each="image : ${images}">
<td class="centerTD">
<img alt="Image supprimée ou corrompue" th:src="'/images/' + ${image}"
height="250px"/>
</td>
<td style="text-align: left; padding-left: 50px">
<button class="deleteImg" th:data-image="${image}" type="button"
style="height: 250px">
<span th:text="#{keyword.delete}"
style="display: inline-block; transform: rotate(90deg); font-weight: bolder"></span>
</button>
</td>
</tr>
</table>
</td>
<td>
<!-- Étapes -->
<table>
<tr>
<td style="text-align: center">
<h3 th:text="#{recipe.steps}"></h3>
</td>
<td style="text-align: left; vertical-align: middle; padding-left: 20px">
<button id="addStep" type="button">+</button>
</td>
<td style="width: 100%"></td>
</tr>
<tr>
<td colspan="3">
<table id="steps" style="width:100%">
<tbody></tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<button id="addImg" th:data-recipeID="${recipe.recipeID}" type="button"
th:text="#{recipe.edit.addImage}"></button>
</td>
</tr>
<tr th:include="fragments.html :: mainTableButtons"></tr>
</table>
</form>
</div>
</div>
<!-- Images -->
<div class="flexContent" style="display: flex; flex-direction: column">
<div style="display: flex; flex-direction: row; justify-content: center;">
<h3 th:text="#{keyword.images}" style="margin: 0"></h3>
<button id="addImage" type="button" style="width: 20px; height: 20px; margin: 0 20px;">+</button>
</div>
<div class="imagesContainer">
<div th:each="image : ${images}" class="imageContainer">
<img th:alt="#{recipe.image.corrupted}"
th:src="@{|${baseUrl}/images/${image}|}"
width="400px"/>
<div>
<button class="deleteImg" th:data-image="${image}" type="button" style="width: 100%">
<span th:text="#{keyword.delete}"></span>
</button>
</div>
</div>
</div>
</div>
<div th:include="fragments.html :: formEndButtons" style="width: 100%; margin-bottom:50px"></div>
</div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
@ -215,7 +213,6 @@
<script>
/*<![CDATA[*/
let stepNbr = 0;
(() => {
@ -239,34 +236,38 @@
document.querySelectorAll(".deleteImg").forEach(e => {
e.addEventListener("click", async () => {
checkPassword(null, () => {
let data = {};
if (confirm(askChangePage)) {
checkPassword(null, () => {
let data = {};
data['image'] = e.getAttribute("data-image");
hideElement(errorMsgBox);
data['image'] = e.getAttribute("data-image");
hideElement(errorMsgBox);
axios.post("/images/delete", data)
.then(r => {
const data = r.data;
axios.post("/images/delete", data)
.then(r => {
const data = r.data;
if (data['error'] !== undefined) {
errorMsgBoxText.innerText = data['error'];
if (data['error'] !== undefined) {
errorMsgBoxText.innerText = data['error'];
showElement(errorMsgBox);
} else {
document.location.reload();
}
})
.catch(e => {
console.log(e);
errorMsgBoxText.innerText = "[[#{error.serverError}]]";
showElement(errorMsgBox);
} else {
document.location.reload();
}
})
.catch(e => {
console.log(e);
errorMsgBoxText.innerText = "[[#{error.serverError}]]";
showElement(errorMsgBox);
});
});
});
});
}
});
});
document.querySelector("#addImg").addEventListener("click", () => {
document.location.href = "/images/add/[[${recipe.recipeID}]]";
document.querySelector("#addImage").addEventListener("click", () => {
if (confirm(askChangePage)) {
document.location.href = "/images/add/[[${recipe.recipeID}]]";
}
});
const recipeText = "[[${recipeJSON}]]";
@ -283,37 +284,37 @@
})();
function addStep(value) {
let input = document.createElement("input");
input.type = "text";
input.id = `step_${stepNbr}`;
input.classList.add("step", "rawInput");
input.name = "step";
input.style.width = "100%";
input.autocomplete = "off";
if (value != null) input.value = value;
let column = document.createElement("td");
column.appendChild(input);
let number = document.createElement("label");
number.textContent = `${stepNbr + 1}. `;
number.for = input.id;
let deleteButton = document.createElement("button");
deleteButton.type = "button";
deleteButton.innerText = "[[#{keyword.remove}]]";
deleteButton.addEventListener("click", () => deleteStep(input.id));
let deleteButtonColumn = document.createElement("td");
deleteButtonColumn.appendChild(deleteButton);
let div = document.createElement("div");
div.appendChild(number);
div.appendChild(input);
div.appendChild(deleteButton);
let row = document.createElement("tr");
row.appendChild(column);
row.appendChild(deleteButtonColumn)
document.querySelector("#steps tbody").appendChild(row);
document.querySelector("#steps").appendChild(div);
stepNbr++;
}
function deleteStep(stepID) {
// Supprime la rangée de l'étape
document.querySelector(`#${stepID}`).parentElement.parentElement.remove();
document.querySelector(`#${stepID}`).parentElement.remove();
}
/*]]*/

View File

@ -1,43 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{recipe.edit.title})"></th:block>
<title th:text="#{recipe.edit.title}"></title>
<style>
h1 {
margin-bottom: 0;
}
th {
padding: 0 15px;
}
td {
padding: 0 20px;
}
.descriptionCell {
border-right-color: #e6e6e6;
border-right-style: solid;
border-right-width: 2px;
}
table {
border-collapse: separate;
border-spacing: 0 10px;
margin: 0 auto;
text-align: center;
}
section {
text-align: center;
}
.recipeDescription {
max-width: 400px;
}
</style>
<th:block th:include="fragments.html :: head(#{recipe.edit.title}, 'recipesList')"></th:block>
</head>
<body>
@ -45,41 +9,49 @@
<header th:include="fragments.html :: header"></header>
<!-- Corps de la page -->
<section>
<div id="researchBoxContainer">
<input id="researchBox" class="noStyle" type="text" th:placeholder="#{keyword.search}"
onkeyup="performSearch(this.value)"/>
</div>
<div th:include="fragments.html :: messages"></div>
<p class="success" th:if="${recipeCode != null}"
th:text="#{recipe.success.edit(${recipeCode})}"></p>
<h1 th:text="#{recipe.edit.title}"></h1>
<th:block th:if="${!recipeMap.empty}" th:each="company : ${recipeMap.keySet()}">
<h2 class="companyTabTitle" th:data-companyName="${company.companyName}"
th:text="${company.companyName}"></h2>
<table style="display:none" th:id="'recipes_' + ${company.companyName}"
th:if="${!recipeMap.get(company).empty}">
<tr>
<th th:text="#{recipe.color}"></th>
<th th:text="#{recipe.description}"></th>
<th th:text="#{recipe.sample}"></th>
<th></th>
</tr>
<tr class="recipeRow" th:each="recipe : ${recipeMap.get(company)}"
th:data-approbationDate="${recipe.approbationDate}">
<td class="descriptionCell" th:text="${recipe.recipeCode}"></td>
<td class="descriptionCell recipeDescription" th:text="${recipe.recipeDescription}"></td>
<td class="descriptionCell" th:text="'#' + ${recipe.sample}"></td>
<td>
<button class="editRecipe" th:data-code="${recipe.recipeCode}"
th:data-recipeID="${recipe.recipeID}" type="button" th:text="#{menu.edit}"></button>
</td>
</tr>
</table>
</th:block>
<b th:if="${recipeMap.empty}" class="error" th:text="#{recipe.error.anyFound}"></b>
<div class="content recipesContainer">
<div class="companyTab" th:if="${!recipeMap.empty}" th:each="company : ${recipeMap.keySet()}"
th:data-companyID="${company.companyID}">
<h2 class="companyTabTitle" th:data-companyName="${company.companyName}"
th:text="${company.companyName}"></h2>
<table style="display:none" th:id="'recipes_' + ${company.companyName}" class="recipesList"
th:if="${!recipeMap.get(company).empty}">
<tr>
<th th:text="#{recipe.color}"></th>
<th th:text="#{recipe.description}"></th>
<th th:text="#{recipe.sample}"></th>
<th></th>
</tr>
<tr class="recipeRow" th:each="recipe : ${recipeMap.get(company)}"
th:data-approbationDate="${recipe.approbationDate}" th:data-recipeID="${recipe.recipeID}">
<td class="descriptionCell" th:text="${recipe.recipeCode}"></td>
<td class="descriptionCell recipeDescription" th:text="${recipe.recipeDescription}"></td>
<td class="descriptionCell" th:text="'#' + ${recipe.sample}"></td>
<td>
<button class="editRecipe" th:data-code="${recipe.recipeCode}"
th:data-recipeID="${recipe.recipeID}" type="button" th:text="#{menu.edit}"></button>
</td>
</tr>
</table>
</div>
<b th:if="${recipeMap.empty}" class="error" th:text="#{recipe.error.anyFound}"></b>
</div>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
<script th:src="@{|${baseUrl}/js/recipeResearch.js|}"></script>
<script>
(() => {
document.querySelectorAll(".editRecipe").forEach((e) => {

View File

@ -1,111 +1,9 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{recipe.explore.title(${recipe.recipeCode})})"></th:block>
<th:block th:include="fragments.html :: head(#{recipe.explore.title(${recipe.recipeCode})}, null)"></th:block>
<style>
td, th {
width: 200px;
}
table {
margin: auto;
text-align: left;
border-collapse: separate;
}
section {
text-align: center;
}
p {
display: inline;
}
.mixes {
background-color: #f5f5f5;
border: 1px solid #7a7a7a;
border-collapse: collapse;
margin: 0;
min-width: 600px;
}
.mixes tr:nth-child(odd) {
background-color: #fafafa;
}
#steps li {
padding: 5px;
width: 400px;
}
#steps li:nth-child(even) {
background-color: #f5f5f5;
margin: 0;
}
#steps li:nth-child(odd) {
background-color: #fafafa;
}
.mixes td, .mixes th {
min-width: 100px;
}
.recipeLocation {
width: 55px;
border-color: white;
color: #7a7a7a;
}
.recipeLocation:hover {
border-color: #e6e6e6;
}
.recipeLocation:focus {
border-color: #7a7a7a;
color: black;
}
.notEnough td {
background-color: #ffb3b3;
}
.quantityColumn {
max-width: 100px;
}
.totalQuantityLabel {
padding-right: 10px;
text-align: right;
vertical-align: middle;
}
.inventoryQuantity {
display: none;
}
.inventoryQuantityColumn {
min-width: auto !important;
width: auto !important;
}
.unitsColumn {
max-width: 25px;
}
.calculationColumn {
min-width: 125px !important;
}
.calculation {
color: dimgrey;
}
.calculation span {
color: darkgreen;
}
</style>
<link rel="stylesheet" href="/css/explore.css"/>
</head>
<body>
@ -114,7 +12,6 @@
<!-- Corps de la page -->
<section>
<div th:include="fragments.html :: messages"></div>
<p class="success" th:text="${success}"></p>
<h1 th:text="#{recipe.explore.title(${recipe.recipeCode})}"></h1>
<button id="modifyRecipe" type="button" th:text="#{menu.edit}"></button>
@ -131,189 +28,183 @@
th:text="#{recipe.xlsVersion}">
</button>
<input id="recipeID" name="recipeID" th:value="${recipe.recipeID}" type="hidden"/>
<input id="recipeID"
name="recipeID"
type="hidden"
th:value="${recipe.recipeID}"/>
<table class="mainTable">
<tr>
<td style="vertical-align: top;">
<table>
<tr>
<td><b th:text="#{recipe.color} + ':'"></b></td>
<td th:text="${recipe.recipeCode}"></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b th:text="#{keyword.company} + ':'"></b></td>
<td th:text="${recipe.company.companyName}"></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b th:text="#{recipe.description} + ':'"></b></td>
<td th:text="${recipe.recipeDescription}"></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b th:text="#{recipe.sample} + ':'"></b></td>
<td th:text="${recipe.sample}"></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b th:text="#{recipe.approbationDate} + ':'"></b></td>
<td th:if="${recipe.approbationDate != ''}" th:text="${recipe.approbationDate}"></td>
<td th:if="${recipe.approbationDate == ''}" th:text="#{recipe.warning.notApproved.short}"></td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b th:text="#{recipe.remark} + ':'"></b></td>
<td th:text="${recipe.remark}">
</td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
<tr>
<td><b th:text="#{recipe.notice} + ':'"></b></td>
<td>
<textarea class="toSave" cols="20" id="note" name="note" rows="2"
th:text="${recipe.note}" style="margin: 0"></textarea>
</td>
</tr>
<tr>
<td></td>
<td>
<button id="formSubmit" type="button" th:text="#{keyword.save}"
style="margin-top: 20px"></button>
</td>
</tr>
</table>
</td>
<td>
<!-- Formulaires sans autocomplétion pour éviter que le navigateur cache les valeurs lors d'un rafraichissement et invalide les données dans data-quantityML -->
<form action="#" autocomplete="off">
<table style="border-spacing: 20px;">
<th:block th:each="mix : ${mixes}">
<tr>
<td><b th:text="${mix.mixType.typeName} + ':'"></b><br/><br/>
<label
th:for="'location' + ${mix.mixID}"
th:text="' ' + #{mix.location} + ': '">
<!-- Formulaires sans autocomplétion pour éviter que le navigateur cache des valeurs lors d'un rafraichissement-->
<form action="#" autocomplete="off">
<div class="content">
<div class="flexContent recipeDescription">
<div>
<b th:text="#{recipe.color} + ':'"></b>
<p th:text="${recipe.recipeCode}">
</div>
<div>
<b th:text="#{keyword.company} + ':'"></b>
<p th:text="${recipe.company.companyName}">
</div>
<div>
<b th:text="#{recipe.description} + ':'"></b>
<p th:text="${recipe.recipeDescription}">
</div>
<div>
<b th:text="#{recipe.sample} + ':'"></b>
<p th:text="${recipe.sample}">
</div>
<div>
<b th:text="#{recipe.approbationDate} + ':'"></b>
<p th:if="${recipe.approbationDate != ''}" th:text="${recipe.approbationDate}">
<p th:if="${recipe.approbationDate == ''}" th:text="#{recipe.warning.notApproved.short}">
</div>
<div th:if="${!recipe.remark.empty}">
<b th:text="#{recipe.remark} + ':'"></b>
<p th:text="${recipe.remark}">
</div>
<div>
<b th:text="#{recipe.notice} + ':'"></b>
<textarea class="rawInput toSave"
id="note"
cols="20"
rows="2"
name="note"
placeholder="N/A"
th:text="${recipe.note}" style="margin: 0"></textarea>
</div>
<button id="formSubmit"
type="button"
th:text="#{keyword.save}"
style="margin-top: 20px"></button>
</div>
<div th:if="${mixes.size() > 0}"
class="flexContent">
<th:block th:each="mix : ${mixes}">
<div class="mixContainer">
<div class="mixInfo">
<b th:text="${mix.mixType.typeName} + ':'"></b><br/><br/>
<label th:for="'location' + ${mix.mixID}"
th:text="' ' + #{mix.location} + ': '">
</label>
<input
class="recipeLocation toSave"
th:id="'location' + ${mix.mixID}"
name="location"
th:data-mixID="${mix.mixID}"
th:value="${mix.location}"
placeholder="N/A"
type="text"/>
</td>
<td>
<table class="mixes" th:id="'mix-' + ${mix.mixID}">
<tr>
<th th:text="#{keyword.material}"></th>
<th th:text="#{keyword.type}"></th>
<th style="min-width: auto !important;">
<button
type="button"
data-hidden="true"
onclick="hideQuantities(this)">
->
</button>
</th>
<!-- Changement des quantités -->
<th></th>
<th th:text="#{keyword.units}"></th>
<th th:text="#{keyword.calculation}"></th>
</tr>
<!-- Produits -->
<tr th:each="mixQuantity : ${mix.mixQuantities}"
th:with="material = ${mixQuantity.material}"
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 class="inventoryQuantityColumn">
<p class="inventoryQuantity"
th:data-quantityML="${mixQuantity.quantity}"
th:text="${mixQuantity.quantity}"></p>
</td>
<td class="quantityColumn">
<input th:if="${!material.isMixType()}" class="quantityCustomizer"
min="0.001" step="0.001"
th:data-materialID="${material.materialID}"
th:data-mixID="${mix.mixID}"
th:data-quantityML="${mixQuantity.quantity}"
th:data-usePercentages="${material.materialType.usePercentages}"
th:value="${mixQuantity.quantity}"
th:disabled="${material.materialType.usePercentages}"
type="number"/></td>
<td class="unitsColumn">
<p class="inventoryQuantityUnits"
th:unless="${material.materialType.usePercentages}">mL</p>
<p th:if="${material.materialType.usePercentages}">%</p>
</td>
<td class="calculationColumn">
<p class="calculation" th:data-mixID="${mix.mixID}"
th:data-materialID="${material.materialID}"></p>
</td>
</tr>
<tr>
<td class="totalQuantityLabel" colspan="3">
Total:
</td>
<td>
<input
class="totalQuantityCustomizer"
type="number"
min="0.001"
step="0.001"
th:data-mixID="${mix.mixID}"
/>
</td>
<td>
<p class="inventoryQuantityUnits">mL</p>
</td>
</tr>
</table>
</td>
<td>
<button class="useMixSubmit" type="button" th:text="#{keyword.use}"></button>
</td>
</tr>
<tr th:include="fragments.html :: separator"></tr>
</th:block>
</table>
</form>
</td>
</tr>
<tr>
<td>
<!-- Images -->
<th:block th:each="image : ${images}">
<img th:alt="#{recipe.image.corrupted}" th:src="@{|${baseUrl}/images/${image}|}" width="400px"/>
</label>
<input class="recipeLocation rawInput toSave"
th:id="'location' + ${mix.mixID}"
name="location"
th:data-mixID="${mix.mixID}"
th:value="${mix.location}"
placeholder="N/A"
type="text"/>
</div>
<div>
<table class="mix"
th:id="'mix-' + ${mix.mixID}">
<tr>
<th th:text="#{keyword.material}"></th>
<th th:text="#{keyword.type}"></th>
<th style="min-width: auto !important;">
<button
type="button"
data-hidden="true"
onclick="hideQuantities(this)">
->
</button>
</th>
<!-- Changement des quantités -->
<th></th>
<th th:text="#{keyword.units}"></th>
<th th:text="#{keyword.calculation}"></th>
</tr>
<!-- Produits -->
<tr th:each="mixQuantity : ${mix.mixQuantities}"
th:with="material = ${mixQuantity.material}"
th:id="'material-' + ${material.materialID}">
<td class="materialCodeColumn"
th:classappend="${material.isMixType()} ? '' : materialCode"
th:data-materialID="${material.materialID}"
th:text="${material.materialCode}"></td>
<td th:text="${material.materialType.materialTypeName}"></td>
<td class="inventoryQuantityColumn">
<p class="inventoryQuantity"
th:data-quantityML="${mixQuantity.quantity}"
th:text="${mixQuantity.quantity}"></p>
</td>
<td class="quantityColumn">
<input th:if="${!material.isMixType()}"
class="quantityCustomizer noStyle"
min="0.001" step="0.001"
th:data-materialID="${material.materialID}"
th:data-mixID="${mix.mixID}"
th:data-quantityML="${mixQuantity.quantity}"
th:data-usePercentages="${material.materialType.usePercentages}"
th:value="${mixQuantity.quantity}"
th:disabled="${material.materialType.usePercentages}"
type="number"/></td>
<td class="unitsColumn">
<p class="inventoryQuantityUnits"
th:unless="${material.materialType.usePercentages}">mL</p>
<p th:if="${material.materialType.usePercentages}">%</p>
</td>
<td class="calculationColumn">
<p class="calculation" th:data-mixID="${mix.mixID}"
th:data-materialID="${material.materialID}"></p>
</td>
</tr>
<tr>
<td class="totalQuantityLabel" colspan="3">
Total:
</td>
<td>
<input
class="totalQuantityCustomizer noStyle"
type="number"
min="0.001"
step="0.001"
th:data-mixID="${mix.mixID}"
/>
</td>
<td>
<p class="inventoryQuantityUnits">mL</p>
</td>
</tr>
</table>
</div>
<div class="mixActionsContainer">
<button class="useMixSubmit" type="button" th:text="#{keyword.use}"></button>
<button type="button" th:text="#{keyword.print}"></button>
</div>
</div>
</th:block>
</td>
<td id="steps">
</div>
<div th:with="steps = ${recipe.recipeSteps}"
th:if="${recipe.recipeSteps.size() > 0}"
class="flexContent steps">
<!-- Étapes -->
<div style="padding-left:100px">
<h3 th:text="#{recipe.steps}"></h3>
<div style="min-width: 300px">
<div class="subtitle">
<h3 th:text="#{recipe.steps}"></h3>
</div>
<ol>
<th:block th:each="step : ${recipe.recipeSteps}">
<li th:text="${step.stepMessage}"></li>
</th:block>
<li th:each="step : ${steps}"
th:text="${step.stepMessage}"></li>
</ol>
</div>
</td>
</tr>
</table>
</div>
<div th:if="${images.size() > 0}"
class="flexContent imagesContainer">
<!-- Images -->
<img th:each="image : ${images}"
th:alt="#{recipe.image.corrupted}"
th:src="@{|${baseUrl}/images/${image}|}"
class="recipeImage"
width="400px"
onmouseenter="startHovering()" onmouseleave="endHovering()"/>
</div>
</div>
</form>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
<script>
/*<![CDATA[*/
const quantityKeyword = "[[#{keyword.quantity}]]";
@ -450,7 +341,7 @@
});
window.addEventListener("load", () => {
document.querySelectorAll(".mixes").forEach(e => doCalculations(e));
document.querySelectorAll(".mix").forEach(e => doCalculations(e));
});
})();
@ -495,8 +386,8 @@
} else {
e.style.display = "inline";
e.parentElement.setAttribute("style", "min-width: 100px !important");
button.parentElement.setAttribute("style", "min-width: 100px !important");
e.parentElement.setAttribute("style", "min-width: 75px !important");
button.parentElement.setAttribute("style", "min-width: 75px !important");
button.innerText = quantityKeyword;
}
@ -566,10 +457,60 @@
e.dataset.totalQuantity = convertMlToUnit(e.dataset.quantity);
});
document.querySelectorAll(".mixes").forEach(e => doCalculations(e));
document.querySelectorAll(".mix").forEach(e => doCalculations(e));
}
startHovering = () => document.querySelector(".imagesContainer").classList.add("hovering");
endHovering = () => document.querySelector(".imagesContainer").classList.remove("hovering");
/*]]>*/
</script>
<script type="module">
import * as bpac from "/js/bpac.js";
const dataFolder = "/lbx";
const labelPath = dataFolder + "/Couleur.lbx";
console.log(labelPath);
const recipeCode = "[[${recipe.recipeCode}]]";
const banner = "[[${recipe.company.companyName}]]";
const baseMaterial = "[[${recipe.getBase().materialCode}]]";
async function print(mix) {
if (!document.querySelector(".bpac-extension-installed")) {
console.log("No extension");
return;
}
try {
const objDoc = bpac.IDocument;
await objDoc.Open("http://localhost:9090/lbx/Couleur.lbx");
const objColor = await objDoc.GetObject("color");
objColor.Text = recipeCode;
const objBanner = await objDoc.GetObject("banner");
objBanner.Text = banner;
const objBarcode = await objDoc.GetObject("color_barcode");
objBarcode.Text = recipeCode;
objDoc.StartPrint("", 0);
objDoc.PrintOut(1, 0);
objDoc.EndPrint();
objDoc.Close();
// } else {
// console.log("Can't open document");
// }
} catch (e) {
console.log(e);
}
}
window.addEventListener("load", () => {
//print();
});
</script>
</body>
</html>

View File

@ -1,42 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{recipe.delete.title})"></th:block>
<style>
h1 {
margin-bottom: 0;
}
th {
padding: 0 15px;
}
td {
padding: 0 20px;
}
.descriptionCell {
border-right-color: #e6e6e6;
border-right-style: solid;
border-right-width: 2px;
}
table {
border-collapse: separate;
border-spacing: 0 10px;
margin: 0 auto;
text-align: center;
}
section {
text-align: center;
}
.recipeDescription {
max-width: 400px;
}
</style>
<th:block th:include="fragments.html :: head(#{recipe.delete.title}, 'recipesList')"></th:block>
</head>
<body>
@ -44,43 +9,51 @@
<header th:include="fragments.html :: header"></header>
<!-- Corps de la page -->
<section>
<div id="researchBoxContainer">
<input id="researchBox" class="noStyle" type="text" th:placeholder="#{keyword.search}"
onkeyup="performSearch(this.value)"/>
</div>
<div th:include="fragments.html :: messages"></div>
<p class="success" th:if="${recipeCode != null}"
th:text="#{recipe.success.deleted(${recipeCode})}"></p>
<h1 th:text="#{recipe.delete.title}"></h1>
<form th:action="@{/recipe/remover/}" class="requireAuth-remover" method="POST">
<th:block th:if="${!recipeMap.empty}" th:each="company : ${recipeMap.keySet()}">
<h2 class="companyTabTitle" th:data-companyName="${company.companyName}"
th:text="${company.companyName}"></h2>
<table style="display:none" th:if="${!recipeMap.get(company).empty}"
th:id="'recipes_' + ${company.companyName}">
<tr>
<th th:text="#{recipe.color}"></th>
<th th:text="#{recipe.description}"></th>
<th th:text="#{recipe.sample}"></th>
<th></th>
</tr>
<tr th:each="recipe : ${recipeMap.get(company)}" class="recipeRow"
th:data-approbationDate="${recipe.approbationDate}">
<td class="descriptionCell" th:text="${recipe.recipeCode}"></td>
<td class="descriptionCell recipeDescription" th:text="${recipe.recipeDescription}"></td>
<td class="descriptionCell" th:text="'#' + ${recipe.sample}"></td>
<td>
<button class="remover" th:data-code="${recipe.recipeCode}"
th:data-entityID="${recipe.recipeID}" type="button"
th:text="#{keyword.delete}"></button>
</td>
</tr>
</table>
</th:block>
<b class="error" th:if="${recipeMap.empty}" th:text="#{recipe.error.anyFound}"></b>
</form>
<div class="content recipesContainer">
<form th:action="@{/recipe/remover/}" class="requireAuth-remover" method="POST">
<div class="companyTab" th:if="${!recipeMap.empty}" th:each="company : ${recipeMap.keySet()}"
th:data-companyID="${company.companyID}">
<h2 class="companyTabTitle" th:data-companyName="${company.companyName}"
th:text="${company.companyName}"></h2>
<table style="display:none" th:id="'recipes_' + ${company.companyName}" class="recipesList"
th:if="${!recipeMap.get(company).empty}">
<tr>
<th th:text="#{recipe.color}"></th>
<th th:text="#{recipe.description}"></th>
<th th:text="#{recipe.sample}"></th>
<th></th>
</tr>
<tr class="recipeRow" th:each="recipe : ${recipeMap.get(company)}"
th:data-approbationDate="${recipe.approbationDate}" th:data-recipeID="${recipe.recipeID}">
<td class="descriptionCell" th:text="${recipe.recipeCode}"></td>
<td class="descriptionCell recipeDescription" th:text="${recipe.recipeDescription}"></td>
<td class="descriptionCell" th:text="'#' + ${recipe.sample}"></td>
<td>
<button class="remover" th:data-code="${recipe.recipeCode}"
th:data-entityid="${recipe.recipeID}" type="button"
th:text="#{keyword.delete}"></button>
</td>
</tr>
</table>
</div>
</form>
<b th:if="${recipeMap.empty}" class="error" th:text="#{recipe.error.anyFound}"></b>
</div>
</section>
<!-- Fragment du pied de page -->
<footer th:include="fragments.html :: footer(null, true)"></footer>
<script th:src="@{|${baseUrl}/js/recipeResearch.js|}"></script>
</body>
</html>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="fr" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:include="fragments.html :: head(#{keyword.updates})"></th:block>
<th:block th:include="fragments.html :: head(#{keyword.updates}, null)"></th:block>
<style>
#markdown {

View File

@ -1,10 +1,18 @@
# v1.2.0 (Imprimante P-touch)
### Corrections
* +++ Correction d'un bug qui empêchait la suppression des mélanges.
* Correction d'un bug qui empêchait la suppression des mélanges.
* Correction d'un bug qui empêche les boutons supprimer de fonctionner.
* Correction d'un bug qui permettait d'envoyer les formulaires demandant des mots de passe sans donner un mot de passe valide.
* Correction d'une désynchronisation entre le nom des mélanges et leurs produits internes.
* Amélioration du style du site.
* Transition des modèles vers Lombok.
### Ajouts
* +++ Ajout du support pour l'imprimante P-touch
* +++ Ajout du support pour l'imprimante P-touch.
* Les produits dans l'inventaire sont maintenant ordonnés alphabétiquement.
* Changement de l'ordre d'un mélange.
* Ajout d'un type de produit aux mélanges.
* Ajout de jQuery, début de la transition.
# v1.1.3
### Corrections