Feat/disable services 662 (#669)

* disable button for services

* add button to disable services

---------

Co-authored-by: Sarahshr <sarah@les-tilleuls.coop>
This commit is contained in:
Sarahshr 2023-12-21 14:21:58 +01:00 committed by Slim
parent 45b25f42c9
commit a664e9a9bd
20 changed files with 266 additions and 157 deletions

View file

@ -4,6 +4,8 @@ App\Entity\Configuration:
features (extends configuration_template): features (extends configuration_template):
configuration: configuration:
services:
servicesEnabled: true
notificationsSender: notificationsSender:
notificationsSenderEmail: info@example.com notificationsSenderEmail: info@example.com
notificationsSenderName: Contact notificationsSenderName: Contact

View file

@ -10,14 +10,19 @@ use App\Controller\RequestTrait;
use App\Dto\Product\Search; use App\Dto\Product\Search;
use App\Entity\Product; use App\Entity\Product;
use App\Entity\User; use App\Entity\User;
use App\Enum\Product\ProductType;
use App\Form\Type\Product\SearchFormType; use App\Form\Type\Product\SearchFormType;
use App\Message\Query\Product\GetProductByIdQuery; use App\Message\Query\Product\GetProductByIdQuery;
use App\MessageBus\QueryBus; use App\MessageBus\QueryBus;
use App\Repository\ConfigurationRepository;
use App\Repository\ProductRepository;
use App\Search\Meilisearch; use App\Search\Meilisearch;
use Knp\Component\Pager\PaginatorInterface; use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Messenger\Exception\HandlerFailedException; use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Requirement\Requirement; use Symfony\Component\Routing\Requirement\Requirement;
@ -42,6 +47,8 @@ final class ProductController extends AbstractController
private readonly QueryBus $queryBus, private readonly QueryBus $queryBus,
private readonly PaginatorInterface $paginator, private readonly PaginatorInterface $paginator,
private readonly Meilisearch $meilisearch, private readonly Meilisearch $meilisearch,
private readonly ConfigurationRepository $configurationRepository,
private readonly ProductRepository $productRepository,
) { ) {
} }
@ -64,6 +71,7 @@ final class ProductController extends AbstractController
'objects_pagination' => $this->paginate($this->meilisearch->searchObjects($searchDto)), 'objects_pagination' => $this->paginate($this->meilisearch->searchObjects($searchDto)),
'services_pagination' => $this->paginate($this->meilisearch->searchServices($searchDto)), 'services_pagination' => $this->paginate($this->meilisearch->searchServices($searchDto)),
'search_form' => $searchForm, 'search_form' => $searchForm,
'services_enabled' => $this->configurationRepository->getServicesParameter(),
]); ]);
} }
@ -79,6 +87,14 @@ final class ProductController extends AbstractController
)] )]
public function show(string $slug, string $id): Response public function show(string $slug, string $id): Response
{ {
/** @var ?Product $product */
$product = $this->productRepository->find(['id' => $id]);
if ($product === null) {
throw new NotFoundHttpException();
}
if (($product->getType() === ProductType::SERVICE && $this->configurationRepository->getServicesParameter()) || $product->getType() === ProductType::OBJECT) {
try { try {
/** @var Product $product */ /** @var Product $product */
$product = $this->queryBus->query(new GetProductByIdQuery(Uuid::fromString($id))); $product = $this->queryBus->query(new GetProductByIdQuery(Uuid::fromString($id)));
@ -87,5 +103,8 @@ final class ProductController extends AbstractController
} }
return $this->render('pages/product/show.html.twig', compact('slug', 'id', 'product')); return $this->render('pages/product/show.html.twig', compact('slug', 'id', 'product'));
} else {
throw new GoneHttpException();
}
} }
} }

View file

@ -10,6 +10,7 @@ use App\Dto\Product\Search;
use App\Entity\User; use App\Entity\User;
use App\Message\Query\User\Account\GetUserQuery; use App\Message\Query\User\Account\GetUserQuery;
use App\MessageBus\QueryBus; use App\MessageBus\QueryBus;
use App\Repository\ConfigurationRepository;
use App\Search\Meilisearch; use App\Search\Meilisearch;
use Knp\Component\Pager\PaginatorInterface; use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@ -33,6 +34,7 @@ final class ProfileAction extends AbstractController
private readonly QueryBus $queryBus, private readonly QueryBus $queryBus,
private readonly PaginatorInterface $paginator, private readonly PaginatorInterface $paginator,
private readonly Meilisearch $meilisearch, private readonly Meilisearch $meilisearch,
private readonly ConfigurationRepository $configurationRepository,
) { ) {
} }
@ -59,6 +61,7 @@ final class ProfileAction extends AbstractController
'user' => $user, 'user' => $user,
'objects_pagination' => $this->paginate($this->meilisearch->searchObjects($searchDto)), 'objects_pagination' => $this->paginate($this->meilisearch->searchObjects($searchDto)),
'services_pagination' => $this->paginate($this->meilisearch->searchServices($searchDto)), 'services_pagination' => $this->paginate($this->meilisearch->searchServices($searchDto)),
'services_enabled' => $this->configurationRepository->getServicesParameter(),
]); ]);
} }
} }

View file

@ -47,6 +47,8 @@ final class MyAccountAction extends AbstractController
$canCreateGroup = $configuration->isGroupsCreationForAll() || $user->isAdmin(); $canCreateGroup = $configuration->isGroupsCreationForAll() || $user->isAdmin();
$contactEmail = $configuration->getContactEmail(); $contactEmail = $configuration->getContactEmail();
return $this->render('pages/account/index.html.twig', compact('userHasNewLoanMessage', 'userHasNewLendingMessage', 'canCreateGroup', 'contactEmail')); $servicesConfig = $this->configurationRepository->getServicesParameter();
return $this->render('pages/account/index.html.twig', compact('userHasNewLoanMessage', 'userHasNewLendingMessage', 'canCreateGroup', 'contactEmail', 'servicesConfig'));
} }
} }

View file

@ -11,12 +11,14 @@ use App\Entity\Product;
use App\Entity\User; use App\Entity\User;
use App\Form\Type\Product\ServiceFormType; use App\Form\Type\Product\ServiceFormType;
use App\MessageBus\QueryBus; use App\MessageBus\QueryBus;
use App\Repository\ConfigurationRepository;
use App\Tests\Functional\Controller\Product\ServiceControllerTest; use App\Tests\Functional\Controller\Product\ServiceControllerTest;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Requirement\Requirement; use Symfony\Component\Routing\Requirement\Requirement;
use Symfony\Component\Security\Http\Attribute\CurrentUser; use Symfony\Component\Security\Http\Attribute\CurrentUser;
@ -37,6 +39,7 @@ final class ServiceController extends AbstractController
public function __construct( public function __construct(
private readonly QueryBus $queryBus, private readonly QueryBus $queryBus,
private readonly ProductManager $productManager, private readonly ProductManager $productManager,
private readonly ConfigurationRepository $configurationRepository,
) { ) {
} }
@ -51,6 +54,7 @@ final class ServiceController extends AbstractController
], name: 'new')] ], name: 'new')]
public function new(Request $request, #[CurrentUser] User $user): Response public function new(Request $request, #[CurrentUser] User $user): Response
{ {
if ($this->configurationRepository->getServicesParameter()) {
$product = $this->productManager->initService($user); $product = $this->productManager->initService($user);
$form = $this->getForm($product, $request); $form = $this->getForm($product, $request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
@ -63,6 +67,9 @@ final class ServiceController extends AbstractController
} }
return $this->render('pages/product/new_service.html.twig', compact('form')); return $this->render('pages/product/new_service.html.twig', compact('form'));
} else {
throw new GoneHttpException();
}
} }
#[Route([ #[Route([
@ -74,6 +81,7 @@ final class ServiceController extends AbstractController
)] )]
public function edit(string $id, Request $request): Response public function edit(string $id, Request $request): Response
{ {
if ($this->configurationRepository->getServicesParameter()) {
$product = $this->getProductForEdit($id); $product = $this->getProductForEdit($id);
$form = $this->getForm($product, $request); $form = $this->getForm($product, $request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
@ -86,5 +94,8 @@ final class ServiceController extends AbstractController
} }
return $this->render('pages/product/edit_service.html.twig', compact('form', 'product')); return $this->render('pages/product/edit_service.html.twig', compact('form', 'product'));
} else {
throw new GoneHttpException();
}
} }
} }

View file

@ -15,12 +15,14 @@ use App\Message\Query\User\GetUserObjectsQuery;
use App\Message\Query\User\GetUserServicesQuery; use App\Message\Query\User\GetUserServicesQuery;
use App\MessageBus\QueryBus; use App\MessageBus\QueryBus;
use App\Repository\CategoryRepository; use App\Repository\CategoryRepository;
use App\Repository\ConfigurationRepository;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Knp\Component\Pager\Pagination\PaginationInterface; use Knp\Component\Pager\Pagination\PaginationInterface;
use Knp\Component\Pager\PaginatorInterface; use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\GoneHttpException;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser; use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted; use Symfony\Component\Security\Http\Attribute\IsGranted;
@ -40,6 +42,7 @@ final class UserProductsController extends AbstractController
private readonly QueryBus $queryBus, private readonly QueryBus $queryBus,
private readonly PaginatorInterface $paginator, private readonly PaginatorInterface $paginator,
public readonly CategoryRepository $categoryRepository, public readonly CategoryRepository $categoryRepository,
private readonly ConfigurationRepository $configurationRepository,
) { ) {
} }
@ -89,6 +92,10 @@ final class UserProductsController extends AbstractController
$query = $this->queryBus->query(new GetUserServicesQuery($user->getId(), $category?->getId())); $query = $this->queryBus->query(new GetUserServicesQuery($user->getId(), $category?->getId()));
$pagination = $this->paginate($query, $this->getPage($request)); $pagination = $this->paginate($query, $this->getPage($request));
if ($this->configurationRepository->getServicesParameter()) {
return $this->render('pages/account/product/list.html.twig', compact('pagination', 'form')); return $this->render('pages/account/product/list.html.twig', compact('pagination', 'form'));
} else {
throw new GoneHttpException('there is no services');
}
} }
} }

View file

@ -75,6 +75,24 @@ class Configuration
/** end of basic getters and setters ------------------------------------------------ */ /** end of basic getters and setters ------------------------------------------------ */
/**
* @return bool[]
*/
public function getServices(): array
{
/** @var array<bool> $services */
$services = $this->configuration['services'] ?? [];
return $services;
}
public function getServicesEnabled(): bool
{
$services = $this->getServices();
return $services['servicesEnabled'];
}
/** /**
* @return array<string, string> * @return array<string, string>
*/ */

View file

@ -25,10 +25,18 @@ final class ParametersFormType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options): void
{ {
$builder $builder
->add('servicesEnabled', CheckboxType::class, [
'label' => 'parameter.services',
'label_attr' => [
'class' => 'checkbox-inline checkbox-switch',
],
])
->add('notificationsSenderEmail', EmailType::class, [ ->add('notificationsSenderEmail', EmailType::class, [
'label' => 'parameter.mail', 'label' => 'parameter.mail',
'label_attr' => ['class' => 'col-sm-2 col-form-label'], 'label_attr' => ['class' => 'col-sm-2 col-form-label'],
]) ])
->add('notificationsSenderName', TextType::class, [ ->add('notificationsSenderName', TextType::class, [
'label' => 'parameter.name', 'label' => 'parameter.name',
]) ])

View file

@ -17,6 +17,10 @@ final class ParametersFormCommand extends AbstractFormCommand
final public const ONLY_ADMIN = 'only_admin'; final public const ONLY_ADMIN = 'only_admin';
final public const ALL = 'all'; final public const ALL = 'all';
// services section —————————————————————————————————————————————
#[Assert\Type('bool')]
public bool $servicesEnabled = true;
// notificationsSender section ————————————————————————————————————————————— // notificationsSender section —————————————————————————————————————————————
#[Assert\Email()] #[Assert\Email()]
#[Assert\NotBlank()] #[Assert\NotBlank()]
@ -55,6 +59,7 @@ final class ParametersFormCommand extends AbstractFormCommand
protected function getSections(): array protected function getSections(): array
{ {
return [ return [
'services',
'notificationsSender', 'notificationsSender',
'contact', 'contact',
'groups', 'groups',
@ -71,6 +76,7 @@ final class ParametersFormCommand extends AbstractFormCommand
public function hydrate(Configuration $configuration): self public function hydrate(Configuration $configuration): self
{ {
$instanceConfiguration = $configuration->getConfiguration(); $instanceConfiguration = $configuration->getConfiguration();
// dd($instanceConfiguration);
foreach (array_keys(get_class_vars($this::class)) as $classVar) { foreach (array_keys(get_class_vars($this::class)) as $classVar) {
$this->{$classVar} = $instanceConfiguration[$this->getSection($classVar)][$classVar]; // @phpstan-ignore-line $this->{$classVar} = $instanceConfiguration[$this->getSection($classVar)][$classVar]; // @phpstan-ignore-line
} }

View file

@ -58,4 +58,16 @@ final class ConfigurationRepository extends ServiceEntityRepository
return $cfg; return $cfg;
} }
public function getServicesParameter(): bool
{
/** @var array{configuration: array{ services: array{ servicesEnabled: bool }}} $config */
$config = $this
->createQueryBuilder('c')
->select('c.configuration')
->setMaxResults(1)
->getQuery()->getOneOrNullResult();
return $config['configuration']['services']['servicesEnabled'];
}
} }

View file

@ -14,6 +14,11 @@
{% block main %} {% block main %}
{{ form_start(form) }} {{ form_start(form) }}
<div class="row mb-lg-5"> <div class="row mb-lg-5">
<h2 class="h3 fw-bold mt-3">{{ 'parameters.services.h2'|trans }}</h2>
<hr/>
{{ form_widget(form.servicesEnabled) }}
<h2 class="h3 fw-bold mt-3">{{ 'parameters.senders.h2'|trans }}</h2> <h2 class="h3 fw-bold mt-3">{{ 'parameters.senders.h2'|trans }}</h2>
<hr/> <hr/>

View file

@ -15,7 +15,7 @@
</div> </div>
<p class="ms-3 mb-0 fs-6 fw-bolder">{{ name }}</p> <p class="ms-3 mb-0 fs-6 fw-bolder">{{ name }}</p>
</div> </div>
{% if isPlace %} {% if isPlace and address is not null %}
<div class="row mt-3"> <div class="row mt-3">
<div class="col"> <div class="col">
<p class="fw-bold fs-6 mb-0">{{ 'templates.components.product.lender.address'|trans }}</p> <p class="fw-bold fs-6 mb-0">{{ 'templates.components.product.lender.address'|trans }}</p>

View file

@ -11,6 +11,7 @@
{{ 'product.list_type_objects'|trans }} ({{ objects_pagination.totalItemCount }}) {{ 'product.list_type_objects'|trans }} ({{ objects_pagination.totalItemCount }})
</button> </button>
</li> </li>
{% if services_enabled %}
<li class="nav-item product-type w-50" role="presentation"> <li class="nav-item product-type w-50" role="presentation">
<button class="nav-link rounded-0 w-100 {% if index == 1 %}active{% endif %}" <button class="nav-link rounded-0 w-100 {% if index == 1 %}active{% endif %}"
id="pills-services-tab" id="pills-services-tab"
@ -21,6 +22,7 @@
({{ services_pagination.totalItemCount }}) ({{ services_pagination.totalItemCount }})
</button> </button>
</li> </li>
{% endif %}
</ul> </ul>
</div> </div>
</div> </div>

View file

@ -15,7 +15,7 @@
pagination: objects_pagination, pagination: objects_pagination,
} %} } %}
</div> </div>
{% if services_pagination is defined and services_pagination is not null %} {% if services_pagination is defined and services_pagination is not null and services_enabled %}
<div class="tab-pane fade" <div class="tab-pane fade"
id="pills-services" id="pills-services"
role="tabpanel" role="tabpanel"

View file

@ -53,6 +53,7 @@
}, },
{ {
section: 'Mes services', section: 'Mes services',
disable: servicesConfig,
links: [ links: [
{ {
name: 'Voir mes services', name: 'Voir mes services',
@ -133,6 +134,7 @@
} %} } %}
<div class="row flex-wrap"> <div class="row flex-wrap">
{% for my_account_link in my_account_links %} {% for my_account_link in my_account_links %}
{% if my_account_link.disable is not defined %}
<div class="col-12 col-md-4"> <div class="col-12 col-md-4">
<nav class="myAccount d-flex flex-column mt-3 mt-md-5"> <nav class="myAccount d-flex flex-column mt-3 mt-md-5">
<div class="myAccount-header w-100 rounded-2"> <div class="myAccount-header w-100 rounded-2">
@ -183,6 +185,7 @@
</div> </div>
</nav> </nav>
</div> </div>
{% endif %}
{% endfor %} {% endfor %}
</div> </div>
<div class="d-grid col-12 col-md-6 mx-auto mt-5"> <div class="d-grid col-12 col-md-6 mx-auto mt-5">

View file

@ -4,8 +4,8 @@
<div class="px-3 px-lg-0"> <div class="px-3 px-lg-0">
{% include 'components/layout/_title_3.html.twig' with {name: 'product.list_name'|trans} %} {% include 'components/layout/_title_3.html.twig' with {name: 'product.list_name'|trans} %}
{% include 'components/product/_search.html.twig' with {form: search_form} %} {% include 'components/product/_search.html.twig' with {form: search_form} %}
{% include 'components/product/_section.html.twig'with {objects_pagination, services_pagination} %} {% include 'components/product/_section.html.twig'with {objects_pagination, services_pagination, services_enabled} %}
{% include 'components/product/_tab_content.html.twig' with {objects_pagination, services_pagination} %} {% include 'components/product/_tab_content.html.twig' with {objects_pagination, services_pagination, services_enabled} %}
</div> </div>
{% endblock %} {% endblock %}

View file

@ -56,7 +56,7 @@
<span>{{ (i18n_prefix ~ '.no_result')|trans }}</span> <span>{{ (i18n_prefix ~ '.no_result')|trans }}</span>
</div> </div>
{% else %} {% else %}
{% include 'components/product/_section.html.twig' %} {% include 'components/product/_section.html.twig' with {services_enabled} %}
{% include 'components/product/_tab_content.html.twig' with {objects_pagination, services_pagination} %} {% include 'components/product/_tab_content.html.twig' with {objects_pagination, services_pagination} %}
{% endif %} {% endif %}
</div> </div>

View file

@ -34,7 +34,7 @@ final class ParametersControllerTest extends WebTestCase
$form = $crawler->selectButton('parameters_form_submit')->form(); $form = $crawler->selectButton('parameters_form_submit')->form();
self::assertSame(5, $crawler->filter('input:checked')->count()); self::assertSame(6, $crawler->filter('input:checked')->count());
/** @var FormField $notificationsSenderEmailField */ /** @var FormField $notificationsSenderEmailField */
$notificationsSenderEmailField = $form->get('parameters_form[notificationsSenderEmail]'); $notificationsSenderEmailField = $form->get('parameters_form[notificationsSenderEmail]');
@ -69,7 +69,7 @@ final class ParametersControllerTest extends WebTestCase
$crawler = $client->followRedirect(); $crawler = $client->followRedirect();
self::assertResponseIsSuccessful(); self::assertResponseIsSuccessful();
self::assertSame(1, $crawler->filter('input:checked')->count()); self::assertSame(2, $crawler->filter('input:checked')->count());
$form = $crawler->selectButton('parameters_form_submit')->form(); $form = $crawler->selectButton('parameters_form_submit')->form();
/** @var FormField $groupsCreationModeField */ /** @var FormField $groupsCreationModeField */

View file

@ -260,6 +260,11 @@
<target>Paramètres de l'instance</target> <target>Paramètres de l'instance</target>
</trans-unit> </trans-unit>
<trans-unit id="e1qNBMp" resname="parameters.services.h2">
<source>parameters.services.h2</source>
<target>Services</target>
</trans-unit>
<trans-unit id="eNqNAUs" resname="parameters.senders.h2"> <trans-unit id="eNqNAUs" resname="parameters.senders.h2">
<source>parameters.senders.h2</source> <source>parameters.senders.h2</source>
<target>Expéditeur·rice des notifications</target> <target>Expéditeur·rice des notifications</target>

View file

@ -6,6 +6,12 @@
</header> </header>
<body> <body>
<!-- notification sender section -->
<trans-unit id="qlPbwB3" resname="parameter.services">
<source>parameter.services</source>
<target>Services activés</target>
</trans-unit>
<!-- notification sender section --> <!-- notification sender section -->
<trans-unit id="qqTbwBV" resname="parameter.mail"> <trans-unit id="qqTbwBV" resname="parameter.mail">
<source>parameter.mail</source> <source>parameter.mail</source>