Merge branch 'feature/ndf' into develop

This commit is contained in:
Simon Vieille 2022-04-29 16:54:23 +02:00
commit 80b317391c
26 changed files with 1691 additions and 184 deletions

View file

@ -7,8 +7,9 @@
"prefer-stable": true,
"require": {
"php": ">=8.0.0",
"doctrine/orm": "2.11.*",
"knplabs/knp-snappy": "^1.4",
"murph/murph-core": "^1.13,>=1.13.0",
"murph/murph-core": "dev-master",
"sabre/dav": "^4.3"
},
"require-dev": {

View file

@ -14,6 +14,7 @@ security:
role_hierarchy:
ROLE_WRITER: ROLE_USER
ROLE_MANAGER: ROLE_WRITER
ROLE_TREASURER: ROLE_USER
ROLE_ADMIN: ROLE_MANAGER
firewalls:

View file

@ -0,0 +1,337 @@
<?php
namespace App\Controller;
use App\Core\Controller\Admin\Crud\CrudController;
use App\Core\Crud\CrudConfiguration;
use App\Core\Crud\Field;
use App\Core\Entity\EntityInterface;
use App\Core\Event\EntityManager\EntityManagerEvent;
use App\Core\Form\FileUploadHandler;
use App\Core\Manager\EntityManager;
use App\Entity\ExpenseReport as Entity;
use App\Factory\ExpenseReportFactory as Factory;
use App\Form\ExpenseReportType as Type;
use App\Repository\ExpenseReportRepositoryQuery as RepositoryQuery;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
class ExpenseReportAdminController extends CrudController
{
protected FileUploadHandler $fileUpload;
protected Filesystem $fs;
public function __construct(FileUploadHandler $fileUpload, Filesystem $fs)
{
$this->fileUpload = $fileUpload;
$this->fs = $fs;
}
/**
* @Route("/admin/expense_report/{page}", name="admin_expense_report_index", methods={"GET"}, requirements={"page":"\d+"})
*/
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
{
if (!$this->isGranted('ROLE_TREASURER')) {
$query->filterByUser($this->getUser());
}
return $this->doIndex($page, $query, $request, $session);
}
/**
* @Route("/admin/expense_report/new", name="admin_expense_report_new", methods={"GET", "POST"})
*/
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
{
return $this->doNew($factory->create($this->getUser()), $entityManager, $request);
}
/**
* @Route("/admin/expense_report/show/{entity}", name="admin_expense_report_show", methods={"GET"})
*/
public function show(Entity $entity): Response
{
if (!$this->isGranted('ROLE_TREASURER')) {
if ($entity->getUser()->getId() !== $entity->getUser()->getId()) {
throw $this->createAccessDeniedException();
}
}
return $this->doShow($entity);
}
/**
* @Route("/admin/expense_report/filter", name="admin_expense_report_filter", methods={"GET"})
*/
public function filter(Session $session): Response
{
return $this->doFilter($session);
}
/**
* @Route("/admin/expense_report/edit/{entity}", name="admin_expense_report_edit", methods={"GET", "POST"})
*/
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
{
if (!$this->isGranted('ROLE_TREASURER')) {
if ($entity->getUser()->getId() !== $entity->getUser()->getId()) {
throw $this->createAccessDeniedException();
}
if (true === $entity->getIsRequestedPayment()) {
return $this->redirectToRoute('admin_expense_report_show', [
'entity' => $entity->getId(),
]);
}
}
return $this->doEdit($entity, $entityManager, $request, [$this, 'beforeUpdate']);
}
/**
* @Route("/admin/expense_report/request_payment/{entity}/{token}", name="admin_expense_report_request_payment", methods={"GET"})
*/
public function requestPayment(
Entity $entity,
string $token,
EntityManager $entityManager,
Request $request,
EventDispatcherInterface $eventDispatcher
): Response {
if (!$this->isGranted('ROLE_TREASURER')) {
if ($entity->getUser()->getId() !== $entity->getUser()->getId()) {
throw $this->createAccessDeniedException();
}
}
if (!$this->isCsrfTokenValid('request_payment', $token)) {
throw $this->createAccessDeniedException();
}
$entity->setIsRequestedPayment(true);
$entityManager->update($entity);
$eventDispatcher->dispatch(new EntityManagerEvent($entity), 'expense_report.requested_payment');
$this->addFlash('success', 'La demande a été envoyée.');
return $this->redirectToRoute('admin_expense_report_show', [
'entity' => $entity->getId(),
]);
}
/**
* @Route("/admin/expense_report/paud/{entity}/{token}", name="admin_expense_report_paid", methods={"GET"})
*/
public function paid(
Entity $entity,
string $token,
EntityManager $entityManager,
Request $request,
EventDispatcherInterface $eventDispatcher
): Response {
if (!$this->isGranted('ROLE_TREASURER')) {
throw $this->createAccessDeniedException();
}
if (!$this->isCsrfTokenValid('paid', $token)) {
throw $this->createAccessDeniedException();
}
$entity->setIsPaid(true);
if (!$entity->getPaidAt()) {
$entity->setPaidAt(new \DateTime());
}
$entityManager->update($entity);
$eventDispatcher->dispatch(new EntityManagerEvent($entity), 'expense_report.paid');
$this->addFlash('success', 'The data has been saved.');
return $this->redirectToRoute('admin_expense_report_show', [
'entity' => $entity->getId(),
]);
}
protected function beforeUpdate(EntityInterface $entity, FormInterface $form, Request $request)
{
$deleteBills = $request->request->get($form->getName())['deleteBills']['bills'] ?? [];
$bills = $entity->getBills();
foreach ($bills as $key => $value) {
if (in_array($value, $deleteBills)) {
unset($bills[$key]);
$this->fs->remove($value);
}
}
$directory = sprintf(
'uploads/Notes de frais/%d/%d',
$this->getUser()->getId(),
$entity->getId()
);
$newBills = $request->files->get($form->getName())['newBills'] ?? [];
foreach ($newBills as $data) {
$this->fileUpload->handleForm(
$data['file'],
$directory,
function ($filename) use ($directory, &$bills) {
$bills[] = $directory.'/'.$filename;
},
true
);
}
$entity->setBills($bills);
}
/**
* @Route("/admin/expense_report/sort/{page}", name="admin_expense_report_sort", methods={"POST"}, requirements={"page":"\d+"})
*/
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
{
return $this->doSort($page, $query, $entityManager, $request, $session);
}
/**
* @Route("/admin/expense_report/batch/{page}", name="admin_expense_report_batch", methods={"POST"}, requirements={"page":"\d+"})
*/
public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
{
if (!$this->isGranted('ROLE_TREASURER')) {
$query->filterByUser($this->getUser());
}
return $this->doBatch($page, $query, $entityManager, $request, $session);
}
/**
* @Route("/admin/expense_report/delete/{entity}", name="admin_expense_report_delete", methods={"DELETE"})
*/
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
if (!$this->isGranted('ROLE_TREASURER')) {
if ($entity->getUser()->getId() !== $entity->getUser()->getId()) {
throw $this->createAccessDeniedException();
}
}
return $this->doDelete($entity, $entityManager, $request);
}
protected function getConfiguration(): CrudConfiguration
{
return CrudConfiguration::create()
->setPageTitle('index', 'Notes de frais')
->setPageTitle('edit', 'Note de frais {id}')
->setPageTitle('new', 'Nouvelle note de frais')
->setPageTitle('show', 'Note de frais {id}')
->setPageRoute('index', 'admin_expense_report_index')
->setPageRoute('new', 'admin_expense_report_new')
->setPageRoute('edit', 'admin_expense_report_edit')
->setPageRoute('show', 'admin_expense_report_show')
->setPageRoute('sort', 'admin_expense_report_sort')
->setPageRoute('batch', 'admin_expense_report_batch')
->setPageRoute('delete', 'admin_expense_report_delete')
->setPageRoute('filter', 'admin_expense_report_filter')
->setPageRoute('requestPayment', 'admin_expense_report_request_payment')
->setPageRoute('paid', 'admin_expense_report_paid')
->setFormOptions('new', [
'is_treasurer' => $this->isGranted('ROLE_TREASURER'),
])
->setFormOptions('edit', [
'is_treasurer' => $this->isGranted('ROLE_TREASURER'),
])
->setView('form', 'admin/expense_report/_form.html.twig')
->setView('show_entity', 'admin/expense_report/_show.html.twig')
->setView('edit', 'admin/expense_report/edit.html.twig')
->setForm('edit', Type::class, [])
->setForm('new', Type::class)
// ->setForm('filter', Type::class)
// ->setMaxPerPage('index', 20)
// ->setIsSortableCollection('index', false)
// ->setSortableCollectionProperty('sortOrder')
// ->setAction('index', 'new', true)
// ->setAction('index', 'show', true)
// ->setAction('index', 'edit', true)
// ->setAction('index', 'delete', true)
// ->setAction('edit', 'back', true)
// ->setAction('edit', 'show', true)
// ->setAction('edit', 'delete', true)
// ->setAction('show', 'back', true)
// ->setAction('show', 'edit', true)
->setDefaultSort('index', 'dateTo', 'desc')
->setField('index', 'Personne', Field\TextField::class, [
'property_builder' => function (EntityInterface $entity) {
return $entity->getUser()->getDisplayName();
},
'attr' => ['class' => 'col-md-3'],
])
->setField('index', 'Date from', Field\DateField::class, [
'property' => 'dateFrom',
'sort' => ['dateFrom', '.dateFrom'],
'format' => 'd/m/Y',
'attr' => ['class' => 'col-md-3'],
])
->setField('index', 'Date to', Field\DateField::class, [
'property' => 'dateTo',
'sort' => ['dateTo', '.dateTo'],
'format' => 'd/m/Y',
'attr' => ['class' => 'col-md-3'],
])
->setField('index', 'Statut', Field\ButtonField::class, [
'property_builder' => function (EntityInterface $entity) {
if ($entity->getIsPaid()) {
return 'Payée';
}
if ($entity->getIsRequestedPayment()) {
return 'En attente de paiement';
}
return 'Brouillon';
},
'attr' => ['class' => 'col-md-3'],
'button_attr_builder' => function (EntityInterface $entity) {
$class = 'light';
if ($entity->getIsPaid()) {
$class = 'success';
} elseif ($entity->getIsRequestedPayment()) {
$class = 'warning';
}
return ['class' => 'btn btn-sm btn-'.$class];
},
])
// ->setBatchAction('index', 'delete', 'Delete', function (EntityInterface $entity, EntityManager $manager) {
// $manager->delete($entity);
// })
;
}
protected function getSection(): string
{
return 'expense_report';
}
}

View file

@ -0,0 +1,196 @@
<?php
namespace App\Entity;
use App\Repository\ExpenseReportRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Core\Entity\EntityInterface;
/**
* @ORM\Entity(repositoryClass=ExpenseReportRepository::class)
*/
class ExpenseReport implements EntityInterface
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=User::class, inversedBy="expenseReports")
* @ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* @ORM\Column(type="array")
*/
private $moves = [];
/**
* @ORM\Column(type="array")
*/
private $variousPayments = [];
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $paidAt;
/**
* @ORM\Column(type="array")
*/
private $bills = [];
/**
* @ORM\Column(type="date")
*/
private $dateFrom;
/**
* @ORM\Column(type="date")
*/
private $dateTo;
/**
* @ORM\Column(type="float")
*/
private $scalePerKilometer;
/**
* @ORM\Column(type="boolean", options={"default"=0})
*/
private $isRequestedPayment;
/**
* @ORM\Column(type="boolean", options={"default"=0})
*/
private $isPaid;
public function getId(): ?int
{
return $this->id;
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
public function getMoves(): ?array
{
return $this->moves;
}
public function setMoves(array $moves): self
{
$this->moves = $moves;
return $this;
}
public function getVariousPayments(): ?array
{
return $this->variousPayments;
}
public function setVariousPayments(array $variousPayments): self
{
$this->variousPayments = $variousPayments;
return $this;
}
public function getIsPaid(): ?bool
{
return $this->isPaid;
}
public function setIsPaid(bool $isPaid): self
{
$this->isPaid = $isPaid;
return $this;
}
public function getPaidAt(): ?\DateTimeInterface
{
return $this->paidAt;
}
public function setPaidAt(?\DateTimeInterface $paidAt): self
{
$this->paidAt = $paidAt;
return $this;
}
public function getBills(): ?array
{
return $this->bills;
}
public function setBills(array $bills): self
{
$this->bills = $bills;
return $this;
}
public function getDateFrom(): ?\DateTimeInterface
{
return $this->dateFrom;
}
public function setDateFrom(\DateTimeInterface $dateFrom): self
{
$this->dateFrom = $dateFrom;
return $this;
}
public function getDateTo(): ?\DateTimeInterface
{
return $this->dateTo;
}
public function setDateTo(\DateTimeInterface $dateTo): self
{
$this->dateTo = $dateTo;
return $this;
}
public function getScalePerKilometer(): ?float
{
return $this->scalePerKilometer;
}
public function setScalePerKilometer(float $scalePerKilometer): self
{
$this->scalePerKilometer = $scalePerKilometer;
return $this;
}
public function getIsRequestedPayment(): ?bool
{
return $this->isRequestedPayment;
}
public function setIsRequestedPayment(bool $isRequestedPayment): self
{
$this->isRequestedPayment = $isRequestedPayment;
return $this;
}
}

View file

@ -5,6 +5,8 @@ namespace App\Entity;
use App\Core\Doctrine\Timestampable;
use App\Core\Entity\EntityInterface;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
@ -75,8 +77,19 @@ class User implements PasswordAuthenticatedUserInterface, UserInterface, TwoFact
*/
protected $isManager;
/**
* @ORM\Column(type="boolean", nullable=true)
*/
protected $isTreasurer;
/**
* @ORM\OneToMany(targetEntity=ExpenseReport::class, mappedBy="user", orphanRemoval=true)
*/
private $expenseReports;
public function __construct()
{
$this->expenseReports = new ArrayCollection();
}
public function __toString()
@ -127,6 +140,10 @@ class User implements PasswordAuthenticatedUserInterface, UserInterface, TwoFact
$roles[] = 'ROLE_MANAGER';
}
if ($this->getIsTreasurer()) {
$roles[] = 'ROLE_TREASURER';
}
return array_unique($roles);
}
@ -268,4 +285,46 @@ class User implements PasswordAuthenticatedUserInterface, UserInterface, TwoFact
return $this;
}
public function getIsTreasurer(): ?bool
{
return $this->isTreasurer;
}
public function setIsTreasurer(?bool $isTreasurer): self
{
$this->isTreasurer = $isTreasurer;
return $this;
}
/**
* @return Collection<int, ExpenseReport>
*/
public function getExpenseReports(): Collection
{
return $this->expenseReports;
}
public function addExpenseReport(ExpenseReport $expenseReport): self
{
if (!$this->expenseReports->contains($expenseReport)) {
$this->expenseReports[] = $expenseReport;
$expenseReport->setUser($this);
}
return $this;
}
public function removeExpenseReport(ExpenseReport $expenseReport): self
{
if ($this->expenseReports->removeElement($expenseReport)) {
// set the owning side to null (unless already changed)
if ($expenseReport->getUser() === $this) {
$expenseReport->setUser(null);
}
}
return $this;
}
}

View file

@ -0,0 +1,126 @@
<?php
namespace App\EventSubscriber;
use App\Core\Entity\EntityInterface;
use App\Core\Event\EntityManager\EntityManagerEvent;
use App\Core\EventSubscriber\EntityManagerEventSubscriber;
use App\Core\Notification\MailNotifier;
use App\Core\Setting\SettingManager;
use App\Entity\ExpenseReport;
use App\Repository\UserRepositoryQuery;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class ExpenseReportEventSubscriber extends EntityManagerEventSubscriber
{
protected SettingManager $manager;
protected MailNotifier $notifier;
protected UrlGeneratorInterface $urlGenerator;
protected UserRepositoryQuery $userQuery;
public function __construct(
SettingManager $manager,
MailNotifier $notifier,
UrlGeneratorInterface $urlGenerator,
UserRepositoryQuery $userQuery
) {
$this->manager = $manager;
$this->notifier = $notifier;
$this->urlGenerator = $urlGenerator;
$this->userQuery = $userQuery;
}
public static function getSubscribedEvents()
{
return array_merge(parent::getSubscribedEvents(), [
'expense_report.requested_payment' => ['onRequestedPayment', self::$priority],
'expense_report.paid' => ['onPaid', self::$priority],
]);
}
public function supports(EntityInterface $entity)
{
return $entity instanceof ExpenseReport;
}
public function onPaid(EntityManagerEvent $event)
{
if (!$this->supports($event->getEntity())) {
return;
}
$mails = array_map(
fn ($e) => $e->getEmail(),
$this->userQuery->where('.isTreasurer = true')->find()
);
$this->notifier
->setSubject('[Tinternet][NDF] Paiement réalisé')
->addRecipient($event->getEntity()->getUser()->getEmail())
->addRecipients($mails, true)
->notify('mail/expense_report/paid.html.twig', [
'entity' => $event->getEntity(),
'show_url' => $this->urlGenerator->generate(
'admin_expense_report_show',
[
'entity' => $event->getEntity()->getId(),
],
UrlGeneratorInterface::ABSOLUTE_URL
),
], 'text/plain')
;
}
public function onRequestedPayment(EntityManagerEvent $event)
{
if (!$this->supports($event->getEntity())) {
return;
}
$mails = array_map(
fn ($e) => $e->getEmail(),
$this->userQuery->where('.isTreasurer = true')->find()
);
$this->notifier
->setSubject('[Tinternet][NDF] Demande de paiement')
->addRecipients($mails, false)
->notify('mail/expense_report/requested_payment.html.twig', [
'entity' => $event->getEntity(),
'show_url' => $this->urlGenerator->generate(
'admin_expense_report_show',
[
'entity' => $event->getEntity()->getId(),
],
UrlGeneratorInterface::ABSOLUTE_URL
),
], 'text/plain')
;
}
public function onPreCreate(EntityManagerEvent $event)
{
if (!$this->supports($event->getEntity())) {
return;
}
$this->addScalePerKilometer($event->getEntity());
}
public function onPreUpdate(EntityManagerEvent $event)
{
if (!$this->supports($event->getEntity())) {
return;
}
$this->addScalePerKilometer($event->getEntity());
}
protected function addScalePerKilometer(EntityInterface $entity)
{
if (null === $entity->getScalePerKilometer() || 0.0 === $entity->getScalePerKilometer()) {
$scale = $this->manager->get('expense_report_scale_per_kilometer');
$entity->setScalePerKilometer($scale->getValue());
}
}
}

View file

@ -5,8 +5,9 @@ namespace App\EventSubscriber;
use App\Core\Event\Setting\SettingEvent;
use App\Core\EventSubscriber\SettingEventSubscriber as EventSubscriber;
use App\Core\Setting\SettingManager;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Range;
/**
* class SettingEventSubscriber.
@ -24,8 +25,12 @@ class SettingEventSubscriber extends EventSubscriber
public function onInit(SettingEvent $event)
{
// $this->manager->init('myapp_foo', 'My app', 'Foo', 'Default value');
// $this->manager->init('myapp_bar', 'My app', 'Bar', true);
$this->manager->init(
'expense_report_scale_per_kilometer',
'Note de frais',
'Barème au kilomètre',
0
);
}
public function onFormInit(SettingEvent $event)
@ -34,25 +39,24 @@ class SettingEventSubscriber extends EventSubscriber
$builder = $data['builder'];
$entity = $data['entity'];
// if ('myapp_foo' === $entity->getCode()) {
// $builder->add(
// 'value',
// TextType::class,
// [
// 'label' => $entity->getLabel(),
// ]
// );
// }
//
// if ('myapp_bar' === $entity->getCode()) {
// $builder->add(
// 'value',
// CheckboxType::class,
// [
// 'label' => $entity->getLabel(),
// 'required' => false,
// ]
// );
// }
if ('expense_report_scale_per_kilometer' === $entity->getCode()) {
$builder->add(
'value',
NumberType::class,
[
'html5' => true,
'required' => true,
'label' => $entity->getLabel(),
'scale' => 2,
'attr' => [
'step' => 0.01,
],
'constraints' => [
new NotBlank(),
new Range(['min' => 0]),
],
]
);
}
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace App\Factory;
use App\Core\Factory\FactoryInterface;
use App\Entity\ExpenseReport as Entity;
use App\Entity\User;
class ExpenseReportFactory implements FactoryInterface
{
public function create(User $user): Entity
{
$entity = new Entity();
$entity->setUser($user);
return $entity;
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Core\Form\FileManager\FilePickerType;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class ExpenseReportDeleteBillType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$choices = [];
foreach ($options['bills'] as $bill) {
$choices[basename($bill)] = $bill;
}
$builder->add(
'bills',
ChoiceType::class,
[
'label' => false,
'required' => false,
'multiple' => true,
'choices' => $choices,
'expanded' => true,
]
);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'bills' => [],
]);
}
}

View file

@ -0,0 +1,107 @@
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Range;
class ExpenseReportMoveType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('date', DateType::class, [
'html5' => true,
'required' => true,
'widget' => 'single_text',
'row_attr' => [
'class' => 'col-md-6 pr-1',
],
'constraints' => [
new NotBlank(),
],
])
->add('event', TextType::class, [
'row_attr' => [
'class' => 'col-md-6',
],
'constraints' => [
new NotBlank(),
],
])
->add('addressFrom', TextType::class, [
'row_attr' => [
'class' => 'col-md-6 pr-1',
],
'constraints' => [
new NotBlank(),
],
])
->add('addressTo', TextType::class, [
'row_attr' => [
'class' => 'col-md-6',
],
'constraints' => [
new NotBlank(),
],
])
->add('distance', NumberType::class, [
'html5' => true,
'scale' => 0,
'row_attr' => [
'class' => 'col-md-6 pr-1',
],
'constraints' => [
new NotBlank(),
new Range(['min' => 0]),
],
])
->add('isRoundTrip', ChoiceType::class, [
'row_attr' => [
'class' => 'col-md-6',
],
'choices' => [
'Non' => false,
'Oui' => true,
],
'required' => true,
])
->add('highwayPay', NumberType::class, [
'row_attr' => [
'class' => 'col-md-6 pr-1',
],
'attr' => [
'step' => 0.01,
],
'scale' => 2,
'required' => false,
'html5' => true,
])
->add('parkingPay', NumberType::class, [
'row_attr' => [
'class' => 'col-md-6',
],
'attr' => [
'step' => 0.01,
],
'scale' => 2,
'required' => false,
'html5' => true,
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
// Configure your form options here
]);
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Core\Form\FileManager\FilePickerType;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Form\Extension\Core\Type\FileType;
class ExpenseReportNewBillType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('file', FileType::class, [
'required' => true,
'label' => false,
'row_attr' => [
'class' => 'col-md-12',
],
'constraints' => [
new NotBlank(),
],
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
// Configure your form options here
]);
}
}

View file

@ -0,0 +1,125 @@
<?php
namespace App\Form;
use App\Core\Form\Type\CollectionType;
use App\Entity\ExpenseReport;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ExpenseReportType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('dateFrom', null, [
'html5' => true,
'widget' => 'single_text',
])
->add('dateTo', null, [
'html5' => true,
'widget' => 'single_text',
])
->add('moves', CollectionType::class, [
'entry_type' => ExpenseReportMoveType::class,
'collection_name' => 'moves',
'prototype' => true,
'allow_add' => true,
'allow_delete' => true,
'row_attr' => [
'class' => 'mb-3 pb-3 pl-3 pr-3 pt-1 bg-light',
],
'label_attr' => [
'class' => 'font-weight-bold',
],
'attr' => [
'class' => 'mb-3 row',
],
])
->add('variousPayments', CollectionType::class, [
'entry_type' => ExpenseReportVariousPaymentType::class,
'collection_name' => 'variousPayments',
'prototype' => true,
'allow_add' => true,
'allow_delete' => true,
'row_attr' => [
'class' => 'mb-3 pb-3 pl-3 pr-3 pt-1 bg-light',
],
'label_attr' => [
'class' => 'font-weight-bold',
],
'attr' => [
'class' => 'mb-3 row',
],
])
;
if ($builder->getData()->getId()) {
$builder
->add('newBills', CollectionType::class, [
'entry_type' => ExpenseReportNewBillType::class,
'mapped' => false,
'label' => 'Nouvelles factures',
'collection_name' => 'newBills',
'prototype' => true,
'allow_add' => true,
'allow_delete' => true,
'row_attr' => [
'class' => 'mb-3 pb-3 pl-3 pr-3 pt-1 bg-light',
],
'label_attr' => [
'class' => 'font-weight-bold',
],
'attr' => [
'class' => 'mb-3 row',
],
])
;
}
if (count($builder->getData()->getBills())) {
$builder
->add('deleteBills', ExpenseReportDeleteBillType::class, [
'mapped' => false,
'label' => 'Factures à supprimer',
'bills' => $builder->getData()->getBills(),
'row_attr' => [
'class' => 'mb-3 p-3 bg-light',
],
'label_attr' => [
'class' => 'font-weight-bold',
],
'attr' => [
'class' => 'row mb-3',
],
])
;
}
if ($options['is_treasurer']) {
$builder
->add('isRequestedPayment', CheckboxType::class, [
'required' => false,
])
->add('isPaid', CheckboxType::class, [
'required' => false,
])
->add('paidAt', null, [
'required' => false,
'html5' => true,
'widget' => 'single_text',
])
;
}
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => ExpenseReport::class,
'is_treasurer' => false,
]);
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Range;
class ExpenseReportVariousPaymentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('date', DateType::class, [
'html5' => true,
'required' => true,
'widget' => 'single_text',
'row_attr' => [
'class' => 'col-md-4 pr-1',
],
'constraints' => [
new NotBlank(),
],
])
->add('label', TextType::class, [
'row_attr' => [
'class' => 'col-md-4 pr-1',
],
'constraints' => [
new NotBlank(),
],
])
->add('amount', NumberType::class, [
'required' => true,
'html5' => true,
'row_attr' => [
'class' => 'col-md-4 pr-1',
],
'constraints' => [
new NotBlank(),
new Range(['min' => 0]),
],
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
// Configure your form options here
]);
}
}

View file

@ -25,5 +25,17 @@ class UserType extends BaseUserType
]
);
$builder->add(
'isTreasurer',
CheckboxType::class,
[
'label' => 'Trésorier⋅ière',
'required' => false,
'attr' => [
],
'constraints' => [
],
]
);
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace App\Repository;
use App\Entity\ExpenseReport;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\OptimisticLockException;
use Doctrine\ORM\ORMException;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method ExpenseReport|null find($id, $lockMode = null, $lockVersion = null)
* @method ExpenseReport|null findOneBy(array $criteria, array $orderBy = null)
* @method ExpenseReport[] findAll()
* @method ExpenseReport[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ExpenseReportRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ExpenseReport::class);
}
/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function add(ExpenseReport $entity, bool $flush = true): void
{
$this->_em->persist($entity);
if ($flush) {
$this->_em->flush();
}
}
/**
* @throws ORMException
* @throws OptimisticLockException
*/
public function remove(ExpenseReport $entity, bool $flush = true): void
{
$this->_em->remove($entity);
if ($flush) {
$this->_em->flush();
}
}
// /**
// * @return ExpenseReport[] Returns an array of ExpenseReport objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('e')
->andWhere('e.exampleField = :val')
->setParameter('val', $value)
->orderBy('e.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?ExpenseReport
{
return $this->createQueryBuilder('e')
->andWhere('e.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View file

@ -0,0 +1,23 @@
<?php
namespace App\Repository;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use App\Repository\ExpenseReportRepository as Repository;
use App\Entity\User;
class ExpenseReportRepositoryQuery extends RepositoryQuery
{
public function __construct(Repository $repository, PaginatorInterface $paginator)
{
parent::__construct($repository, 'e', $paginator);
}
public function filterByUser(User $user): self
{
return $this
->andWhere('.user = :user')
->setParameter('user', $user->getId());
}
}

View file

@ -0,0 +1,16 @@
<div class="row">
<div class="col-md-3 order-2 pl-md-3">
{% for item in ['dateFrom', 'dateTo', 'isRequestedPayment', 'isPaid', 'paidAt', 'newBills', 'deleteBills'] %}
{% if form[item] is defined %}
{% include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) with {form: form[item]} %}
{% endif %}
{% endfor %}
</div>
<div class="col-md-9 order-1 pr-md-3">
{% for item in ['moves', 'variousPayments', 'newBills'] %}
{% if form[item] is defined %}
{% include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) with {form: form[item]} %}
{% endif %}
{% endfor %}
</div>
</div>

View file

@ -0,0 +1,117 @@
{% set total = 0 %}
{% if entity.moves|length %}
<div class="table-responsive">
<table class="table mb-0">
<thead class="bg-light">
<tr>
<th width="100">Date</th>
<th>Évènement</th>
<th>Trajet</th>
<th width="120" class="text-right">Distance</th>
<th width="120">Aller-retour</th>
<th width="90" class="text-right">Péage(s)</th>
<th width="90" class="text-right">Parkink(s)</th>
<th width="90" class="text-right">Montant&nbsp;TTC</th>
</tr>
</thead>
{% for item in entity.moves %}
<tr>
<td>
{{ item.date|date('d/m/Y') }}
</td>
<td>
{{ item.event }}
</td>
<td>
{{ item.addressFrom }}<br>
{{ item.addressTo }}
</td>
<td class="text-right">
{{ item.distance }}Km
</td>
<td>
{{ item.isRoundTrip ? 'oui' : 'non' }}
</td>
<td class="text-right">
{{ item.highwayPay > 0 ? (item.highwayPay ~ '€') : '-' }}
</td>
<td class="text-right">
{{ item.parkingPay > 0 ? (item.parkingPay ~ '€') : '-' }}
</td>
<td class="text-right">
{% set itemTotal = item.parkingPay + item.highwayPay + (item.distance * entity.scalePerKilometer * (item.isRoundTrip ? 2 : 1)) %}
{% set total = total + itemTotal %}
{{ itemTotal }}
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% if entity.moves|length %}
<div class="table-responsive">
<table class="table mb-0">
<thead class="bg-light">
<tr>
<th width="100">Date</th>
<th>Libellé</th>
<th width="90" class="text-right">Montant&nbsp;TTC</th>
</tr>
</thead>
{% for item in entity.variousPayments %}
<tr>
<td>
{{ item.date|date('d/m/Y') }}
</td>
<td>
{{ item.label }}
</td>
<td class="text-right">
{% set total = total + item.amount %}
{{ item.amount ~ '€' }}
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
<div class="table-responsive">
<table class="table">
<tr>
<td class="font-weight-bold">
Facture(s)
</td>
<td class="text-right"><span class="font-weight-bold mr-3">Total</span> {{ total }}€</td>
</tr>
<tr>
<td>
{% for item in entity.bills %}
<div class="mb-1">
<a class="btn btn-light border" href="{{ asset(item) }}" target="_blank">{{ item|split('/')|last }}</a>
</div>
{% else %}
-
{% endfor %}
</td>
<td width="250" class="text-right">
{% if entity.isPaid %}
{% if entity.paidAt %}
<span class="btn btn-success">Payée le {{ entity.paidAt|date('d/m/Y à H:i') }}</span>
{% else %}
<span class="btn btn-success">Payée</span>
{% endif %}
{% elseif entity.isRequestedPayment %}
<span class="btn btn-warning">En attente de paiement</span>
{% else %}
<span class="btn btn-light">Brouillon</span>
{% endif %}
</td>
</tr>
</table>
</div>

View file

@ -0,0 +1,27 @@
{% extends '@Core/admin/crud/edit.html.twig' %}
{% block header_actions_after %}
{% if not entity.isPaid %}
{% if not entity.isRequestedPayment and (entity.user.id == app.user.id or is_granted('ROLE_TREASURER')) %}
{% set url = safe_path(configuration.pageRoute('requestPayment'), {
entity: entity.id,
token: csrf_token('request_payment')
}) %}
<a class="btn btn-success" href="{{ url }}" onclick="return confirm('Avez-vous bien enregistré vos modification ?')">
Demander le paiement
</a>
{% endif %}
{% if entity.isRequestedPayment and is_granted('ROLE_TREASURER') %}
{% set url = safe_path(configuration.pageRoute('paid'), {
entity: entity.id,
token: csrf_token('paid')
}) %}
<a class="btn btn-success" href="{{ url }}">
Définir comme payée
</a>
{% endif %}
{% endif %}
{% endblock %}

View file

@ -62,4 +62,15 @@
icon: 'fa fa-list-alt'
}) }}
</ul>
{{ include('@Core/admin/module/_menu_section.html.twig', {label: 'Divers'}) }}
<ul class="nav flex-column">
{{ include('@Core/admin/module/_menu_item.html.twig', {
id: 'expense_report',
label: 'Notes de frais',
route: path('admin_expense_report_index'),
icon: 'fa fa-credit-card'
}) }}
</ul>
{% endif %}

View file

@ -1,7 +1,7 @@
<div class="row">
<div class="col-12 p-3">
<div class="row">
{% for item in ['displayName', 'email', 'isAdmin', 'isWriter', 'isManager'] %}
{% for item in ['displayName', 'email', 'isAdmin', 'isWriter', 'isManager', 'isTreasurer'] %}
<div class="col-12">
{{ form_row(form[item]) }}
</div>

View file

@ -0,0 +1,27 @@
<div class="row">
<div class="col-12 p-3">
<ul class="list-group">
<li class="list-group-item">
<span class="font-weight-bold pb-2 d-block">{{ 'Display name'|trans }}</span>
{{ entity.displayName }}
</li>
<li class="list-group-item">
<span class="font-weight-bold pb-2 d-block">{{ 'E-mail'|trans }}</span>
{{ entity.email }}
</li>
<li class="list-group-item">
<span class="font-weight-bold pb-2 d-block">{{ 'Permissions'|trans }}</span>
{{ entity.roles|join('<br>')|replace({
ROLE_USER: 'User'|trans,
ROLE_WRITER: 'Writer'|trans,
ROLE_ADMIN: 'Administrator'|trans,
ROLE_TREASURER: 'ROLE_TREASURER'|trans,
ROLE_MANAGER: 'ROLE_MANAGER'|trans,
})|raw }}
</li>
</ul>
</div>
</div>

View file

@ -0,0 +1,4 @@
Le paiement pour la note de frais #{{ entity.id }} a été réalisé :
{{ show_url }}

View file

@ -0,0 +1,4 @@
Une demande de paiement a été réalisée pour la note de frais #{{ entity.id }} :
{{ show_url }}

View file

@ -1,3 +1,5 @@
"ROLE_MANAGER": "Gestionnaire"
"ROLE_TREASURER": "Trésorier⋅ière"
"Address": "Adresse"
"Zip code": "Code postal"
"City": "Ville"
@ -26,3 +28,19 @@
"Caldav password": "Mot de passe du serveur Caldav"
"Caldav calendar uri": "URI du calendrier Caldav"
"Color": "Couleur"
"Moves": "Trajets"
"Highway pay": "Frais de péage (TTC)"
"Parking pay": "Frais de parking (TTC)"
"Various payments": "Paiements divers"
"Bills": "Factures"
"Date from": "Date de début"
"Date to": "Date de fin"
"Is paid": "Est payé(e)"
"Is requested payment": "Paiement demandé"
"Paid at": "Payé(e) le"
"Event": "Évènement"
"Address from": "Adresse de départ"
"Address to": "Adresse d'arrivée"
"Distance": "Distance (sans retour)"
"Is round trip": "Aller-retour"
"Amount": "Montant (TTC)"

378
yarn.lock
View file

@ -3,11 +3,12 @@
"@ampproject/remapping@^2.1.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34"
integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==
version "2.2.0"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
dependencies:
"@jridgewell/trace-mapping" "^0.3.0"
"@jridgewell/gen-mapping" "^0.1.0"
"@jridgewell/trace-mapping" "^0.3.9"
"@babel/code-frame@7.12.11":
version "7.12.11"
@ -922,12 +923,12 @@
integrity sha512-GKsCFPk85vH5FuCuVQ48NTLc9hk0T3DsBH9zABaicTYIJayFcUa8N4/Y+L3i4tduzDqqyvoxkv+5n43GmC5gEA==
"@editorjs/editorjs@^2.23.2":
version "2.23.2"
resolved "https://registry.yarnpkg.com/@editorjs/editorjs/-/editorjs-2.23.2.tgz#a4d3e60de8052602233750eda353596c09131a90"
integrity sha512-SXWQjxE/ZO6zWvo83U+gfkM9LVJzYOtNkttyXkdfyYALVWiENyxtlej/Jmd1TZ4yWpKwQEICPXz0ceGkzKnJyQ==
version "2.24.1"
resolved "https://registry.yarnpkg.com/@editorjs/editorjs/-/editorjs-2.24.1.tgz#73125dd164c6e89f1ed8c1fb425a660a1d58d58f"
integrity sha512-NlYrDXCOSv+qmokD9G+QgQqF9R2kcQYdFl3tQW/askrgjCErh4vPtcgsFC15WFyy6m/ZOXIuiUZjs/oLfGSc1Q==
dependencies:
codex-notifier "^1.1.2"
codex-tooltip "^1.0.4"
codex-tooltip "^1.0.5"
nanoid "^3.1.22"
"@editorjs/header@^2.6.2":
@ -946,9 +947,9 @@
integrity sha512-q4Z4fAGUQVItTZT5uQ8/QcNCDq5VYMqDZ0NJpEiWSHSnX/Cq2/TUI6IBSSkuIdEg97Vs7gCjyRRhHVbneOgM6Q==
"@editorjs/link@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@editorjs/link/-/link-2.4.0.tgz#f54e41854e2453116999da985b745a182db14069"
integrity sha512-Dk5n4HkiXtZvhDIEBu7uiXhkPl2lhmMFW8FaUnOiERZHJzIZGQf/qe6GdFN6Bo51j/wVADVyq6m8ZylKwIiCVA==
version "2.4.1"
resolved "https://registry.yarnpkg.com/@editorjs/link/-/link-2.4.1.tgz#0fb500b8591068b6deb859dcfff4ec47b02f2dba"
integrity sha512-AqudulnZT/E5RO7itgentDedX25PGGvNmkbkdUXn8SuMKeYzfZ0hWAcgKLNNQHHCHm59F6AQz7JcXwfQuYhxyg==
dependencies:
"@babel/runtime" "^7.10.2"
@ -978,14 +979,14 @@
integrity sha512-IWOBWjL2ngPP63GcIAltyD9kc7OVZFma4kS+T5JRHvKKDspYsnmrxsbRmCPc+coZQzqPxXHkiOZuNMdmGX/Y3w==
"@editorjs/raw@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@editorjs/raw/-/raw-2.3.0.tgz#d0c3daa1144e38b4d92bc5eca2faebba337bfae7"
integrity sha512-wC+3pTQVB2hvDcF4mcMgOr7DhO432lGYqOam/CwfyyfG5BDqaPrwgZTiVyteZ25D5ZQD677IVYZ48oX33+2HgQ==
version "2.3.1"
resolved "https://registry.yarnpkg.com/@editorjs/raw/-/raw-2.3.1.tgz#3788d407cab83e1305545fc04b492c27ac117b7f"
integrity sha512-pMPsgzlmdMGR1A2K9Opky5JimO/YC71GGSAYvAlLNGFZZ+2I9K/64jo7MS6E0g9WVks2JRI+X5CF8qk2V9Ok2A==
"@editorjs/table@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@editorjs/table/-/table-2.0.1.tgz#ec7726b7b662ed4ddbcd023e9a4644fbb6ea73b8"
integrity sha512-PB8VM+GPRwGy7IlF+WrEQw2A2c36xEXBnYIvf2VGNJo8A7PjYHtuWrlyHHCnGpY4lHXYnavZ/U8pKAfXv86XjA==
version "2.0.2"
resolved "https://registry.yarnpkg.com/@editorjs/table/-/table-2.0.2.tgz#eb4aa3a7428bb23c95fa6bb3251a4aaa44ab3fb9"
integrity sha512-Ws5c9kmYsbWTBJ1WKSZRN+ATrqFFzjf/zggVu8AI/mVcNvSYUiEVqibLtbh5RLHiDGkELRZ9Bw5PQfVCg7yuBw==
"@editorjs/underline@^1.0.0":
version "1.0.0"
@ -1040,17 +1041,30 @@
update-notifier "^2.2.0"
yargs "^8.0.2"
"@jridgewell/gen-mapping@^0.1.0":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
dependencies:
"@jridgewell/set-array" "^1.0.0"
"@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/resolve-uri@^3.0.3":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c"
integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==
version "3.0.6"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz#4ac237f4dabc8dd93330386907b97591801f7352"
integrity sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw==
"@jridgewell/set-array@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.0.tgz#1179863356ac8fbea64a5a4bcde93a4871012c01"
integrity sha512-SfJxIxNVYLTsKwzB3MoOQ1yxf4w/E6MdkvTgrgAt1bfxjSrLUoHMKrDOykwN14q65waezZIdqDneUIPh4/sKxg==
"@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.11"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec"
integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==
"@jridgewell/trace-mapping@^0.3.0":
"@jridgewell/trace-mapping@^0.3.9":
version "0.3.9"
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
@ -1248,9 +1262,9 @@
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
"@types/node@*":
version "17.0.25"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.25.tgz#527051f3c2f77aa52e5dc74e45a3da5fb2301448"
integrity sha512-wANk6fBrUwdpY4isjWrKTufkrXdu1D2YHCot2fD/DfWxF5sMrVSA+KN7ydckvaTCh0HiqX9IVl0L5/ZoXg5M7w==
version "17.0.30"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.30.tgz#2c6e8512acac70815e8176aa30c38025067880ef"
integrity sha512-oNBIZjIqyHYP8VCNAV9uEytXVeXG2oR0w9lgAXro20eugRQfY002qr3CUl6BAe+Yf/z3CRjPdz27Pu6WWtuSRw==
"@types/qs@*":
version "6.9.7"
@ -1262,10 +1276,10 @@
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
"@types/retry@^0.12.0":
version "0.12.1"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065"
integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==
"@types/retry@0.12.0":
version "0.12.0"
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
"@types/serve-index@^1.9.1":
version "1.9.1"
@ -1615,9 +1629,9 @@ acorn@^7.1.1, acorn@^7.4.0:
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.0.5, acorn@^8.4.1, acorn@^8.5.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
version "8.7.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
adjust-sourcemap-loader@3.0.0:
version "3.0.0"
@ -2011,26 +2025,28 @@ bluebird@^3.1.1, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5:
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
body-parser@1.19.2:
version "1.19.2"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e"
integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==
body-parser@1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5"
integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==
dependencies:
bytes "3.1.2"
content-type "~1.0.4"
debug "2.6.9"
depd "~1.1.2"
http-errors "1.8.1"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "~2.3.0"
qs "6.9.7"
raw-body "2.4.3"
on-finished "2.4.1"
qs "6.10.3"
raw-body "2.5.1"
type-is "~1.6.18"
unpipe "1.0.0"
bonjour-service@^1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.11.tgz#5418e5c1ac91c89a406f853a942e7892829c0d89"
integrity sha512-drMprzr2rDTCtgEE3VgdA9uUFaUHF+jXduwYSThHJnKMYM+FhI9Z3ph+TX3xy0LtgYHae6CHYPJ/2UnK8nQHcA==
version "1.0.12"
resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.12.tgz#28fbd4683f5f2e36feedb833e24ba661cac960c3"
integrity sha512-pMmguXYCu63Ug37DluMKEHdxc+aaIf/ay4YbF8Gxtba+9d3u+rmEWy61VK3Z3hp8Rskok3BunHYnG0dUHAsblw==
dependencies:
array-flatten "^2.1.2"
dns-equal "^1.0.0"
@ -2075,15 +2091,15 @@ braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.20.2:
version "4.20.2"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88"
integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==
browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.20.3:
version "4.20.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf"
integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==
dependencies:
caniuse-lite "^1.0.30001317"
electron-to-chromium "^1.4.84"
caniuse-lite "^1.0.30001332"
electron-to-chromium "^1.4.118"
escalade "^3.1.1"
node-releases "^2.0.2"
node-releases "^2.0.3"
picocolors "^1.0.0"
buffer-from@^1.0.0:
@ -2180,10 +2196,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001317:
version "1.0.30001332"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd"
integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001332:
version "1.0.30001334"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001334.tgz#892e9965b35285033fc2b8a8eff499fe02f13d8b"
integrity sha512-kbaCEBRRVSoeNs74sCuq92MJyGrMtjWVfhltoHUCW4t4pXFvGjUBrfo47weBRViHkiV3eBYyIsfl956NtHGazw==
capture-stack-trace@^1.0.0:
version "1.0.1"
@ -2386,7 +2402,7 @@ codex-notifier@^1.1.2:
resolved "https://registry.yarnpkg.com/codex-notifier/-/codex-notifier-1.1.2.tgz#a733079185f4c927fa296f1d71eb8753fe080895"
integrity sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==
codex-tooltip@^1.0.4:
codex-tooltip@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/codex-tooltip/-/codex-tooltip-1.0.5.tgz#ba25fd5b3a58ba2f73fd667c2b46987ffd1edef2"
integrity sha512-IuA8LeyLU5p1B+HyhOsqR6oxyFQ11k3i9e9aXw40CrHFTRO2Y1npNBVU3W1SvhKAbUU7R/YikUBdcYFP0RcJag==
@ -2580,10 +2596,10 @@ cookie-signature@1.0.6:
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
cookie@0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
cookie@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
copy-concurrently@^1.0.0:
version "1.0.5"
@ -2598,17 +2614,17 @@ copy-concurrently@^1.0.0:
run-queue "^1.0.0"
core-js-compat@^3.20.2, core-js-compat@^3.21.0:
version "3.22.2"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.2.tgz#eec621eb276518efcf718d0a6d9d042c3d0cad48"
integrity sha512-Fns9lU06ZJ07pdfmPMu7OnkIKGPKDzXKIiuGlSvHHapwqMUF2QnnsWwtueFZtSyZEilP0o6iUeHQwpn7LxtLUw==
version "3.22.3"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.3.tgz#9b10d786052d042bc97ee8df9c0d1fb6a49c2005"
integrity sha512-wliMbvPI2idgFWpFe7UEyHMvu6HWgW8WA+HnDRtgzoSDYvXFMpoGX1H3tPDDXrcfUSyXafCLDd7hOeMQHEZxGw==
dependencies:
browserslist "^4.20.2"
browserslist "^4.20.3"
semver "7.0.0"
core-js@^3.0.0:
version "3.22.2"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.2.tgz#3ea0a245b0895fa39d1faa15fe75d91ade504a01"
integrity sha512-Z5I2vzDnEIqO2YhELVMFcL1An2CIsFe9Q7byZhs8c/QxummxZlAHw33TUHbIte987LkisOgL0LwQ1P9D6VISnA==
version "3.22.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.3.tgz#498c41d997654cb00e81c7a54b44f0ab21ab01d5"
integrity sha512-1t+2a/d2lppW1gkLXx3pKPVGbBdxXAkqztvWb1EJ8oF8O2gIGiytzflNiFEehYwVK/t2ryUsGBoOFFvNx95mbg==
core-util-is@1.0.2:
version "1.0.2"
@ -2940,15 +2956,20 @@ delegates@^1.0.0:
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
depd@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
detect-indent@~5.0.0:
version "5.0.0"
@ -3142,10 +3163,10 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.4.84:
version "1.4.117"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.117.tgz#829d747deb9faa653cab72764a891ef523ba7413"
integrity sha512-ypZHxY+Sf/PXu7LVN+xoeanyisnJeSOy8Ki439L/oLueZb4c72FI45zXcK3gPpmTwyufh9m6NnbMLXnJh/0Fxg==
electron-to-chromium@^1.4.118:
version "1.4.127"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.127.tgz#4ef19d5d920abe2676d938f4170729b44f7f423a"
integrity sha512-nhD6S8nKI0O2MueC6blNOEZio+/PWppE/pevnf3LOlQA/fKPCrDp2Ao4wx4LFwmIkJpVdFdn2763YWLy9ENIZg==
emoji-regex@^7.0.1:
version "7.0.3"
@ -3594,37 +3615,38 @@ execa@^5.0.0:
strip-final-newline "^2.0.0"
express@^4.17.3:
version "4.17.3"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1"
integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==
version "4.18.0"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.0.tgz#7a426773325d0dd5406395220614c0db10b6e8e2"
integrity sha512-EJEXxiTQJS3lIPrU1AE2vRuT7X7E+0KBbpm5GSoK524yl0K8X+er8zS2P14E64eqsVNoWbMCT7MpmQ+ErAhgRg==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.19.2"
body-parser "1.20.0"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.4.2"
cookie "0.5.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "~1.1.2"
depd "2.0.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "~1.1.2"
finalhandler "1.2.0"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
proxy-addr "~2.0.7"
qs "6.9.7"
qs "6.10.3"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.17.2"
serve-static "1.14.2"
send "0.18.0"
serve-static "1.15.0"
setprototypeof "1.2.0"
statuses "~1.5.0"
statuses "2.0.1"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
@ -3722,17 +3744,17 @@ filter-obj@^1.1.0:
resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs=
finalhandler@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
finalhandler@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "~2.3.0"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "~1.5.0"
statuses "2.0.1"
unpipe "~1.0.0"
find-cache-dir@^3.3.1:
@ -4173,7 +4195,7 @@ har-validator@~5.1.3:
ajv "^6.12.3"
har-schema "^2.0.0"
has-bigints@^1.0.1:
has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
@ -4296,15 +4318,15 @@ http-deceiver@^1.2.7:
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=
http-errors@1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c"
integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "~1.1.2"
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses ">= 1.5.0 < 2"
statuses "2.0.1"
toidentifier "1.0.1"
http-errors@~1.6.2:
@ -5337,6 +5359,11 @@ lodash.some@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"
integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=
lodash.sortby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
lodash.truncate@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
@ -5755,10 +5782,10 @@ node-notifier@^9.0.0:
uuid "^8.3.0"
which "^2.0.2"
node-releases@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96"
integrity sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw==
node-releases@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476"
integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==
nopt@^4.0.1, nopt@^4.0.3:
version "4.0.3"
@ -5909,9 +5936,9 @@ npm-user-validate@^1.0.1:
integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==
npm@^6.14.5:
version "6.14.16"
resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.16.tgz#a882d6b0b32d5212461f0c58719152add1a7b99a"
integrity sha512-LMiLGYsVNJfVPlQg7v2NYjG7iRIapcLv+oMunlq7fkXVx0BATCjRu7XyWl0G+iuZzHy4CjtM32QB8ox8juTgaw==
version "6.14.17"
resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.17.tgz#932cd2df5f28db0f13cc487873109d5212acaf83"
integrity sha512-CxEDn1ydVRPDl4tHrlnq+WevYAhv4GF2AEHzJKQ4prZDZ96IS3Uo6t0Sy6O9kB6XzqkI+J00WfYCqqk0p6IJ1Q==
dependencies:
JSONStream "^1.3.5"
abbrev "~1.1.1"
@ -6111,10 +6138,10 @@ obuf@^1.0.0, obuf@^1.1.2:
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
@ -6243,11 +6270,11 @@ p-map@^2.0.0:
integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
p-retry@^4.5.0:
version "4.6.1"
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.1.tgz#8fcddd5cdf7a67a0911a9cf2ef0e5df7f602316c"
integrity sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==
version "4.6.2"
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==
dependencies:
"@types/retry" "^0.12.0"
"@types/retry" "0.12.0"
retry "^0.13.1"
p-try@^1.0.0:
@ -6857,10 +6884,12 @@ qrcodejs@^1.0.0:
resolved "https://registry.yarnpkg.com/qrcodejs/-/qrcodejs-1.0.0.tgz#afab5e9e858521f859ae336d2ed0f9fd2e76cca7"
integrity sha1-r6tenoWFIfhZrjNtLtD5/S52zKc=
qs@6.9.7:
version "6.9.7"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe"
integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==
qs@6.10.3:
version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
dependencies:
side-channel "^1.0.4"
qs@~6.5.2:
version "6.5.3"
@ -6894,13 +6923,13 @@ range-parser@^1.2.1, range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c"
integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==
raw-body@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
dependencies:
bytes "3.1.2"
http-errors "1.8.1"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
@ -7296,9 +7325,9 @@ sass-loader@^11.0.0:
neo-async "^2.6.2"
sass@^1.49.7:
version "1.50.1"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.50.1.tgz#e9b078a1748863013c4712d2466ce8ca4e4ed292"
integrity sha512-noTnY41KnlW2A9P8sdwESpDmo+KBNkukI1i8+hOK3footBUcohNHtdOJbckp46XO95nuvcHDDZ+4tmOnpK3hjw==
version "1.51.0"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.51.0.tgz#25ea36cf819581fe1fe8329e8c3a4eaaf70d2845"
integrity sha512-haGdpTgywJTvHC2b91GSq+clTKGbtkkZmVAb82jZQN/wTy6qs8DdFm2lhEQbEwrY0QDRgSQ3xDurqM977C3noA==
dependencies:
chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0"
@ -7373,24 +7402,24 @@ semver@^7.2.1, semver@^7.3.2, semver@^7.3.5:
dependencies:
lru-cache "^6.0.0"
send@0.17.2:
version "0.17.2"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820"
integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==
send@0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
dependencies:
debug "2.6.9"
depd "~1.1.2"
destroy "~1.0.4"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "1.8.1"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "~2.3.0"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "~1.5.0"
statuses "2.0.1"
serialize-javascript@^5.0.1:
version "5.0.1"
@ -7419,15 +7448,15 @@ serve-index@^1.9.1:
mime-types "~2.1.17"
parseurl "~1.3.2"
serve-static@1.14.2:
version "1.14.2"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa"
integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==
serve-static@1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.17.2"
send "0.18.0"
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
@ -7617,11 +7646,18 @@ source-map@^0.5.0:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.7.3, source-map@~0.7.2:
source-map@^0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
source-map@~0.8.0-beta.0:
version "0.8.0-beta.0"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==
dependencies:
whatwg-url "^7.0.0"
spdx-correct@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
@ -7718,7 +7754,12 @@ stackframe@^1.1.1:
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.1.tgz#1033a3473ee67f08e2f2fc8eba6aef4f845124e1"
integrity sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==
"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0:
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
"statuses@>= 1.4.0 < 2":
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
@ -8007,13 +8048,13 @@ terser-webpack-plugin@^5.1.1, terser-webpack-plugin@^5.1.3:
terser "^5.7.2"
terser@^5.7.2:
version "5.12.1"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.12.1.tgz#4cf2ebed1f5bceef5c83b9f60104ac4a78b49e9c"
integrity sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==
version "5.13.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-5.13.0.tgz#d43fd71861df1b4df743980caa257c6fa03acc44"
integrity sha512-sgQ99P+fRBM1jAYzN9RTnD/xEWx/7LZgYTCRgmYriSq1wxxqiQPJgXkkLBBuwySDWJ2PP0PnVQyuf4xLUuH4Ng==
dependencies:
acorn "^8.5.0"
commander "^2.20.0"
source-map "~0.7.2"
source-map "~0.8.0-beta.0"
source-map-support "~0.5.20"
text-table@^0.2.0, text-table@~0.2.0:
@ -8050,9 +8091,9 @@ tiny-relative-date@^1.3.0:
integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==
tinymce@^5.7.1:
version "5.10.3"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-5.10.3.tgz#9485cf00159fd70c4948cb5e9dd49bce2a775899"
integrity sha512-O59ssHNnujWvSk5Gt8hIGrdNCMKVWVQv9F8siAgLTRgTh0t3NDHrP1UlLtCxArUi9DPWZvlBeUz8D5fJTu7vnA==
version "5.10.4"
resolved "https://registry.yarnpkg.com/tinymce/-/tinymce-5.10.4.tgz#24ee843c7648ade708605dec15d8dad07809f7db"
integrity sha512-L0ivAhGu7bEo6cUBrCzhtKlkIQqG2sTcL+uu7soMSxrECQIC5VwUnzp9HCEf+fRl36q6zavLV48lf8jelj+gXA==
tmp@^0.2.1:
version "0.2.1"
@ -8086,6 +8127,13 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tr46@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
dependencies:
punycode "^2.1.0"
tsconfig-paths@^3.14.1:
version "3.14.1"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
@ -8154,13 +8202,13 @@ umask@^1.1.0, umask@~1.1.0:
integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=
unbox-primitive@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
dependencies:
function-bind "^1.1.1"
has-bigints "^1.0.1"
has-symbols "^1.0.2"
call-bind "^1.0.2"
has-bigints "^1.0.2"
has-symbols "^1.0.3"
which-boxed-primitive "^1.0.2"
underscore.string@^3.3.5:
@ -8177,9 +8225,9 @@ underscore.string@^3.3.5:
integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=
underscore@>=1.7.0, underscore@>=1.8.3, underscore@^1.11.0, underscore@^1.13.1:
version "1.13.2"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.2.tgz#276cea1e8b9722a8dbed0100a407dda572125881"
integrity sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==
version "1.13.3"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.3.tgz#54bc95f7648c5557897e5e968d0f76bc062c34ee"
integrity sha512-QvjkYpiD+dJJraRA8+dGAU4i7aBbb2s0S3jA45TFOvg2VgqvdCDd/3N6CqA8gluk1W91GLoXg5enMUx560QzuA==
unicode-canonical-property-names-ecmascript@^2.0.0:
version "2.0.0"
@ -8448,6 +8496,11 @@ web-resource-inliner@^4.2.1:
valid-data-url "^2.0.0"
xtend "^4.0.2"
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
webpack-cli@^4.9.1:
version "4.9.2"
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.2.tgz#77c1adaea020c3f9e2db8aad8ea78d235c83659d"
@ -8585,6 +8638,15 @@ websocket-extensions@>=0.1.1:
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
whatwg-url@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
dependencies:
lodash.sortby "^4.7.0"
tr46 "^1.0.1"
webidl-conversions "^4.0.2"
which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"