feat(EsyWebController): Ajoute création et affichage de sites internet
Ce commit implémente l'ajout et la visualisation des sites internet. Il inclut la gestion de formulaires, l'enregistrement en base de données, et l'affichage des détails.
This commit is contained in:
@@ -10,6 +10,7 @@ import {LockdownWall} from './class/LockdownWall'
|
||||
import {SecurityWall} from './class/SecurityWall'
|
||||
import {IpWall} from './class/IpWall'
|
||||
import {ConfirmModal} from './class/ConfirmModal'
|
||||
import {SearchCustomer} from './class/SearchCustomer'
|
||||
import preactCustomElement from './functions/preact'
|
||||
import * as Sentry from "@sentry/browser";
|
||||
|
||||
@@ -25,6 +26,7 @@ function script() {
|
||||
customElements.define('ip-wall',IpWall,{})
|
||||
customElements.define('order-ctrl',OrderCtrl,{extends:'div'})
|
||||
customElements.define("register-payment",RegisterPayment,{extends:'button'})
|
||||
customElements.define("search-customer",SearchCustomer,{extends:'select'})
|
||||
}
|
||||
|
||||
function full() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@import "tailwindcss";
|
||||
@import "editor/ckeditor5.css";
|
||||
@import "editor/tom-select.scss";
|
||||
@import url('https://fonts.googleapis.com/css2?family=Intel+One+Mono:ital,wght@0,300..700;1,300..700&display=swap');
|
||||
|
||||
h1,h2,h3,h4,h5,h6,
|
||||
@@ -440,3 +441,34 @@ confirm-modal{
|
||||
color: orange;
|
||||
font-weight: bolder;
|
||||
}
|
||||
.ts-control {
|
||||
padding: 0;
|
||||
}
|
||||
.ts-wrapper,
|
||||
.ts-control{
|
||||
border: unset;
|
||||
background: oklch(21% 0.034 264.665deg) !important;
|
||||
|
||||
.item {
|
||||
color: white;
|
||||
font-weight: bolder;
|
||||
}
|
||||
input {
|
||||
color: white;
|
||||
font-weight: bolder;
|
||||
}
|
||||
}
|
||||
.ts-dropdown {
|
||||
background: var(--color-gray-800) !important;
|
||||
border: unset !important;
|
||||
.option{
|
||||
&:hover {
|
||||
background: var(--color-gray-600) !important;
|
||||
}
|
||||
background: var(--color-gray-800) !important;
|
||||
color: white;
|
||||
&.selected {
|
||||
background: var(--color-gray-600) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
assets/class/SearchCustomer.js
Normal file
10
assets/class/SearchCustomer.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import TomSelect from "tom-select";
|
||||
|
||||
export class SearchCustomer extends HTMLSelectElement{
|
||||
connectedCallback() {
|
||||
let element = this;
|
||||
new TomSelect(element,{
|
||||
create: false
|
||||
});
|
||||
}
|
||||
}
|
||||
409
assets/editor/tom-select.scss
Normal file
409
assets/editor/tom-select.scss
Normal file
@@ -0,0 +1,409 @@
|
||||
/**
|
||||
* tom-select.css (v2.4.3)
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at:
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
||||
* ANY KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*
|
||||
*/
|
||||
.ts-control {
|
||||
border: 1px solid #d0d0d0;
|
||||
padding: 8px 8px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.full .ts-control {
|
||||
background-color: #fff;
|
||||
}
|
||||
.disabled .ts-control, .disabled .ts-control * {
|
||||
cursor: default !important;
|
||||
}
|
||||
.focus .ts-control {
|
||||
box-shadow: none;
|
||||
}
|
||||
.ts-control > * {
|
||||
vertical-align: baseline;
|
||||
display: inline-block;
|
||||
}
|
||||
.ts-wrapper.multi .ts-control > div {
|
||||
cursor: pointer;
|
||||
margin: 0 3px 3px 0;
|
||||
padding: 2px 6px;
|
||||
background: #f2f2f2;
|
||||
color: #303030;
|
||||
border: 0 solid #d0d0d0;
|
||||
}
|
||||
.ts-wrapper.multi .ts-control > div.active {
|
||||
background: #e8e8e8;
|
||||
color: #303030;
|
||||
border: 0 solid #cacaca;
|
||||
}
|
||||
.ts-wrapper.multi.disabled .ts-control > div, .ts-wrapper.multi.disabled .ts-control > div.active {
|
||||
color: rgb(124.5, 124.5, 124.5);
|
||||
background: white;
|
||||
border: 0 solid white;
|
||||
}
|
||||
.ts-control > input {
|
||||
flex: 1 1 auto;
|
||||
min-width: 7rem;
|
||||
display: inline-block !important;
|
||||
padding: 0 !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
text-indent: 0 !important;
|
||||
border: 0 none !important;
|
||||
background: none !important;
|
||||
line-height: inherit !important;
|
||||
-webkit-user-select: auto !important;
|
||||
-moz-user-select: auto !important;
|
||||
-ms-user-select: auto !important;
|
||||
user-select: auto !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.ts-control > input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.ts-control > input:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.has-items .ts-control > input {
|
||||
margin: 0 4px !important;
|
||||
}
|
||||
.ts-control.rtl {
|
||||
text-align: right;
|
||||
}
|
||||
.ts-control.rtl.single .ts-control:after {
|
||||
left: 15px;
|
||||
right: auto;
|
||||
}
|
||||
.ts-control.rtl .ts-control > input {
|
||||
margin: 0 4px 0 -2px !important;
|
||||
}
|
||||
.disabled .ts-control {
|
||||
opacity: 0.5;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.input-hidden .ts-control > input {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
}
|
||||
|
||||
.ts-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
border: 1px solid #d0d0d0;
|
||||
background: var(--color-gray-600) !important;
|
||||
margin: 0.25rem 0 0;
|
||||
border-top: 0 none;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
.ts-dropdown [data-selectable] {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ts-dropdown [data-selectable] .highlight {
|
||||
background: rgba(125, 168, 208, 0.2);
|
||||
border-radius: 1px;
|
||||
}
|
||||
.ts-dropdown .option,
|
||||
.ts-dropdown .optgroup-header,
|
||||
.ts-dropdown .no-results,
|
||||
.ts-dropdown .create {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
.ts-dropdown .option, .ts-dropdown [data-disabled], .ts-dropdown [data-disabled] [data-selectable].option {
|
||||
cursor: inherit;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.ts-dropdown [data-selectable].option {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ts-dropdown .optgroup:first-child .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.ts-dropdown .optgroup-header {
|
||||
color: #303030;
|
||||
background: #fff;
|
||||
cursor: default;
|
||||
}
|
||||
.ts-dropdown .active {
|
||||
background-color: #f5fafd;
|
||||
color: #495c68;
|
||||
}
|
||||
.ts-dropdown .active.create {
|
||||
color: #495c68;
|
||||
}
|
||||
.ts-dropdown .create {
|
||||
color: rgba(48, 48, 48, 0.5);
|
||||
}
|
||||
.ts-dropdown .spinner {
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
margin: 5px 8px;
|
||||
}
|
||||
.ts-dropdown .spinner::after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 3px;
|
||||
border-radius: 50%;
|
||||
border: 5px solid #d0d0d0;
|
||||
border-color: #d0d0d0 transparent #d0d0d0 transparent;
|
||||
animation: lds-dual-ring 1.2s linear infinite;
|
||||
}
|
||||
@keyframes lds-dual-ring {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.ts-dropdown-content {
|
||||
overflow: hidden auto;
|
||||
max-height: 200px;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.ts-wrapper.plugin-drag_drop .ts-dragging {
|
||||
color: transparent !important;
|
||||
}
|
||||
.ts-wrapper.plugin-drag_drop .ts-dragging > * {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
.plugin-checkbox_options:not(.rtl) .option input {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.plugin-checkbox_options.rtl .option input {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* stylelint-disable function-name-case */
|
||||
.plugin-clear_button {
|
||||
--ts-pr-clear-button: 1em;
|
||||
}
|
||||
.plugin-clear_button .clear-button {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
right: calc(8px - 6px);
|
||||
margin-right: 0 !important;
|
||||
background: transparent !important;
|
||||
transition: opacity 0.5s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.plugin-clear_button.form-select .clear-button, .plugin-clear_button.single .clear-button {
|
||||
right: max(var(--ts-pr-caret), 8px);
|
||||
}
|
||||
.plugin-clear_button.focus.has-items .clear-button, .plugin-clear_button:not(.disabled):hover.has-items .clear-button {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ts-wrapper .dropdown-header {
|
||||
position: relative;
|
||||
padding: 10px 8px;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
background: color-mix(#fff, #d0d0d0, 85%);
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
.ts-wrapper .dropdown-header-close {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
color: #303030;
|
||||
opacity: 0.4;
|
||||
margin-top: -12px;
|
||||
line-height: 20px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
.ts-wrapper .dropdown-header-close:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.plugin-dropdown_input.focus.dropdown-active .ts-control {
|
||||
box-shadow: none;
|
||||
border: 1px solid #d0d0d0;
|
||||
}
|
||||
.plugin-dropdown_input .dropdown-input {
|
||||
border: 1px solid #d0d0d0;
|
||||
border-width: 0 0 1px;
|
||||
display: block;
|
||||
padding: 8px 8px;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
.plugin-dropdown_input .items-placeholder {
|
||||
border: 0 none !important;
|
||||
box-shadow: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
.plugin-dropdown_input.has-items .items-placeholder, .plugin-dropdown_input.dropdown-active .items-placeholder {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.ts-wrapper.plugin-input_autogrow.has-items .ts-control > input {
|
||||
min-width: 0;
|
||||
}
|
||||
.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control > input {
|
||||
flex: none;
|
||||
min-width: 4px;
|
||||
}
|
||||
.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control > input::-ms-input-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control > input::placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.ts-dropdown.plugin-optgroup_columns .ts-dropdown-content {
|
||||
display: flex;
|
||||
}
|
||||
.ts-dropdown.plugin-optgroup_columns .optgroup {
|
||||
border-right: 1px solid #f2f2f2;
|
||||
border-top: 0 none;
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
.ts-dropdown.plugin-optgroup_columns .optgroup:last-child {
|
||||
border-right: 0 none;
|
||||
}
|
||||
.ts-dropdown.plugin-optgroup_columns .optgroup::before {
|
||||
display: none;
|
||||
}
|
||||
.ts-dropdown.plugin-optgroup_columns .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
|
||||
.ts-wrapper.plugin-remove_button .item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button .item .remove {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 0 6px;
|
||||
border-radius: 0 2px 2px 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button .item .remove:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button.disabled .item .remove:hover {
|
||||
background: none;
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button .remove-single {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
font-size: 23px;
|
||||
}
|
||||
|
||||
.ts-wrapper.plugin-remove_button:not(.rtl) .item {
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove {
|
||||
border-left: 1px solid #d0d0d0;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button:not(.rtl) .item.active .remove {
|
||||
border-left-color: #cacaca;
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button:not(.rtl).disabled .item .remove {
|
||||
border-left-color: white;
|
||||
}
|
||||
|
||||
.ts-wrapper.plugin-remove_button.rtl .item {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button.rtl .item .remove {
|
||||
border-right: 1px solid #d0d0d0;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button.rtl .item.active .remove {
|
||||
border-right-color: #cacaca;
|
||||
}
|
||||
.ts-wrapper.plugin-remove_button.rtl.disabled .item .remove {
|
||||
border-right-color: white;
|
||||
}
|
||||
|
||||
:root {
|
||||
--ts-pr-clear-button: 0px;
|
||||
--ts-pr-caret: 0px;
|
||||
--ts-pr-min: .75rem;
|
||||
}
|
||||
|
||||
.ts-wrapper.single .ts-control, .ts-wrapper.single .ts-control input {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ts-control:not(.rtl) {
|
||||
padding-right: max(var(--ts-pr-min), var(--ts-pr-clear-button) + var(--ts-pr-caret)) !important;
|
||||
}
|
||||
|
||||
.ts-control.rtl {
|
||||
padding-left: max(var(--ts-pr-min), var(--ts-pr-clear-button) + var(--ts-pr-caret)) !important;
|
||||
}
|
||||
|
||||
.ts-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ts-dropdown,
|
||||
.ts-control,
|
||||
.ts-control input {
|
||||
color: #303030;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.ts-control,
|
||||
.ts-wrapper.single.input-active .ts-control {
|
||||
background: var(--color-gray-600);
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.ts-hidden-accessible {
|
||||
border: 0 !important;
|
||||
clip: rect(0 0 0 0) !important;
|
||||
-webkit-clip-path: inset(50%) !important;
|
||||
clip-path: inset(50%) !important;
|
||||
overflow: hidden !important;
|
||||
padding: 0 !important;
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
/*# sourceMappingURL=tom-select.css.map */
|
||||
36
migrations/Version20251017075007.php
Normal file
36
migrations/Version20251017075007.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20251017075007 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE website ADD revendeur_id INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE website ADD CONSTRAINT FK_476F5DE7F4218D56 FOREIGN KEY (revendeur_id) REFERENCES revendeur (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
|
||||
$this->addSql('CREATE INDEX IDX_476F5DE7F4218D56 ON website (revendeur_id)');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE SCHEMA public');
|
||||
$this->addSql('ALTER TABLE website DROP CONSTRAINT FK_476F5DE7F4218D56');
|
||||
$this->addSql('DROP INDEX IDX_476F5DE7F4218D56');
|
||||
$this->addSql('ALTER TABLE website DROP revendeur_id');
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
"react-email-editor": "^1.7.11",
|
||||
"sortablejs": "^1.15.6",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"tom-select": "^2.4.3",
|
||||
"vite-plugin-favicon": "^1.0.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ namespace App\Controller\ApiInterne\Intranet;
|
||||
|
||||
|
||||
use App\Entity\CustomerAdvertPayment;
|
||||
use App\Entity\CustomerDnsEmail;
|
||||
use App\Repository\CustomerAdvertPaymentRepository;
|
||||
use App\Repository\CustomerRepository;
|
||||
use App\Service\Echeance\EventEcheanceCreated;
|
||||
use App\Service\Logger\LoggerService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
@@ -23,6 +25,32 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class CustomerController extends AbstractController
|
||||
{
|
||||
|
||||
#[Route(path: '/api-interne/customer',name: 'api-interne-search',methods: ['POST','GET'])]
|
||||
public function searchCustomer(Request $request,CustomerRepository $customerRepository): Response
|
||||
{
|
||||
$request = $request->get('q',null);
|
||||
if(is_null($request)){
|
||||
return $this->json([
|
||||
'items' => []
|
||||
]);
|
||||
}
|
||||
$list = $customerRepository->search($request);
|
||||
$items =[];
|
||||
|
||||
/** @var \App\Entity\Customer $item */
|
||||
foreach ($list as $item) {
|
||||
$items[] = [
|
||||
'id' => $item->getId(),
|
||||
'title' => $item->getRaisonSocial(),
|
||||
];
|
||||
}
|
||||
return $this->json([
|
||||
'items' => $items
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
#[Route(path: '/api-interne/intranet/customer/payment/cancel',name: 'api-interne-intranet-customer-payment-cancel')]
|
||||
public function customerPaymentCancel(EntityManagerInterface $entityManager,Request $request,CustomerAdvertPaymentRepository $customerAdvertPayment): Response
|
||||
{
|
||||
|
||||
@@ -7,8 +7,11 @@ use App\Form\Artemis\EsyWeb\WebsiteType;
|
||||
use App\Repository\EsyWeb\WebsiteRepository;
|
||||
use App\Repository\EsyWebTutoRepository;
|
||||
use App\Service\Logger\LoggerService;
|
||||
use App\Service\Website\EventCreatedWebsite;
|
||||
use Cocur\Slugify\Slugify;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
@@ -18,29 +21,55 @@ use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
|
||||
class EsyWebController extends AbstractController
|
||||
{
|
||||
#[Route(path: '/artemis/esyweb', name: 'artemis_esyweb', methods: ['GET', 'POST'])]
|
||||
public function tutos(LoggerService $loggerService,WebsiteRepository $websiteRepository)
|
||||
public function websites(LoggerService $loggerService,Request $request,EntityManagerInterface $entityManager,WebsiteRepository $websiteRepository)
|
||||
{
|
||||
$loggerService->log("VIEW","Affiche la page de site internet",$this->getUser());
|
||||
|
||||
if($request->query->has('idValidate')) {
|
||||
$website = $websiteRepository->find($request->query->get('idValidate'));
|
||||
$website->setState("validate");
|
||||
$entityManager->persist($website);
|
||||
$entityManager->flush();
|
||||
$loggerService->log("VALIDATE","Validation du site internet",$this->getUser());
|
||||
return $this->redirectToRoute('artemis_esyweb');
|
||||
}
|
||||
return $this->render('artemis/esyweb/website.twig', [
|
||||
'websites' => $websiteRepository->findAll(),
|
||||
]);
|
||||
}
|
||||
#[Route(path: '/artemis/esyweb/{id}', name: 'artemis_esyweb_view', methods: ['GET', 'POST'])]
|
||||
public function websiteView(?Website $website,LoggerService $loggerService,Request $request,EntityManagerInterface $entityManager,WebsiteRepository $websiteRepository)
|
||||
{
|
||||
if(is_null($website)) {
|
||||
return $this->redirectToRoute('artemis_esyweb');
|
||||
}
|
||||
$loggerService->log("VIEW","Affiche la page de site internet - ".$website->getTitle(),$this->getUser());
|
||||
|
||||
return $this->render('artemis/esyweb/website_view.twig', [
|
||||
'current' => $request->get('current','main'),
|
||||
'website' => $website
|
||||
]);
|
||||
}
|
||||
#[Route(path: '/artemis/esyweb/add', name: 'artemis_esyweb_add', methods: ['GET', 'POST'])]
|
||||
public function websiteAdd(LoggerService $loggerService,Request $request)
|
||||
public function websiteAdd(LoggerService $loggerService,Request $request,EntityManagerInterface $entityManager,EventDispatcherInterface $eventDispatcher)
|
||||
{
|
||||
$loggerService->log("VIEW","Affiche la page de création de site internet",$this->getUser());
|
||||
|
||||
$webiste = new Website();
|
||||
$webiste->setState("created");
|
||||
$webiste->setUuid(Uuid::v4());
|
||||
$website = new Website();
|
||||
$website->setState("created");
|
||||
$website->setUuid(Uuid::v4());
|
||||
|
||||
$form = $this->createForm(WebsiteType::class,$webiste);
|
||||
$form = $this->createForm(WebsiteType::class,$website);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
|
||||
$slug = new Slugify();
|
||||
$website->setMainDns($slug->slugify($website->getTitle()).".esy-web.dev");
|
||||
$loggerService->log("CREATED","Création d'un site internet");
|
||||
$entityManager->persist($website);
|
||||
$entityManager->flush();
|
||||
$eventDispatcher->dispatch(new EventCreatedWebsite($website));
|
||||
return $this->redirectToRoute('artemis_esyweb');
|
||||
}
|
||||
return $this->render('artemis/esyweb/add.twig', [
|
||||
'form' => $form->createView(),
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Entity\EsyWeb;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\CustomerDns;
|
||||
use App\Entity\Revendeur;
|
||||
use App\Repository\EsyWeb\WebsiteRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
@@ -42,6 +43,9 @@ class Website
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $type = null;
|
||||
|
||||
#[ORM\ManyToOne(inversedBy: 'websites')]
|
||||
private ?Revendeur $revendeur = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ndd = new ArrayCollection();
|
||||
@@ -153,4 +157,16 @@ class Website
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRevendeur(): ?Revendeur
|
||||
{
|
||||
return $this->revendeur;
|
||||
}
|
||||
|
||||
public function setRevendeur(?Revendeur $revendeur): static
|
||||
{
|
||||
$this->revendeur = $revendeur;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Entity\EsyWeb\Website;
|
||||
use App\Repository\RevendeurRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: RevendeurRepository::class)]
|
||||
@@ -34,6 +37,17 @@ class Revendeur
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $parent = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, Website>
|
||||
*/
|
||||
#[ORM\OneToMany(targetEntity: Website::class, mappedBy: 'revendeur')]
|
||||
private Collection $websites;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->websites = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@@ -122,4 +136,34 @@ class Revendeur
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, Website>
|
||||
*/
|
||||
public function getWebsites(): Collection
|
||||
{
|
||||
return $this->websites;
|
||||
}
|
||||
|
||||
public function addWebsite(Website $website): static
|
||||
{
|
||||
if (!$this->websites->contains($website)) {
|
||||
$this->websites->add($website);
|
||||
$website->setRevendeur($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeWebsite(Website $website): static
|
||||
{
|
||||
if ($this->websites->removeElement($website)) {
|
||||
// set the owning side to null (unless already changed)
|
||||
if ($website->getRevendeur() === $this) {
|
||||
$website->setRevendeur(null);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
namespace App\Form\Artemis\EsyWeb;
|
||||
|
||||
use App\Entity\Customer;
|
||||
use App\Entity\EsyWeb\Website;
|
||||
use App\Entity\Revendeur;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
@@ -15,6 +19,7 @@ class WebsiteType extends AbstractType
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
|
||||
|
||||
$builder
|
||||
->add('type',ChoiceType::class,[
|
||||
'label' => 'Type de site internet',
|
||||
@@ -25,9 +30,37 @@ class WebsiteType extends AbstractType
|
||||
]
|
||||
])
|
||||
->add('title',TextType::class,[
|
||||
'label' => 'Titre',
|
||||
'label' => 'Nom du site internet',
|
||||
'required' => true
|
||||
])
|
||||
->add('customer',EntityType::class,[
|
||||
'label' => 'Client',
|
||||
'attr' =>[
|
||||
'is' => 'search-customer'
|
||||
],
|
||||
'class' => Customer::class,
|
||||
'query_builder' => function (EntityRepository $er) {
|
||||
return $er->createQueryBuilder('c')
|
||||
->orderBy('c.raisonSocial', 'ASC');
|
||||
},
|
||||
'choice_label' => function (Customer $category): string {
|
||||
return $category->getRaisonSocial()." - ".$category->mainContact()->getName()." ".$category->mainContact()->getSurname();
|
||||
}
|
||||
])
|
||||
->add('revendeur',EntityType::class,[
|
||||
'label' => 'Revendeur',
|
||||
'attr' =>[
|
||||
'is' => 'search-customer'
|
||||
],
|
||||
'class' => Revendeur::class,
|
||||
'query_builder' => function (EntityRepository $er) {
|
||||
return $er->createQueryBuilder('c')
|
||||
->orderBy('c.raisonSocial', 'ASC');
|
||||
},
|
||||
'choice_label' => function (Revendeur $category): string {
|
||||
return $category->getRaisonSocial()." - ".$category->getName()." ".$category->getSurname()." - ".$category->getEmail()." ".$category->getCode();
|
||||
}
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,6 @@ class AccountRepository extends ServiceEntityRepository implements PasswordUpgra
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -61,4 +61,20 @@ class CustomerRepository extends ServiceEntityRepository
|
||||
|
||||
return $qt->getQuery();
|
||||
}
|
||||
|
||||
public function search($search)
|
||||
{
|
||||
$qt = $this->createQueryBuilder('customer');
|
||||
$qt->innerJoin('customer.customerContacts','customerContacts');
|
||||
|
||||
$qt->andWhere('customer.raisonSocial LIKE :search')
|
||||
->orWhere('customer.siret LIKE :search')
|
||||
->orWhere('customerContacts.name LIKE :search')
|
||||
->orWhere('customerContacts.surname LIKE :search')
|
||||
->orWhere('customerContacts.email LIKE :search')
|
||||
->orWhere('customerContacts.phone LIKE :search')
|
||||
->setParameter('search','%'.$search.'%');
|
||||
|
||||
return $qt->getQuery()->getResult();
|
||||
}
|
||||
}
|
||||
|
||||
20
src/Service/Website/EventCreatedWebsite.php
Normal file
20
src/Service/Website/EventCreatedWebsite.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Website;
|
||||
|
||||
use App\Entity\EsyWeb\Website;
|
||||
|
||||
class EventCreatedWebsite
|
||||
{
|
||||
public function __construct(private readonly Website $website)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Website
|
||||
*/
|
||||
public function getWebsite(): Website
|
||||
{
|
||||
return $this->website;
|
||||
}
|
||||
}
|
||||
38
src/Service/Website/WebsiteCreatedSubscriber.php
Normal file
38
src/Service/Website/WebsiteCreatedSubscriber.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Website;
|
||||
|
||||
use App\Repository\AccountRepository;
|
||||
use App\Service\Customer\CustomerSendPasswordEmail;
|
||||
use App\Service\Mailer\Mailer;
|
||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
#[AsEventListener(event: EventCreatedWebsite::class, method: 'onEventCreatedWebsite', priority: 1)]
|
||||
class WebsiteCreatedSubscriber
|
||||
{
|
||||
public function __construct(
|
||||
private Mailer $mailer,
|
||||
private AccountRepository $accountRepository,
|
||||
private UrlGeneratorInterface $urlGenerator,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
public function onEventCreatedWebsite(EventCreatedWebsite $event) {
|
||||
$website = $event->getWebsite();
|
||||
$listEmail = [];
|
||||
foreach ($this->accountRepository->findAll() as $account) {
|
||||
if(in_array('ROLE_ROOT',$account->getRoles())) {
|
||||
$listEmail[] = $account->getEmail();
|
||||
}
|
||||
if(in_array('ROLE_ADMIN',$account->getRoles())) {
|
||||
$listEmail[] = $account->getEmail();
|
||||
}
|
||||
}
|
||||
$this->mailer->sendMulti(array_unique($listEmail),"[Mainframe] - Création d'un site internet en attends de validation","mails/esyWeb/website.twig",[
|
||||
'website' => $website,
|
||||
'validation_link' => $this->urlGenerator->generate('artemis_esyweb',['idValidate' => $website->getId()],UrlGeneratorInterface::ABSOLUTE_URL),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,27 @@
|
||||
{% block title %}Création d'un site internet{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ form_start(form) }}
|
||||
{{ form_end(form) }}
|
||||
<h2 class="text-3xl font-semibold text-gray-800 dark:text-gray-200">Crée un site internet</h2>
|
||||
<div class="mt-5 bg-gray-800 rounded-lg shadow-lg p-6 space-y-4">
|
||||
{{ form_start(form) }}
|
||||
<div class="flex space-x-4">
|
||||
<div class="flex-1">
|
||||
{{ form_row(form.title) }}
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
{{ form_row(form.type) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex space-x-4">
|
||||
<div class="flex-1">
|
||||
{{ form_row(form.customer) }}
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
{{ form_row(form.revendeur) }}
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="w-full bg-purple-600 hover:bg-purple-700 text-white font-semibold px-4 py-2 rounded">Crée le site internet</button>
|
||||
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -76,6 +76,9 @@
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">
|
||||
Statut
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="websitesTableBody" class="bg-gray-800 divide-y divide-gray-700">
|
||||
@@ -87,29 +90,40 @@
|
||||
{% for website in websites %}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-white">
|
||||
{{ website.name }}
|
||||
<span>{{ website.title }}</span><br/>
|
||||
<span>{{ website.uuid }}</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-400">
|
||||
{{ website.client }}
|
||||
<span>{{ website.customer.raisonSocial }}</span><br>
|
||||
<span>{{ website.customer.address }}</span><br>
|
||||
<span>{{ website.customer.zipcode }} {{ website.customer.city }}</span><br>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-blue-400 hover:text-blue-300">
|
||||
<a href="http://{{ website.ndd }}" target="_blank">{{ website.ndd }}</a>
|
||||
<a href="http://{{ website.mainDns }}" target="_blank">{{ website.mainDns }}</a>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{% set statusClass = 'bg-gray-600 text-gray-200' %}
|
||||
{% if website.status == 'Actif' %}
|
||||
{% if website.state == 'Actif' %}
|
||||
{% set statusClass = 'bg-green-700/50 text-green-300' %}
|
||||
{% elseif website.status == 'Suspendu' %}
|
||||
{% elseif website.state == 'Suspendu' %}
|
||||
{% set statusClass = 'bg-red-700/50 text-red-300' %}
|
||||
{% elseif website.status == 'En Développement' %}
|
||||
{% elseif website.state == 'En Développement' %}
|
||||
{% set statusClass = 'bg-yellow-700/50 text-yellow-300' %}
|
||||
{% elseif website.state == 'validate' %}
|
||||
{% set statusClass = 'bg-zinc-700/50 text-zinc-300' %}
|
||||
{% elseif website.state == 'created' %}
|
||||
{% set statusClass = 'bg-stone-700/50 text-stone-300' %}
|
||||
{% else %}
|
||||
{% set statusClass = 'bg-indigo-700/50 text-indigo-300' %}
|
||||
{% endif %}
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full {{ statusClass }}">
|
||||
{{ website.status }}
|
||||
{{ ('esyWeb_'~ website.state)|trans }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-blue-400 hover:text-blue-300">
|
||||
<a href="{{ path('artemis_esyweb_view',{id:website.id}) }}" class="block bg-blue-600 hover:bg-blue-700 text-white px-3 py-1 rounded">Voir le site internet</a>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
|
||||
0
templates/artemis/esyweb/website/main.twig
Normal file
0
templates/artemis/esyweb/website/main.twig
Normal file
26
templates/artemis/esyweb/website_view.twig
Normal file
26
templates/artemis/esyweb/website_view.twig
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends 'artemis/base.twig' %}
|
||||
|
||||
|
||||
{% block title %}
|
||||
Site Internet - {{ website.title }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h1 class="text-3xl font-bold text-white mb-6"> Site Internet - {{ website.title }}</h1>
|
||||
</div>
|
||||
<div class="flex space-x-4 mb-6 border-b border-gray-700">
|
||||
{% set active = "text-sm border-b-2 border-purple-500" %}
|
||||
{% set desactive = "text-sm text-gray-400 hover:text-white" %}
|
||||
<a href="{{ path('artemis_esyweb_view',{id:website.id,current:'main'}) }}" class="px-4 py-2 font-semibold {% if current == "main" %}{{ active }}{% else %}{{ desactive }}{% endif %}">
|
||||
<i class="fad fa-home"></i>
|
||||
Informations site internet
|
||||
</a>
|
||||
<a href="{{ path('artemis_esyweb_view',{id:website.id,current:'ndd'}) }}" class="px-4 py-2 font-semibold {% if current == "ndd" %}{{ active }}{% else %}{{ desactive }}{% endif %}">
|
||||
<i class="fad fa-home"></i>
|
||||
Nom de domaine
|
||||
</a>
|
||||
</div>
|
||||
{% include 'artemis/esyweb/website/'~current~".twig" %}
|
||||
|
||||
{% endblock %}
|
||||
71
templates/mails/esyWeb/website.twig
Normal file
71
templates/mails/esyWeb/website.twig
Normal file
@@ -0,0 +1,71 @@
|
||||
{% extends 'mails/base.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{# --- Section Principale : En-tête --- #}
|
||||
<mj-section background-color="#f5f5f5">
|
||||
<mj-column>
|
||||
<mj-text font-size="24px" font-weight="bold" color="#1e40af" align="center">
|
||||
Nouveau Site Web Soumis
|
||||
</mj-text>
|
||||
<mj-text font-size="18px" color="#333333" align="center" padding-top="0">
|
||||
En attente de votre validation
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# --- Section Détails du Site --- #}
|
||||
<mj-section background-color="#ffffff" padding-top="30px" padding-bottom="20px">
|
||||
<mj-column>
|
||||
<mj-text font-size="16px" line-height="24px" color="#333333">
|
||||
Bonjour,
|
||||
</mj-text>
|
||||
<mj-text font-size="16px" line-height="24px" color="#333333">
|
||||
Un nouveau projet de site internet a été soumis et attend votre approbation finale avant d'être mis en ligne.
|
||||
</mj-text>
|
||||
|
||||
{# Bloc des informations clés #}
|
||||
<mj-text font-size="16px" line-height="24px" color="#333333" padding-top="20px">
|
||||
<p style="font-weight: bold; margin-bottom: 5px;">Détails du projet :</p>
|
||||
<ul>
|
||||
<li style="list-style-type: none; margin-bottom: 5px;">
|
||||
<strong>Nom du Site :</strong> {{ datas.website.title }}
|
||||
</li>
|
||||
<li style="list-style-type: none; margin-bottom: 5px;">
|
||||
<strong>Client :</strong> {{ datas.website.customer.raisonSocial }}
|
||||
</li>
|
||||
</ul>
|
||||
</mj-text>
|
||||
|
||||
{# Bouton CTA de Validation #}
|
||||
<mj-button
|
||||
background-color="#1e40af"
|
||||
color="#ffffff"
|
||||
font-size="16px"
|
||||
padding="20px 0"
|
||||
href="{{ datas.validation_link }}"
|
||||
border-radius="8px"
|
||||
>
|
||||
Accéder à la Page de Validation
|
||||
</mj-button>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# --- Section Pied de Page (Rappel) --- #}
|
||||
<mj-section background-color="#f5f5f5" padding-top="10px" padding-bottom="10px">
|
||||
<mj-column>
|
||||
<mj-text font-size="14px" color="#888888" align="center">
|
||||
Veuillez valider ou rejeter ce site via le lien ci-dessus.
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
|
||||
{# --- Clôture --- #}
|
||||
<mj-section background-color="#ffffff">
|
||||
<mj-column>
|
||||
<mj-text font-size="16px" line-height="24px" color="#333333">
|
||||
Cordialement,<br/>
|
||||
L'équipe SARL SITECONSEIL
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
{% endblock %}
|
||||
@@ -79,3 +79,6 @@ customer_settings: Paramétres
|
||||
ech_created: Crée - En attends de validation
|
||||
dns: Nom de domaine
|
||||
dns_email: Nom de Domaine + Emails
|
||||
|
||||
esyWeb_created: Créer en attends de validation
|
||||
esyWeb_validate: Validation - En Attends de déploiement
|
||||
|
||||
Reference in New Issue
Block a user