diff --git a/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php b/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php index 3ebe6f28a..e82bb9ec8 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Entity/AsideActivityCategory.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Chill\AsideActivityBundle\Entity; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; @@ -20,8 +21,20 @@ class AsideActivityCategory */ private int $id; + /** + * @ORM\Column(type="json", length=255) + */ + private array $title; + + /** + * @ORM\OneToMany(targetEntity=AsideActivityCategory::class, mappedBy="parent") + * @ORM\Column(type="boolean") + */ + private bool $isActive = true; + /** * @ORM\ManyToOne(targetEntity=AsideActivityCategory::class, inversedBy="children") + * @ORM\JoinColumn(nullable=true) */ private $parent; @@ -30,15 +43,10 @@ class AsideActivityCategory */ private $children; - /** - * @ORM\Column(type="json", length=255) - */ - private array $title; - - /** - * @ORM\Column(type="boolean") - */ - private bool $isActive = true; + public function __construct() + { + $this->children = new ArrayCollection(); + } public function getId(): ?int { diff --git a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityCategoryType.php b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityCategoryType.php index 1bfc6ed37..c7357b603 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityCategoryType.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityCategoryType.php @@ -4,19 +4,35 @@ namespace Chill\AsideActivityBundle\Form; use Chill\AsideActivityBundle\Entity\AsideActivityCategory; use Chill\MainBundle\Form\Type\TranslatableStringFormType; +use Chill\MainBundle\Templating\TranslatableStringHelper; +use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; - use Symfony\Component\Form\FormBuilderInterface; final class AsideActivityCategoryType extends AbstractType { + + protected $translatableStringHelper; + + public function __construct(TranslatableStringHelper $translatableStringHelper) + { + $this->translatableStringHelper = $translatableStringHelper; + } + public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('title', TranslatableStringFormType::class, [ 'label' => 'Nom', ]) + ->add('parent', EntityType::class, [ + 'class' => AsideActivityCategory::class, + 'required' => false, + 'choice_label' => function (AsideActivityCategory $category){ + return $this->translatableStringHelper->localize($category->getTitle()); + } + ]) ->add('isActive', ChoiceType::class, [ 'choices' => [ diff --git a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php index d7e5a7700..46f54471f 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Form/AsideActivityFormType.php @@ -4,6 +4,7 @@ namespace Chill\AsideActivityBundle\Form; use Chill\AsideActivityBundle\Entity\AsideActivity; use Chill\AsideActivityBundle\Entity\AsideActivityCategory; +use Chill\AsideActivityBundle\Templating\Entity\CategoryRender; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Form\Type\ChillDateType; use Chill\MainBundle\Form\Type\ChillTextareaType; @@ -19,22 +20,25 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; - +use Symfony\Component\Templating\EngineInterface; final class AsideActivityFormType extends AbstractType { protected array $timeChoices; private TranslatableStringHelper $translatableStringHelper; private TokenStorageInterface $storage; + private CategoryRender $categoryRender; public function __construct ( TranslatableStringHelper $translatableStringHelper, ParameterBagInterface $parameterBag, - TokenStorageInterface $storage + TokenStorageInterface $storage, + CategoryRender $categoryRender ){ $this->timeChoices = $parameterBag->get('chill_aside_activity.form.time_duration'); $this->translatableStringHelper = $translatableStringHelper; $this->storage = $storage; + $this->CategoryRender = $categoryRender; } public function buildForm(FormBuilderInterface $builder, array $options) @@ -78,7 +82,9 @@ final class AsideActivityFormType extends AbstractType 'class' => AsideActivityCategory::class, 'placeholder' => 'Choose the activity category', 'choice_label' => function (AsideActivityCategory $asideActivityCategory) { - return $this->translatableStringHelper->localize($asideActivityCategory->getTitle()); + $options = []; + return $this->categoryRender->renderString($asideActivityCategory, $options); + // return $this->translatableStringHelper->localize($asideActivityCategory->getTitle()); }, ]) ->add('duration', ChoiceType::class, $durationTimeOptions) diff --git a/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivityCategory/asideActivityCategory.html.twig b/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivityCategory/asideActivityCategory.html.twig new file mode 100644 index 000000000..17077db14 --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Resources/views/asideActivityCategory/asideActivityCategory.html.twig @@ -0,0 +1,13 @@ +{% set reversed_parents = parents|reverse %} + + + {%- for p in reversed_parents %} + + {{ p.title|localize_translatable_string }}{{ options['default.separator'] }} + + {%- endfor -%} + + {{ asideActivityCategory.title|localize_translatable_string }} + + + diff --git a/src/Bundle/ChillAsideActivityBundle/src/Templating/Entity/CategoryRender.php b/src/Bundle/ChillAsideActivityBundle/src/Templating/Entity/CategoryRender.php new file mode 100644 index 000000000..8bab0986c --- /dev/null +++ b/src/Bundle/ChillAsideActivityBundle/src/Templating/Entity/CategoryRender.php @@ -0,0 +1,80 @@ + ' > ' + ]; + + public function __construct(TranslatableStringHelper $translatableStringHelper, EngineInterface $engine) + { + $this->translatableStringHelper = $translatableStringHelper; + $this->engine = $engine; + } + + /** + * @param AsideActivityCategory $asideActivityCategory + */ + public function renderString($asideActivityCategory, array $options): string + { + $options = array_merge(self::DEFAULT_ARGS, $options); + + $titles[] = $this->translatableStringHelper->localize($asideActivityCategory->getTitle()); + + while ($asideActivityCategory->hasParent()) { + $asideActivityCategory = $asideActivityCategory->getParent(); + $titles[] = $this->translatableStringHelper->localize($asideActivityCategory->getTitle()); + } + + $titles = array_reverse($titles); + + return implode($options[self::SEPERATOR_KEY], $titles); + + } + + /** + * @param AsideActivityCategory $asideActivityCategory + */ + public function supports($asideActivityCategory, array $options): bool + { + return $asideActivityCategory instanceof AsideActivityCategory; + } + + public function buildParents(AsideActivityCategory $asideActivityCategory) + { + $parents = []; + + while($asideActivityCategory->hasParent()) { + $asideActivityCategory = $parents[] = $asideActivityCategory->getParent(); + } + + return $parents; + } + + /** + * @param AsideActivityCategory $asideActivityCategory + */ + public function renderBox($asideActivityCategory, array $options): string + { + $options = array_merge(self::DEFAULT_ARGS, $options); + $parents = $this->buildParents($asideActivityCategory); + + return $this->engine->render('@ChillAsideActivity/asideActivityCategory/asideActivityCategory.html.twig', + [ + 'asideActivityCategory' => $asideActivityCategory, + 'parents' => $parents, + 'options' => $options + ]); + } +} \ No newline at end of file diff --git a/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml b/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml index 8ff656fdc..ede446723 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml +++ b/src/Bundle/ChillAsideActivityBundle/src/config/services.yaml @@ -1,5 +1,12 @@ services: - Chill\AsideActivityBundle\DataFixtures\: - resource: './../DataFixtures' - autowire: true - autoconfigure: true + Chill\AsideActivityBundle\DataFixtures\: + resource: "./../DataFixtures" + autowire: true + autoconfigure: true + + Chill\AsideActivityBundle\Templating\Entity\: + autowire: true + autoconfigure: true + resource: "../Templating/Entity" + tags: + - "chill.render_entity" diff --git a/src/Bundle/ChillAsideActivityBundle/src/migrations/Version20210922182907.php b/src/Bundle/ChillAsideActivityBundle/src/migrations/Version20210922182907.php index 7699f86f3..00948e210 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/migrations/Version20210922182907.php +++ b/src/Bundle/ChillAsideActivityBundle/src/migrations/Version20210922182907.php @@ -19,7 +19,9 @@ final class Version20210922182907 extends AbstractMigration public function up(Schema $schema): void { - $this->addSql('ALTER TABLE chill_asideactivity.asideactivitycategory ADD parent VARCHAR(255) DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_asideactivity.asideactivitycategory ADD parent_id INT DEFAULT NULL'); + $this->addSql('ALTER TABLE chill_asideactivity.asideactivitycategory ADD CONSTRAINT FK_7BF90DBE727ACA70 FOREIGN KEY (parent_id) REFERENCES chill_asideactivity.AsideActivityCategory (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('CREATE INDEX IDX_7BF90DBE727ACA70 ON chill_asideactivity.asideactivitycategory (parent_id)'); } public function down(Schema $schema): void diff --git a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss index 21ecde267..69f3f30f2 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss +++ b/src/Bundle/ChillMainBundle/Resources/public/chill/scss/flex_table.scss @@ -8,39 +8,45 @@ div.flex-bloc, div.flex-table { + display: flex; + align-items: stretch; + align-content: stretch; + box-sizing: border-box; + margin: 1.5em 0; + + div.item-bloc { display: flex; - align-items: stretch; - align-content: stretch; - box-sizing: border-box; - margin: 1.5em 0; + @include border-collapse; + padding: 1em; - div.item-bloc { + div.item-row { + display: flex; + + div.item-col:last-child { display: flex; - @include border-collapse; - padding: 1em; - - div.item-row { - display: flex; - - div.item-col:last-child { - display: flex; - } - } + } } + } - h2, h3, h4, dl, p { - margin: 0; - } - h2, h3, h4 { - color: $blue; - } + h2, + h3, + h4, + dl, + p { + margin: 0; + } + h2, + h3, + h4 { + color: $blue; + } - ul.record_actions { - margin: 0; - li { - margin-right: 5px; - } + ul.record_actions { + margin: 0; + li { + margin-right: 5px; } + } } /* @@ -48,36 +54,43 @@ div.flex-table { */ div.flex-bloc { - flex-direction: row; - flex-wrap: wrap; + flex-direction: row; + flex-wrap: wrap; - div.item-bloc { - flex-grow: 0; flex-shrink: 1; flex-basis: auto; - flex-direction: column; - margin: 0; - hyphens: auto; + div.item-bloc { + flex-grow: 0; + flex-shrink: 1; + flex-basis: auto; + flex-direction: column; + margin: 0; + hyphens: auto; - div.item-row { - flex-grow: 1; flex-shrink: 1; flex-basis: auto; - flex-direction: column; + div.item-row { + flex-grow: 1; + flex-shrink: 1; + flex-basis: auto; + flex-direction: column; - div.item-col { - - &:first-child { - flex-grow: 0; flex-shrink: 0; flex-basis: auto; - } - - &:last-child { - flex-grow: 1; flex-shrink: 1; flex-basis: auto; - @include separator; - - ul.record_actions { - align-self: flex-end; - } - } - } + div.item-col { + &:first-child { + flex-grow: 0; + flex-shrink: 0; + flex-basis: auto; } + + &:last-child { + flex-grow: 1; + flex-shrink: 1; + flex-basis: auto; + @include separator; + + ul.record_actions { + align-self: flex-end; + } + } + } } + } } /* @@ -85,53 +98,57 @@ div.flex-bloc { */ div.flex-table { + flex-direction: column; + + div.item-bloc { flex-direction: column; - div.item-bloc { - flex-direction: column; + &:nth-child(even) { + background-color: $gray-200; - &:nth-child(even) { - background-color: $gray-200; - - .chill-user-quote { - background-color: shade-color($gray-200, 5%) - } - } - - div.item-row { - flex-direction: row; - - div.item-col { - &:first-child { - flex-grow: 0; flex-shrink: 0; flex-basis: auto; - } - - &:last-child { - flex-grow: 1; flex-shrink: 1; flex-basis: auto; - justify-content: flex-end; - - @include media-breakpoint-down(md) { - @include separator; - } - - ul.record_actions { - align-self: flex-start; - } - } - } - - @include media-breakpoint-down(md) { - flex-direction: column; - div.item-col { - &:last-child { - ul.record_actions { - align-self: flex-end; - } - } - } - } - } + .chill-user-quote { + background-color: shade-color($gray-200, 5%); + } } + + div.item-row { + flex-direction: row; + + div.item-col { + &:first-child { + flex-grow: 0; + flex-shrink: 0; + flex-basis: auto; + } + + &:last-child { + flex-grow: 1; + flex-shrink: 1; + flex-basis: auto; + justify-content: flex-end; + + @include media-breakpoint-down(md) { + @include separator; + } + + ul.record_actions { + align-self: flex-start; + } + } + } + + @include media-breakpoint-down(md) { + flex-direction: column; + div.item-col { + &:last-child { + ul.record_actions { + align-self: flex-end; + } + } + } + } + } + } } /* @@ -140,54 +157,54 @@ div.flex-table { */ div.wrap-list { - padding: 0; - width: 100%; + padding: 0; + width: 100%; - div.wl-row { - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-between; + div.wl-row { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; - div.wl-col.title { - width: auto; - flex-shrink: 0; + div.wl-col.title { + width: auto; + flex-shrink: 0; - @include media-breakpoint-up(md) { - //margin-left: 1.5em; - } + @include media-breakpoint-up(md) { + //margin-left: 1.5em; + } - & > * { - padding-right: 1em; - } - } - - div.wl-col.list { - width: 75%; - margin: auto 0 0 auto; - - .wl-item { - margin: 0.1em; - padding: 0em; - display: inline-block; - } - } + & > * { + padding-right: 1em; + } } - &.debug .wl-row { - border: 1px solid $black; + div.wl-col.list { + width: 75%; + margin: auto 0 0 auto; - div.wl-col.title { - background-color: yellow; - } - div.wl-col.list { - background-color: cyan; - - p.wl-item { - background-color: orange; - } - } + .wl-item { + margin: 0.1em; + padding: 0em; + display: inline-block; + } } + } + + &.debug .wl-row { + border: 1px solid $black; + + div.wl-col.title { + background-color: yellow; + } + div.wl-col.list { + background-color: cyan; + + p.wl-item { + background-color: orange; + } + } + } } /* @@ -196,43 +213,56 @@ div.wrap-list { */ div.wrap-header { - width: 100%; + width: 100%; + + div.wh-row { + display: flex; + flex-direction: row; + + &:first-child { + align-items: baseline; + } + &:last-child { + } + + div.wh-col { + &:first-child { + flex-grow: 0; + flex-shrink: 1; + flex-basis: auto; + } + &:last-child { + flex-grow: 1; + flex-shrink: 1; + flex-basis: auto; - div.wh-row { display: flex; - flex-direction: row; + justify-content: flex-end; + } + } + } + &.debug { + border: 1px solid $black; + div.wh-row { + &:first-child div.wh-col { &:first-child { - align-items: baseline; + background-color: $yellow; } - &:last-child {} - - div.wh-col { - &:first-child { - flex-grow: 0; flex-shrink: 1; flex-basis: auto; - } - &:last-child { - flex-grow: 1; flex-shrink: 1; flex-basis: auto; - - display: flex; - justify-content: flex-end; - } - } - } - - &.debug { - border: 1px solid $black; - div.wh-row { - &:first-child div.wh-col { - &:first-child { background-color: $yellow; } - &:last-child { background-color: $beige; } - } - &:last-child div.wh-col { - &:first-child { background-color: $orange; } - &:last-child { background-color: $pink; } - } + &:last-child { + background-color: $beige; } + } + &:last-child div.wh-col { + &:first-child { + background-color: $orange; + } + &:last-child { + background-color: $pink; + } + } } + } } /* @@ -243,69 +273,67 @@ div.flex-bloc, div.flex-table, div.wrap-list, div.wrap-header { + div.separator { + @include separator; + } - div.separator { - @include separator; - } - - ul.record_actions { - flex-grow: 1; - flex-shrink: 0; - flex-basis: auto; - margin: 0; - - li { - margin-right: 5px; - } + ul.record_actions { + flex-grow: 1; + flex-shrink: 0; + flex-basis: auto; + margin: 0; + + li { + margin-right: 5px; } + } } - /* * FLOATBUTTON * p-ê pas convaincant: cet asset est toujours en observation */ div.float-button { + width: 100%; + + div.box { width: 100%; - div.box { - width: 100%; + div.action { + float: right; + } + } + &.top { + div.action { + padding: 0 0 1em 1em; + } - div.action { - float: right; - } + // avoid a position relative that make links unclickable + .fa-ul > li { + position: initial; } - &.top { - div.action { - padding: 0 0 1em 1em; - } - - // avoid a position relative that make links unclickable - .fa-ul > li { - position: initial; - } + } + &.bottom { + display: flex; + overflow: hidden; + div.action { + height: calc(100% - 0em); + shape-outside: inset(calc(100% - 2em) 0 0); + display: flex; + align-items: flex-end; + padding: 0 0 0 1em; + * { + align-self: flex-end !important; + } } - &.bottom { - display: flex; - overflow: hidden; - div.action { - height: calc(100% - 0em); - shape-outside: inset(calc(100% - 2em) 0 0); - display: flex; - align-items: flex-end; - padding: 0 0 0 1em; - * { - align-self: flex-end !important; - } - } - } - &.debug { - padding: 1em; - border: 1px solid black; - background-color: yellow; - div.action { - background-color: transparentize(#00ffff, 0.4); - } + } + &.debug { + padding: 1em; + border: 1px solid black; + background-color: yellow; + div.action { + background-color: transparentize(#00ffff, 0.4); } + } }