Add configuration pages for recipes

master
Julien Rosset 1 month ago
parent a278133eeb
commit 81bf848698

@ -0,0 +1,31 @@
import 'jqueryLocal';
$(function () {
const machineExtraInfo1 = $('#recipe_edit_form_machineExtraInfo1');
const machineExtraInfo1_div = machineExtraInfo1.parents('div.row').first();
const machineExtraInfo1_label = machineExtraInfo1_div.find('label[for="' + machineExtraInfo1.attr('id') + '"]')
const machineExtraInfo2 = $('#recipe_edit_form_machineExtraInfo2');
const machineExtraInfo2_div = machineExtraInfo2.parents('div.row').first();
const machineExtraInfo2_label = machineExtraInfo2_div.find('label[for="' + machineExtraInfo2.attr('id') + '"]')
$('#recipe_edit_form_machine')
.on('change', function () {
const machine = $(this)
.find(':selected')
.dataDefault(
'machine',
{
labelExtraInfo1: null,
labelExtraInfo2: null,
}
);
machineExtraInfo1_div.toggleClass('d-none', machine.labelExtraInfo1 === null);
machineExtraInfo1_label.text(machine.labelExtraInfo1);
machineExtraInfo2_div.toggleClass('d-none', machine.labelExtraInfo2 === null);
machineExtraInfo2_label.text(machine.labelExtraInfo2);
})
.trigger('change');
});

@ -20,6 +20,10 @@ return [
'path' => './assets/datatables2.js',
'entrypoint' => true,
],
'config_recipe_edit' => [
'path' => './assets/config_recipe_edit.js',
'entrypoint' => true,
],
'modalDynamic' => [
'path' => './assets/modalDynamic.js',
],

@ -0,0 +1,101 @@
<?php
namespace App\Controller\Config;
use App\Entity\Recipe;
use App\Form\Config\RecipeEditForm;
use App\Misc\FlashType;
use App\Repository\RecipeRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
/**
* Controller for the configuration pages of material
*/
#[Route('/Config/Recipe')]
#[IsGranted('IS_AUTHENTICATED')]
class RecipeController extends AbstractController {
/**
* @var EntityManagerInterface The entity manager
*/
private readonly EntityManagerInterface $entityManager;
/**
* @var RecipeRepository The recipe repository
*/
private readonly RecipeRepository $recipeRepository;
/**
* Initialization
*
* @param EntityManagerInterface $entityManager The entity manager
* @param RecipeRepository $recipeRepository The material repository
*/
public function __construct (EntityManagerInterface $entityManager, RecipeRepository $recipeRepository) {
$this->entityManager = $entityManager;
$this->recipeRepository = $recipeRepository;
}
/**
* List of materials
*
* @return Response The response
*/
#[Route('/', name: 'config_recipe_list', alias: 'config_recipe')]
public function list (): Response {
return $this->render(
'Config/Recipe/List.html.twig',
[
'recipes' => $this->recipeRepository->findAll(),
]
);
}
/**
* Edit/Create a recipe
*
* @param Request $request The request
* @param Recipe|null $recipe The recipe to edit
*
* @return Response The response
*/
#[Route('/Create', name: 'config_recipe_create')]
#[Route('/Edit-{id}', name: 'config_recipe_edit')]
public function edit (Request $request, ?Recipe $recipe = null): Response {
$recipe ??= new Recipe();
$form = $this->createForm(RecipeEditForm::class, $recipe);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->entityManager->persist($recipe);
$this->entityManager->flush();
$this->addFlash(FlashType::SUCCESS, 'La recette a bien été enregistré.');
return $this->redirectToRoute('config_recipe_list');
}
return $this->render('Config/Recipe/Edit.html.twig', [
'recipe' => $recipe,
'form' => $form->createView(),
]);
}
/**
* Delete a recipe
*
* @param Recipe $recipe The recipe to delete
*
* @return Response The response
*/
#[Route('/Delete-{id}', name: 'config_recipe_delete')]
public function delete (Recipe $recipe): Response {
$this->entityManager->remove($recipe);
$this->entityManager->flush();
$this->addFlash(FlashType::SUCCESS, 'La recette a bien été supprimée.');
return $this->redirectToRoute('config_recipe_list');
}
}

@ -0,0 +1,44 @@
<?php
namespace App\Form\Config;
use App\Entity\InputRecipeMaterial;
use App\Repository\MaterialRepository;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* The form for an input material of a recipe
*/
class InputRecipeMaterialForm extends AbstractType {
/**
* @inheritDoc
*/
public function configureOptions (OptionsResolver $resolver): void {
$resolver->setDefaults(
[
'data_class' => InputRecipeMaterial::class,
]
);
}
/**
* @inheritDoc
*/
public function buildForm (FormBuilderInterface $builder, array $options): void {
$builder
->add('material', null, [
'label' => 'Matériau',
//'query_builder' => function (MaterialRepository $materialRepository): QueryBuilder {
// return $materialRepository->createQueryBuilder('m')
// ->orderBy('m.name');
//},
])
->add('consumedQuantity', null, [
'label' => 'Quantité consommée',
]);
}
}

@ -0,0 +1,37 @@
<?php
namespace App\Form\Config;
use App\Entity\OutputRecipeMaterial;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* The form for an output material of a recipe
*/
class OutputRecipeMaterialForm extends AbstractType {
/**
* @inheritDoc
*/
public function configureOptions (OptionsResolver $resolver): void {
$resolver->setDefaults(
[
'data_class' => OutputRecipeMaterial::class,
]
);
}
/**
* @inheritDoc
*/
public function buildForm (FormBuilderInterface $builder, array $options): void {
$builder
->add('material', null, [
'label' => 'Matériau',
])
->add('producedQuantity', null, [
'label' => 'Quantité produite',
]);
}
}

@ -0,0 +1,86 @@
<?php
namespace App\Form\Config;
use App\Entity\Machine;
use App\Entity\Recipe;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* The form for editing a recipe
*/
class RecipeEditForm extends AbstractType {
/**
* @inheritDoc
*/
public function configureOptions (OptionsResolver $resolver): void {
$resolver->setDefaults(
[
'data_class' => Recipe::class,
]
);
}
/**
* @inheritDoc
*/
public function buildForm (FormBuilderInterface $builder, array $options): void {
$builder
->add('machine', null, [
'label' => 'Machine',
'choice_attr' => function (Machine $machine, string $index, string $machineId): array {
return [
'data-machine' => json_encode([
'labelExtraInfo1' => $machine->getLabelExtraInfo1(),
'labelExtraInfo2' => $machine->getLabelExtraInfo2(),
]),
];
},
])
->add('machineExtraInfo1', TextType::class, [
'label' => 'Information complémentaire n° 1',
'required' => false,
'row_attr' => [
'class' => 'd-none mb-3',
],
])
->add('machineExtraInfo2', null, [
'label' => 'Information complémentaire n° 2',
'required' => false,
'row_attr' => [
'class' => 'd-none mb-3',
],
])
->add('craftingTime', null, [
'label' => 'Temps de production',
])
->add('consumedMaterials', CollectionType::class, [
'label' => 'Matériaux consommés',
'entry_type' => InputRecipeMaterialForm::class,
'entry_options' => [
'label' => false,
],
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
])
->add('producedMaterials', CollectionType::class, [
'label' => 'Matériaux produits',
'entry_type' => OutputRecipeMaterialForm::class,
'entry_options' => [
'label' => false,
],
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
])
->add('submit', SubmitType::class, [
'label' => 'Enregistrer',
]);
}
}

@ -0,0 +1,20 @@
{% extends 'base.html.twig' %}
{% block title %}{% if recipe.id is null %}Création {% else %}Modification{% endif %} recette - {{ parent() }}{% endblock %}
{% block mainContent %}
<h1>
{% if recipe.id is null %}
Création nouvelle recette
{% else %}
Modification d'une recette
{% endif %}
</h1>
{{ form(form) }}
<a href="{{ path('config_recipe_list') }}" class="btn btn-danger">Annuler</a>
{% endblock %}
{% block importmap %}
{{ importmap(['app', 'config_recipe_edit']) }}
{% endblock %}

@ -0,0 +1,95 @@
{% extends '/base.html.twig' %}
{% block title %}Liste des recettes - {{ parent() }}{% endblock %}
{% block importmap %}{{ importmap(['app', 'datatables2']) }}{% endblock %}
{% block mainContent %}
<h1>Liste des recettes</h1>
<div class="d-flex">
<div class="table-responsive mnw-25">
<table class="table table-sm table-striped table-hover table-bordered table-datatable2 align-middle">
<thead>
<tr>
<th scope="col" data-sort-onLoad="1" class="align-middle">Matériaux produits</th>
<th scope="col" class="align-middle">Machine</th>
<th scope="col" class="align-middle">Matériaux consommés</th>
<th scope="col" data-sort="false" class="fit-content align-middle">
<a href="{{ path('config_recipe_create') }}" class="btn btn-primary" data-bs-toggle="tooltip" data-bs-title="Ajouter">
<i class="fa-solid fa-square-plus"></i>
</a>
</th>
</tr>
</thead>
<tbody>
{% for recipe in recipes %}
{% set machine = recipe.machine %}
{% set firstExtraInfo = true %}
<tr>
<td>
{% for producedMaterial in recipe.producedMaterials %}
{% if not loop.first %}<br>{% endif %}{{ producedMaterial }}
{% endfor %}
</td>
<td
{% if machine.labelExtraInfo1 is not null or machine.labelExtraInfo2 is not null %}
data-bs-toggle="tooltip"
data-bs-html="true"
data-bs-title="
{% for extraInfo in 1..2 %}
{% if machine.('labelExtraInfo' ~ extraInfo) is not null %}
{% if firstExtraInfo %}
{% set firstExtraInfo = false %}
{% else %}
<br>
{% endif %}
{{ machine.('labelExtraInfo' ~ extraInfo) }} = {{ recipe.('machineExtraInfo' ~ extraInfo) }}
{% endif %}
{% endfor %}
"
{% endif %}
>
{{ machine }}
</td>
<td>
{% for consumedMaterial in recipe.consumedMaterials %}
{% if not loop.first %}<br>{% endif %}{{ consumedMaterial }}
{% endfor %}
</td>
<td class="fit-content">
<a href="{{ path('config_recipe_edit', {id: recipe.id}) }}"
class="text-primary me-2"
data-bs-toggle="tooltip"
data-bs-title="Éditer"
><i class="fa-solid fa-pen"></i></a>
<a href="#" class="text-danger" id="btDelete" data-bs-toggle="tooltip" data-bs-title="Supprimer">
<span data-bs-toggle="modal"
data-bs-target="#deleteConfirmation"
data-modal-dynamic-link-url="{{ path('config_recipe_delete', {id: recipe.id}) }}"
><i class="fa-solid fa-xmark"></i></span>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="modal modal-dynamic fade" id="deleteConfirmation" tabindex="-1" aria-label="btDelete" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">Suppression</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Êtes-vous sûr de vouloir supprimer ce matériau ?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Non</button>
<a href="#" class="modal-confirm-link btn btn-danger">Oui</a>
</div>
</div>
</div>
</div>
{% endblock %}

@ -19,9 +19,10 @@
Configuration
</a>
<ul class="dropdown-menu" aria-labelledby="dropdown-config">
<li><a href="{{ path('config_materialType_list') }}" class="dropdown-item">Types de matériaux</a></li>
<li><a href="{{ path('config_machine_list') }}" class="dropdown-item">Machines</a></li>
<li><a href="{{ path('config_recipe_list') }}" class="dropdown-item">Recettes</a></li>
<li><a href="{{ path('config_material_list') }}" class="dropdown-item">Matériaux</a></li>
<li><a href="{{ path('config_machine_list') }}" class="dropdown-item">Machines</a></li>
<li><a href="{{ path('config_materialType_list') }}" class="dropdown-item">Types de matériaux</a></li>
</ul>
</li>
</ul>

Loading…
Cancel
Save