diff --git a/assets/admin.js b/assets/admin.js index 3106034..07304ab 100644 --- a/assets/admin.js +++ b/assets/admin.js @@ -14,6 +14,7 @@ import PlaningLogestics from "./libs/PlaningLogestics.js"; import {SortableReorder} from "./libs/SortableReorder.js"; import { StripeCommissionCalculator } from "./libs/StripeCommissionCalculator.js"; import { ProductAddOption } from "./libs/ProductAddOption.js"; +import { TemplateApply } from "./libs/TemplateApply.js"; import { LeafletMap } from "./tools/LeafletMap.js"; // --- CONFIGURATION SENTRY --- @@ -45,6 +46,7 @@ const registerCustomElements = () => { { name: 'crm-editor', class: CrmEditor, extends: 'textarea' }, { name: 'stripe-commission-calculator', class: StripeCommissionCalculator, extends: 'div' }, { name: 'product-add-option', class: ProductAddOption, extends: 'button' }, + { name: 'template-apply', class: TemplateApply, extends: 'button' }, { name: 'leaflet-map', class: LeafletMap } ]; diff --git a/assets/libs/TemplateApply.js b/assets/libs/TemplateApply.js new file mode 100644 index 0000000..bf4fefa --- /dev/null +++ b/assets/libs/TemplateApply.js @@ -0,0 +1,26 @@ +export class TemplateApply extends HTMLButtonElement { + constructor() { + super(); + this.addEventListener('click', this.apply.bind(this)); + } + + apply() { + const selectorId = this.getAttribute('data-selector'); + const urlPattern = this.getAttribute('data-url-pattern'); + const selector = document.querySelector(selectorId); + + if (!selector) return; + + const templateId = selector.value; + if (!templateId) return; + + if (!confirm('Appliquer ce modèle ? Cela ajoutera les points de contrôle au produit.')) return; + + const form = document.createElement('form'); + form.method = 'POST'; + form.action = urlPattern.replace('TEMPLATE_ID', templateId); + + document.body.appendChild(form); + form.submit(); + } +} diff --git a/migrations/Version20260206170047.php b/migrations/Version20260206170047.php new file mode 100644 index 0000000..65ba669 --- /dev/null +++ b/migrations/Version20260206170047.php @@ -0,0 +1,69 @@ +addSql('CREATE TABLE template_point_controle (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, name VARCHAR(255) NOT NULL, points JSON NOT NULL, PRIMARY KEY (id))'); + $this->addSql('ALTER INDEX idx_9591436be30da2f7 RENAME TO IDX_7268396CBE3DB2B7'); + $this->addSql('ALTER INDEX idx_8b27c52bbe30da2f7 RENAME TO IDX_8B27C52BBE3DB2B7'); + $this->addSql('COMMENT ON COLUMN etat_lieux.updated_at IS \'\''); + $this->addSql('ALTER INDEX idx_d71603599b6b5fba RENAME TO IDX_D8D384179B6B5FBA'); + $this->addSql('ALTER TABLE etat_lieux_comment ALTER id DROP DEFAULT'); + $this->addSql('ALTER TABLE etat_lieux_comment ALTER id ADD GENERATED BY DEFAULT AS IDENTITY'); + $this->addSql('COMMENT ON COLUMN etat_lieux_comment.created_at IS \'\''); + $this->addSql('ALTER INDEX idx_etat_lieux_comment RENAME TO IDX_C7341FDB3F1DAE3C'); + $this->addSql('ALTER TABLE etat_lieux_file ALTER id DROP DEFAULT'); + $this->addSql('ALTER TABLE etat_lieux_file ALTER id ADD GENERATED BY DEFAULT AS IDENTITY'); + $this->addSql('COMMENT ON COLUMN etat_lieux_file.created_at IS \'\''); + $this->addSql('ALTER INDEX idx_5f7e1c87d6f39243 RENAME TO IDX_A76FE88B3F1DAE3C'); + $this->addSql('ALTER TABLE etat_lieux_point_control ALTER id DROP DEFAULT'); + $this->addSql('ALTER TABLE etat_lieux_point_control ALTER id ADD GENERATED BY DEFAULT AS IDENTITY'); + $this->addSql('ALTER INDEX idx_etat_lieux_point_control RENAME TO IDX_84DA2663F1DAE3C'); + $this->addSql('ALTER INDEX idx_88755657be30da2f7 RENAME TO IDX_263E7C9FBE3DB2B7'); + $this->addSql('ALTER TABLE product_point_controll ALTER id DROP DEFAULT'); + $this->addSql('ALTER TABLE product_point_controll ALTER id ADD GENERATED BY DEFAULT AS IDENTITY'); + $this->addSql('ALTER INDEX idx_9e3c8f8f4584665a RENAME TO IDX_B6E9A5844584665A'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE template_point_controle'); + $this->addSql('ALTER INDEX idx_7268396cbe3db2b7 RENAME TO idx_9591436be30da2f7'); + $this->addSql('ALTER INDEX idx_8b27c52bbe3db2b7 RENAME TO idx_8b27c52bbe30da2f7'); + $this->addSql('COMMENT ON COLUMN etat_lieux.updated_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER INDEX idx_d8d384179b6b5fba RENAME TO idx_d71603599b6b5fba'); + $this->addSql('ALTER TABLE etat_lieux_comment ALTER id SET DEFAULT nextval(\'etat_lieux_comment_id_seq\'::regclass)'); + $this->addSql('ALTER TABLE etat_lieux_comment ALTER id DROP IDENTITY'); + $this->addSql('COMMENT ON COLUMN etat_lieux_comment.created_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER INDEX idx_c7341fdb3f1dae3c RENAME TO idx_etat_lieux_comment'); + $this->addSql('ALTER TABLE etat_lieux_file ALTER id SET DEFAULT nextval(\'etat_lieux_file_id_seq\'::regclass)'); + $this->addSql('ALTER TABLE etat_lieux_file ALTER id DROP IDENTITY'); + $this->addSql('COMMENT ON COLUMN etat_lieux_file.created_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER INDEX idx_a76fe88b3f1dae3c RENAME TO idx_5f7e1c87d6f39243'); + $this->addSql('ALTER TABLE etat_lieux_point_control ALTER id SET DEFAULT nextval(\'etat_lieux_point_control_id_seq\'::regclass)'); + $this->addSql('ALTER TABLE etat_lieux_point_control ALTER id DROP IDENTITY'); + $this->addSql('ALTER INDEX idx_84da2663f1dae3c RENAME TO idx_etat_lieux_point_control'); + $this->addSql('ALTER INDEX idx_263e7c9fbe3db2b7 RENAME TO idx_88755657be30da2f7'); + $this->addSql('ALTER TABLE product_point_controll ALTER id SET DEFAULT nextval(\'product_point_controll_id_seq\'::regclass)'); + $this->addSql('ALTER TABLE product_point_controll ALTER id DROP IDENTITY'); + $this->addSql('ALTER INDEX idx_b6e9a5844584665a RENAME TO idx_9e3c8f8f4584665a'); + } +} diff --git a/src/Controller/Dashboard/CustomerController.php b/src/Controller/Dashboard/CustomerController.php index 2bd3b16..f8cd35f 100644 --- a/src/Controller/Dashboard/CustomerController.php +++ b/src/Controller/Dashboard/CustomerController.php @@ -92,6 +92,7 @@ class CustomerController extends AbstractController } catch (\Exception $e) { $this->addFlash('warning', 'Erreur création Stripe : ' . $e->getMessage()); } + $this->em->flush(); $this->appLogger->record('CREATE', sprintf( 'Nouveau client créé : %s %s (Type: %s)', diff --git a/src/Controller/Dashboard/ProductController.php b/src/Controller/Dashboard/ProductController.php index db097a3..5691eb4 100644 --- a/src/Controller/Dashboard/ProductController.php +++ b/src/Controller/Dashboard/ProductController.php @@ -121,6 +121,7 @@ class ProductController extends AbstractController $em->flush(); $stripe->createProduct($product); + $em->flush(); $logger->record('CREATE', "Nouveau produit : [{$product->getRef()}] {$product->getName()}"); $bus->dispatch(new DumpSitemapMessage()); @@ -132,7 +133,7 @@ class ProductController extends AbstractController } #[Route(path: '/crm/products/edit/{id}', name: 'app_crm_product_edit', methods: ['GET', 'POST'])] - public function productEdit(Product $product, EntityManagerInterface $em, AppLogger $logger, Request $request, Client $stripe, \App\Repository\ProductBlockedRepository $blockedRepo, \App\Repository\ProductReserveRepository $reserveRepo, OptionsRepository $optionsRepo): Response + public function productEdit(Product $product, EntityManagerInterface $em, AppLogger $logger, Request $request, Client $stripe, \App\Repository\ProductBlockedRepository $blockedRepo, \App\Repository\ProductReserveRepository $reserveRepo, OptionsRepository $optionsRepo, \App\Repository\TemplatePointControleRepository $templateRepo): Response { // 0. Toggle Publish if ($request->query->get('act') === 'togglePublish') { @@ -346,7 +347,8 @@ class ProductController extends AbstractController 'formBlocked' => $formBlocked->createView(), 'product' => $product, 'is_edit' => true, - 'optionsList' => $optionsRepo->findBy([], ['name' => 'ASC']) + 'optionsList' => $optionsRepo->findBy([], ['name' => 'ASC']), + 'templates' => $templateRepo->findAll() ]); } #[Route(path: '/crm/products/delete/{id}', name: 'app_crm_product_delete', methods: ['POST'])] @@ -378,6 +380,7 @@ class ProductController extends AbstractController $em->flush(); $stripe->createOptions($option); + $em->flush(); $logger->record('CREATE', "Nouvelle option : {$option->getName()}"); $this->addFlash('success', "L'option {$option->getName()} a été ajoutée."); diff --git a/src/Controller/Dashboard/TemplatePointControleController.php b/src/Controller/Dashboard/TemplatePointControleController.php new file mode 100644 index 0000000..8fbce3d --- /dev/null +++ b/src/Controller/Dashboard/TemplatePointControleController.php @@ -0,0 +1,111 @@ +render('dashboard/template_point_controle/index.twig', [ + 'templates' => $templatePointControleRepository->findAll(), + ]); + } + + #[Route('/new', name: 'app_template_point_controle_new', methods: ['GET', 'POST'])] + public function new(Request $request, EntityManagerInterface $entityManager): Response + { + $template = new TemplatePointControle(); + + if ($request->isMethod('POST')) { + $name = $request->request->get('name'); + $pointsStr = $request->request->get('points'); + + $points = array_filter(array_map('trim', explode(" +", $pointsStr))); + + $template->setName($name); + $template->setPoints(array_values($points)); + + $entityManager->persist($template); + $entityManager->flush(); + + return $this->redirectToRoute('app_template_point_controle_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->render('dashboard/template_point_controle/new.twig', [ + 'template' => $template, + ]); + } + + #[Route('/{id}/edit', name: 'app_template_point_controle_edit', methods: ['GET', 'POST'])] + public function edit(Request $request, TemplatePointControle $template, EntityManagerInterface $entityManager): Response + { + if ($request->isMethod('POST')) { + $name = $request->request->get('name'); + $pointsStr = $request->request->get('points'); + + $points = array_filter(array_map('trim', explode(" +", $pointsStr))); + + $template->setName($name); + $template->setPoints(array_values($points)); + + $entityManager->flush(); + + return $this->redirectToRoute('app_template_point_controle_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->render('dashboard/template_point_controle/edit.twig', [ + 'template' => $template, + ]); + } + + #[Route('/{id}', name: 'app_template_point_controle_delete', methods: ['POST'])] + public function delete(Request $request, TemplatePointControle $template, EntityManagerInterface $entityManager): Response + { + if ($this->isCsrfTokenValid('delete'.$template->getId(), $request->request->get('_token'))) { + $entityManager->remove($template); + $entityManager->flush(); + } + + return $this->redirectToRoute('app_template_point_controle_index', [], Response::HTTP_SEE_OTHER); + } + + #[Route('/apply/{id}/product/{productId}', name: 'app_template_point_controle_apply', methods: ['POST'])] + public function apply(TemplatePointControle $template, int $productId, EntityManagerInterface $entityManager): Response + { + $product = $entityManager->getRepository(Product::class)->find($productId); + + if (!$product) { + throw $this->createNotFoundException('Product not found'); + } + + foreach ($template->getPoints() as $pointName) { + $point = new ProductPointControll(); + $point->setName($pointName); + $point->setProduct($product); + $entityManager->persist($point); + } + + $entityManager->flush(); + + $this->addFlash('success', 'Template appliqué avec succès !'); + + return $this->redirectToRoute('app_crm_product_edit', ['id' => $product->getId()]); + } +} diff --git a/src/Entity/TemplatePointControle.php b/src/Entity/TemplatePointControle.php new file mode 100644 index 0000000..4b9e5c8 --- /dev/null +++ b/src/Entity/TemplatePointControle.php @@ -0,0 +1,51 @@ +id; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function getPoints(): array + { + return $this->points; + } + + public function setPoints(array $points): static + { + $this->points = $points; + + return $this; + } +} diff --git a/src/Repository/TemplatePointControleRepository.php b/src/Repository/TemplatePointControleRepository.php new file mode 100644 index 0000000..e54f76b --- /dev/null +++ b/src/Repository/TemplatePointControleRepository.php @@ -0,0 +1,18 @@ + + */ +class TemplatePointControleRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TemplatePointControle::class); + } +} diff --git a/templates/dashboard/base.twig b/templates/dashboard/base.twig index e16e53a..e7140da 100644 --- a/templates/dashboard/base.twig +++ b/templates/dashboard/base.twig @@ -14,7 +14,7 @@ {% if not is_online() %}