add conferences, debriefings, projects, theme types, establishment groups

This commit is contained in:
Simon Vieille 2022-04-15 16:45:58 +02:00
parent dcefee3d38
commit 2a1a7d40f5
Signed by: deblan
GPG key ID: 03383D15A1D31745
18 changed files with 3198 additions and 546 deletions

4
.env
View file

@ -19,8 +19,8 @@ APP_SECRET=
###< symfony/framework-bundle ### ###< symfony/framework-bundle ###
###> symfony/mailer ### ###> symfony/mailer ###
MAILER_DSN=smtp://mail.deblan.org:1666?verify_peer=false MAILER_DSN=smtp://localhost
MAILER_FROM=webtinternetsuiviinter@deblan.fr MAILER_FROM=contact@example.com
###< symfony/mailer ### ###< symfony/mailer ###
###> doctrine/doctrine-bundle ### ###> doctrine/doctrine-bundle ###

View file

@ -1,5 +1,6 @@
import '../../vendor/murph/murph-core/src/core/Resources/assets/js/admin.js' import '../../vendor/murph/murph-core/src/core/Resources/assets/js/admin.js'
const Sortable = require('sortablejs').Sortable
const Routing = require('../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js') const Routing = require('../../vendor/friendsofsymfony/jsrouting-bundle/Resources/public/js/router.min.js')
const routes = require('../../public/js/fos_js_routes.json') const routes = require('../../public/js/fos_js_routes.json')
@ -83,4 +84,28 @@ class AddressAutocomplete {
} }
} }
const addressAutocomplete = new AddressAutocomplete() class FilesCollectionSorter {
constructor () {
const collections = document.querySelectorAll('div[data-collection^="collection-files"]')
for (const collection of collections) {
return new Sortable(collection, {
handle: '*[data-collection-item]',
sort: true,
animation: 150,
fallbackTolerance: 3,
onEnd: (e) => {
const positions = collection.querySelectorAll('*[data-collection-item] input[type=hidden]')
console.log(positions);
for (let u = 0; u < positions.length; u++) {
positions[u].value = u
}
}
})
}
}
}
new AddressAutocomplete()
new FilesCollectionSorter()

View file

@ -7,7 +7,7 @@
"prefer-stable": true, "prefer-stable": true,
"require": { "require": {
"php": ">=8.0.0", "php": ">=8.0.0",
"murph/murph-core": "^1.0" "murph/murph-core": "dev-master"
}, },
"require-dev": { "require-dev": {
"symfony/browser-kit": "^5.4", "symfony/browser-kit": "^5.4",

View file

@ -38,5 +38,5 @@ core:
# - text/css # - text/css
# path: "%kernel.project_dir%/public/uploads" # path: "%kernel.project_dir%/public/uploads"
# path_uri: "/uploads" # path_uri: "/uploads"
# path_locked: path_locked: []
# - "%kernel.project_dir%/public/uploads" # - "%kernel.project_dir%/public/uploads"

View file

@ -120,6 +120,8 @@ class EstablishmentAdminController extends CrudController
->setForm('new', Type::class) ->setForm('new', Type::class)
// ->setForm('filter', Type::class) // ->setForm('filter', Type::class)
->setView('form', 'admin/establishment/_form.html.twig')
// ->setMaxPerPage('index', 20) // ->setMaxPerPage('index', 20)
// ->setIsSortableCollection('index', false) // ->setIsSortableCollection('index', false)

View file

@ -125,9 +125,13 @@ class InterventionAdminController extends CrudController
->setView('form', 'admin/intervention/_form.html.twig') ->setView('form', 'admin/intervention/_form.html.twig')
->setField('index', 'Establishment', Field\TextField::class, [ ->setField('index', 'Establishments', Field\TextField::class, [
'property' => 'establishment', 'property_builder' => function(EntityInterface $entity) {
'sort' => ['establishment', '.establishment'], return implode('<br>', array_map(function($e) {
return $e->getName();
}, $entity->getEstablishments()->toArray()));
},
'raw' => true,
]) ])
->setField('index', 'Activity reference', Field\ButtonField::class, [ ->setField('index', 'Activity reference', Field\ButtonField::class, [
'property' => 'activityReference', 'property' => 'activityReference',
@ -137,6 +141,7 @@ class InterventionAdminController extends CrudController
]) ])
->setField('index', 'Date', Field\DateField::class, [ ->setField('index', 'Date', Field\DateField::class, [
'property' => 'date', 'property' => 'date',
'format' => 'd/m/Y',
'sort' => ['date', '.date'], 'sort' => ['date', '.date'],
'attr' => ['class' => 'col-3'], 'attr' => ['class' => 'col-3'],
]) ])

View file

@ -124,6 +124,7 @@ class ToolAdminController extends CrudController
'property' => 'label', 'property' => 'label',
'sort' => ['label', '.label'], 'sort' => ['label', '.label'],
]) ])
->setDefaultSort('index', 'label', 'asc')
->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) { ->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
$manager->delete($entity); $manager->delete($entity);

View file

@ -18,36 +18,42 @@ class Establishment implements EntityInterface
* @ORM\GeneratedValue * @ORM\GeneratedValue
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
*/ */
private $id; protected $id;
/** /**
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
*/ */
private $name; protected $name;
/** /**
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
*/ */
private $address; protected $address;
/** /**
* @ORM\Column(type="string", length=10) * @ORM\Column(type="string", length=10)
*/ */
private $zipCode; protected $zipCode;
/** /**
* @ORM\Column(type="string", length=100) * @ORM\Column(type="string", length=100)
*/ */
private $city; protected $city;
/** /**
* @ORM\OneToMany(targetEntity=Intervention::class, mappedBy="establishment") * @ORM\OneToMany(targetEntity=EstablishmentGroup::class, mappedBy="establishment", orphanRemoval=true, cascade={"persist", "remove"})
*/ */
private $interventions; protected $establishmentGroups;
/**
* @ORM\ManyToMany(targetEntity=Project::class, mappedBy="establishments")
*/
protected $projects;
public function __construct() public function __construct()
{ {
$this->interventions = new ArrayCollection(); $this->establishmentGroups = new ArrayCollection();
$this->projects = new ArrayCollection();
} }
public function __toString() public function __toString()
@ -109,32 +115,59 @@ class Establishment implements EntityInterface
} }
/** /**
* @return Collection<int, Intervention> * @return Collection<int, EstablishmentGroup>
*/ */
public function getInterventions(): Collection public function getEstablishmentGroups(): Collection
{ {
return $this->interventions; return $this->establishmentGroups;
} }
public function addIntervention(Intervention $intervention): self public function addEstablishmentGroup(EstablishmentGroup $establishmentGroup): self
{ {
if (!$this->interventions->contains($intervention)) { if (!$this->establishmentGroups->contains($establishmentGroup)) {
$this->interventions[] = $intervention; $this->establishmentGroups[] = $establishmentGroup;
$intervention->setEstablishment($this); $establishmentGroup->setEstablishment($this);
} }
return $this; return $this;
} }
public function removeIntervention(Intervention $intervention): self public function removeEstablishmentGroup(EstablishmentGroup $establishmentGroup): self
{ {
if ($this->interventions->removeElement($intervention)) { if ($this->establishmentGroups->removeElement($establishmentGroup)) {
// set the owning side to null (unless already changed) // set the owning side to null (unless already changed)
if ($intervention->getEstablishment() === $this) { if ($establishmentGroup->getEstablishment() === $this) {
$intervention->setEstablishment(null); $establishmentGroup->setEstablishment(null);
} }
} }
return $this; return $this;
} }
/**
* @return Collection<int, Project>
*/
public function getProjects(): Collection
{
return $this->projects;
}
public function addProject(Project $project): self
{
if (!$this->projects->contains($project)) {
$this->projects[] = $project;
$project->addEstablishment($this);
}
return $this;
}
public function removeProject(Project $project): self
{
if ($this->projects->removeElement($project)) {
$project->removeEstablishment($this);
}
return $this;
}
} }

View file

@ -18,73 +18,84 @@ class Intervention implements EntityInterface
* @ORM\GeneratedValue * @ORM\GeneratedValue
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
*/ */
private $id; protected $id;
/** /**
* @ORM\ManyToMany(targetEntity=Speaker::class, inversedBy="interventions") * @ORM\ManyToMany(targetEntity=Speaker::class, inversedBy="interventions")
*/ */
private $speakers; protected $speakers;
/**
* @ORM\ManyToOne(targetEntity=Establishment::class, inversedBy="interventions")
* @ORM\JoinColumn(nullable=false, onDelete="CASCADE")
*/
private $establishment;
/** /**
* @ORM\Column(type="string", length=255, nullable=true) * @ORM\Column(type="string", length=255, nullable=true)
*/ */
private $activityReference; protected $activityReference;
/** /**
* @ORM\Column(type="date", nullable=true) * @ORM\Column(type="date", nullable=true)
*/ */
private $date; protected $date;
/** /**
* @ORM\Column(type="integer", nullable=true) * @ORM\Column(type="integer", nullable=true)
*/ */
private $groups; protected $groups;
/** /**
* @ORM\Column(type="string", length=255, nullable=true) * @ORM\Column(type="string", length=255, nullable=true)
*/ */
private $levels; protected $levels;
/** /**
* @ORM\Column(type="text", nullable=true) * @ORM\Column(type="text", nullable=true)
*/ */
private $themes; protected $themes;
/** /**
* @ORM\Column(type="text", nullable=true) * @ORM\Column(type="text", nullable=true)
*/ */
private $content; protected $content;
/** /**
* @ORM\ManyToMany(targetEntity=Tool::class, inversedBy="interventions") * @ORM\ManyToMany(targetEntity=Tool::class, inversedBy="interventions")
*/ */
private $tools; protected $tools;
/** /**
* @ORM\Column(type="text", nullable=true) * @ORM\Column(type="text", nullable=true)
*/ */
private $toolsDetails; protected $toolsDetails;
/** /**
* @ORM\Column(type="text", nullable=true) * @ORM\Column(type="text", nullable=true)
*/ */
private $feedback; protected $feedback;
/** /**
* @ORM\Column(type="text", nullable=true) * @ORM\Column(type="text", nullable=true)
*/ */
private $vigilantPoints; protected $vigilantPoints;
/**
* @ORM\ManyToOne(targetEntity=ThemeType::class, inversedBy="interventions")
* @ORM\JoinColumn(nullable=false)
*/
protected $themeType;
/**
* @ORM\Column(type="text", nullable=true)
*/
protected $comment;
/**
* @ORM\ManyToMany(targetEntity=EstablishmentGroup::class, inversedBy="interventions")
*/
protected $establishmentGroups;
public function __construct() public function __construct()
{ {
$this->speakers = new ArrayCollection(); $this->speakers = new ArrayCollection();
$this->tools = new ArrayCollection(); $this->tools = new ArrayCollection();
$this->establishmentGroups = new ArrayCollection();
} }
public function getId(): ?int public function getId(): ?int
@ -116,18 +127,6 @@ class Intervention implements EntityInterface
return $this; return $this;
} }
public function getEstablishment(): ?Establishment
{
return $this->establishment;
}
public function setEstablishment(?Establishment $establishment): self
{
$this->establishment = $establishment;
return $this;
}
public function getActivityReference(): ?string public function getActivityReference(): ?string
{ {
return $this->activityReference; return $this->activityReference;
@ -159,7 +158,7 @@ class Intervention implements EntityInterface
public function setGroups(?int $groups): self public function setGroups(?int $groups): self
{ {
$this->groups = $groups; $this->groups = abs($groups);
return $this; return $this;
} }
@ -259,4 +258,71 @@ class Intervention implements EntityInterface
return $this; return $this;
} }
public function getThemeType(): ?ThemeType
{
return $this->themeType;
}
public function setThemeType(?ThemeType $themeType): self
{
$this->themeType = $themeType;
return $this;
}
public function getComment(): ?string
{
return $this->comment;
}
public function setComment(string $comment): self
{
$this->comment = $comment;
return $this;
}
public function getEstablishments(): ArrayCollection
{
$data = new ArrayCollection();
foreach ($this->getEstablishmentGroups() as $group) {
if (!$data->contains($group->getEstablishment())) {
$data->add($group->getEstablishment());
}
}
$establishments = $data->toArray();
usort($establishments, function($a, $b) {
return $a->getName() <=> $b->getName();
});
return new ArrayCollection($establishments);
}
/**
* @return Collection<int, EstablishmentGroup>
*/
public function getEstablishmentGroups(): Collection
{
return $this->establishmentGroups;
}
public function addEstablishmentGroup(EstablishmentGroup $establishmentGroup): self
{
if (!$this->establishmentGroups->contains($establishmentGroup)) {
$this->establishmentGroups[] = $establishmentGroup;
}
return $this;
}
public function removeEstablishmentGroup(EstablishmentGroup $establishmentGroup): self
{
$this->establishmentGroups->removeElement($establishmentGroup);
return $this;
}
} }

View file

@ -18,17 +18,17 @@ class Speaker implements EntityInterface
* @ORM\GeneratedValue * @ORM\GeneratedValue
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
*/ */
private $id; protected $id;
/** /**
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
*/ */
private $name; protected $name;
/** /**
* @ORM\ManyToMany(targetEntity=Intervention::class, mappedBy="speakers", cascade={"persist"}) * @ORM\ManyToMany(targetEntity=Intervention::class, mappedBy="speakers", cascade={"persist"})
*/ */
private $interventions; protected $interventions;
public function __construct() public function __construct()
{ {

View file

@ -18,17 +18,17 @@ class Tool implements EntityInterface
* @ORM\GeneratedValue * @ORM\GeneratedValue
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
*/ */
private $id; protected $id;
/** /**
* @ORM\Column(type="string", length=255) * @ORM\Column(type="string", length=255)
*/ */
private $label; protected $label;
/** /**
* @ORM\ManyToMany(targetEntity=Intervention::class, mappedBy="tools", cascade={"persist", "remove"}) * @ORM\ManyToMany(targetEntity=Intervention::class, mappedBy="tools", cascade={"persist", "remove"})
*/ */
private $interventions; protected $interventions;
public function __construct() public function __construct()
{ {

View file

@ -22,53 +22,53 @@ class User implements PasswordAuthenticatedUserInterface, UserInterface, TwoFact
* @ORM\GeneratedValue * @ORM\GeneratedValue
* @ORM\Column(type="integer") * @ORM\Column(type="integer")
*/ */
private $id; protected $id;
/** /**
* @ORM\Column(type="string", length=180, unique=true) * @ORM\Column(type="string", length=180, unique=true)
*/ */
private $email; protected $email;
/** /**
* @ORM\Column(type="json") * @ORM\Column(type="json")
*/ */
private $roles = []; protected $roles = [];
/** /**
* @var string The hashed password * @var string The hashed password
* @ORM\Column(type="string") * @ORM\Column(type="string")
*/ */
private $password; protected $password;
/** /**
* @ORM\Column(type="string", length=255, nullable=true) * @ORM\Column(type="string", length=255, nullable=true)
*/ */
private $displayName; protected $displayName;
/** /**
* @ORM\Column(type="string", length=255, nullable=true) * @ORM\Column(type="string", length=255, nullable=true)
*/ */
private $totpSecret; protected $totpSecret;
/** /**
* @ORM\Column(type="datetime", nullable=true) * @ORM\Column(type="datetime", nullable=true)
*/ */
private $passwordRequestedAt; protected $passwordRequestedAt;
/** /**
* @ORM\Column(type="string", length=255, nullable=true) * @ORM\Column(type="string", length=255, nullable=true)
*/ */
private $confirmationToken; protected $confirmationToken;
/** /**
* @ORM\Column(type="boolean", options={"default"=0}) * @ORM\Column(type="boolean", options={"default"=0})
*/ */
private $isAdmin; protected $isAdmin;
/** /**
* @ORM\Column(type="boolean", options={"default"=0}) * @ORM\Column(type="boolean", options={"default"=0})
*/ */
private $isWriter; protected $isWriter;
public function __construct() public function __construct()
{ {

View file

@ -2,6 +2,7 @@
namespace App\Form; namespace App\Form;
use App\Core\Form\Type\CollectionType;
use App\Entity\Establishment; use App\Entity\Establishment;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
@ -16,6 +17,14 @@ class EstablishmentType extends AbstractType
->add('address') ->add('address')
->add('zipCode') ->add('zipCode')
->add('city') ->add('city')
->add('establishmentGroups', CollectionType::class, [
'collection_name' => 'establishment_groups',
'entry_type' => EstablishmentGroupType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'prototype' => true,
])
; ;
} }

View file

@ -6,6 +6,9 @@ use App\Entity\Intervention;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use App\Entity\Establishment;
use Doctrine\ORM\EntityRepository;
class InterventionType extends AbstractType class InterventionType extends AbstractType
{ {
@ -19,6 +22,7 @@ class InterventionType extends AbstractType
]) ])
->add('groups') ->add('groups')
->add('levels') ->add('levels')
->add('themeType')
->add('themes', null, [ ->add('themes', null, [
'attr' => ['rows' => 5], 'attr' => ['rows' => 5],
]) ])
@ -31,6 +35,9 @@ class InterventionType extends AbstractType
->add('toolsDetails', null, [ ->add('toolsDetails', null, [
'attr' => ['rows' => 5], 'attr' => ['rows' => 5],
]) ])
->add('comment', null, [
'attr' => ['rows' => 7],
])
->add('feedback', null, [ ->add('feedback', null, [
'attr' => ['rows' => 5], 'attr' => ['rows' => 5],
]) ])
@ -40,7 +47,22 @@ class InterventionType extends AbstractType
->add('speakers', null, [ ->add('speakers', null, [
'expanded' => true, 'expanded' => true,
]) ])
->add('establishment') ->add('establishmentGroups', null, [
'multiple' => true,
'attr' => [
'size' => 15,
'data-jschoice' => '',
],
'group_by' => function($choice, $key, $value) {
return $choice->getEstablishment()->getName();
},
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('g')
->leftJoin('g.establishment', 'e')
->addOrderBy('e.name', 'ASC')
->addOrderBy('g.label', 'ASC');
},
])
; ;
} }

View file

@ -1,11 +1,11 @@
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
{% for item in ['establishment', 'activityReference', 'date', 'groups', 'levels', 'speakers'] %} {% for item in ['date', 'establishmentGroups', 'activityReference', 'groups', 'levels', 'speakers'] %}
{% include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) with {form: form[item]} %} {% include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) with {form: form[item]} %}
{% endfor %} {% endfor %}
</div> </div>
<div class="col-md-9 pl-md-3"> <div class="col-md-9 pl-md-3 pr-md-3">
{% for item in ['themes', 'content', 'tools', 'toolsDetails', 'feedback', 'vigilantPoints'] %} {% for item in ['themeType', 'themes', 'content', 'tools', 'toolsDetails', 'comment', 'feedback', 'vigilantPoints'] %}
{% include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) with {form: form[item]} %} {% include(configuration.view('form_widget', '@Core/admin/crud/_form_widget.html.twig')) with {form: form[item]} %}
{% endfor %} {% endfor %}
</div> </div>

View file

@ -1,5 +1,5 @@
{% if is_granted('ROLE_WRITER') %} {% if is_granted('ROLE_WRITER') %}
{{ include('@Core/admin/module/_menu_section.html.twig', {label: 'Interventions'}) }} {{ include('@Core/admin/module/_menu_section.html.twig', {label: 'Configuration'}) }}
<ul class="nav flex-column"> <ul class="nav flex-column">
{{ include('@Core/admin/module/_menu_item.html.twig', { {{ include('@Core/admin/module/_menu_item.html.twig', {
@ -15,6 +15,13 @@
route: path('admin_establishment_index'), route: path('admin_establishment_index'),
icon: 'fa fa-building' icon: 'fa fa-building'
}) }} }) }}
{{ include('@Core/admin/module/_menu_item.html.twig', {
id: 'theme_type',
label: 'Types de thématiques',
route: path('admin_theme_type_index'),
icon: 'fa fa-dice-d6'
}) }}
{{ include('@Core/admin/module/_menu_item.html.twig', { {{ include('@Core/admin/module/_menu_item.html.twig', {
id: 'tool', id: 'tool',
@ -22,6 +29,17 @@
route: path('admin_tool_index'), route: path('admin_tool_index'),
icon: 'fa fa-dice-d6' icon: 'fa fa-dice-d6'
}) }} }) }}
</ul>
{{ include('@Core/admin/module/_menu_section.html.twig', {label: 'Suivis'}) }}
<ul class="nav flex-column">
{{ include('@Core/admin/module/_menu_item.html.twig', {
id: 'project',
label: 'Projets',
route: path('admin_project_index'),
icon: 'fa fa-box-open'
}) }}
{{ include('@Core/admin/module/_menu_item.html.twig', { {{ include('@Core/admin/module/_menu_item.html.twig', {
id: 'intervention', id: 'intervention',
@ -29,5 +47,19 @@
route: path('admin_intervention_index'), route: path('admin_intervention_index'),
icon: 'fa fa-comment-dots' icon: 'fa fa-comment-dots'
}) }} }) }}
{{ include('@Core/admin/module/_menu_item.html.twig', {
id: 'conference',
label: 'Conférences',
route: path('admin_conference_index'),
icon: 'fa fa-users'
}) }}
{{ include('@Core/admin/module/_menu_item.html.twig', {
id: 'debriefing',
label: 'Comptes-rendus',
route: path('admin_debriefing_index'),
icon: 'fa fa-list-alt'
}) }}
</ul> </ul>
{% endif %} {% endif %}

View file

@ -4,7 +4,10 @@
"Speaker": "Intervenant⋅e" "Speaker": "Intervenant⋅e"
"Speakers": "Intervenant⋅e⋅s" "Speakers": "Intervenant⋅e⋅s"
"Establishment": "Établissement" "Establishment": "Établissement"
"Establishments": "Établissements"
"Establishment groups": "Classes et groupes"
"Activity reference": "Activité de référence" "Activity reference": "Activité de référence"
"Comment": "Commentaire"
"Groups": "Groupe(s)" "Groups": "Groupe(s)"
"Levels": "Niveau(x)" "Levels": "Niveau(x)"
"Themes": "Thématiques abordées" "Themes": "Thématiques abordées"
@ -12,3 +15,9 @@
"Tools details": "Détails outils et supports" "Tools details": "Détails outils et supports"
"Feedback": "Les retours des participant⋅e⋅s" "Feedback": "Les retours des participant⋅e⋅s"
"Vigilant points": "Point(s) de vigilance observé(s) par l'intervenant⋅e" "Vigilant points": "Point(s) de vigilance observé(s) par l'intervenant⋅e"
"Persons": "Personnes"
"Theme type": "Type de thématique"
"Topic": "Sujet"
"Project": "Projet"
"Internal contributors": "Contributeurs internes"
"External contributors": "Contributeurs externes"

3380
yarn.lock

File diff suppressed because it is too large Load diff