Merge branch 'develop' into trunk

This commit is contained in:
K Muller 2026-08-13 14:06:23 +02:00
commit 1e07265b62
16 changed files with 356 additions and 80 deletions

View file

@ -30,18 +30,18 @@ class StaticPageController extends Controller
*/
public function edit(StaticPage $static_page): \Illuminate\Contracts\View\View
{
$isCommitmentPage = in_array(
$hasSections = in_array(
$static_page->type,
StaticPageType::getCommitmentsPages()
[...StaticPageType::getCommitmentsPages(), StaticPageType::home],
);
if ($isCommitmentPage) {
if ($hasSections) {
$static_page->load(['sections.blocks', 'sections.pictos']);
}
return view('bo.pages.static_pages.edit', [
'staticPageModel' => $static_page,
'isCommitmentPage' => $isCommitmentPage
'hasSections' => $hasSections,
]);
}

View file

@ -51,6 +51,8 @@ class HomeController extends Controller
});
$videoModels = Video::get9PublishedHomeVideosOrderedWithProducts();
$discountsTotal = Discount::getCachedDiscounts()->sum('value');
$staticPageModel->load(['sections.blocks', 'sections.pictos']);
return view('fo.pages.home', compact(
'staticPageModel',
'mainSlideModels',

View file

@ -13,6 +13,7 @@ use Illuminate\Http\Exceptions\HttpResponseException;
class StorePictoRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
@ -49,7 +50,8 @@ class StorePictoRequest extends FormRequest
'description' => ['nullable', 'string', new WysiwygLength(5)],
'picture_alt' => 'required_with:picture|nullable|between:3,255',
'picture_title' => 'required_with:picture|nullable|between:3,255',
'section_id' => 'required|exists:section_pages,id',
'url' => 'nullable|url|between:3,255',
'section_id' => 'required|exists:section_pages,id',
];
return array_merge(
@ -66,11 +68,12 @@ class StorePictoRequest extends FormRequest
public function attributes()
{
return [
'title' => trans('validation.attributes.title'),
'title' => trans('validation.attributes.title'),
'description' => trans('validation.attributes.description'),
'picture' => trans('validation.attributes.image'),
'picture_alt' => trans('validation.custom.attribute_alt'),
'picture_title' => trans('validation.custom.attribute_title'),
'url' => trans('validation.custom.url'),
];
}

View file

@ -21,6 +21,7 @@ class Picto extends Model
'picture',
'picture_width',
'picture_height',
'url',
'section_id',
'order'
];

View file

@ -70,7 +70,7 @@ class SectionPage extends Model
protected static function booted()
{
static::saving(function ($section) {
$section->load('page');
if (
$section->page->type === StaticPageType::raiseSustainably
&& $section->name === 'section 3'

View file

@ -0,0 +1,215 @@
<?php
use App\Enums\InputType;
use App\Enums\StaticPageType;
use App\Models\StaticPage;
use Illuminate\Database\Migrations\Migration;
use App\Traits\Migrations\HasStaticPageMigration;
return new class extends Migration
{
use HasStaticPageMigration;
private function getSections(): array
{
return [
[
'name' => 'Section 1',
'data' => [
'title' => [
'label' => 'Section 1 : Titre',
'inputType' => InputType::string->value,
'value' => 'Le Gaulois, la volaille préférée des Français',
'validate' => 'required|string|between:3,255',
'required' => true
],
'description' => [
'label' => 'Section 1 : Description',
'inputType' => InputType::text->value,
'value' => "<p>Découvrez nos produits de volailles 100% nées, élevées et préparées en France. Autant d'occasions de multiplier de bons repas et de partager de savoureux moments !</p>",
'validate' => 'required|string|min:15',
'required' => true
],
],
'blocks' => [
'reductionBlock' => true,
]
],
[
'name' => 'Section 2',
'data' => [
'title' => [
'label' => 'Section 2 : Titre',
'inputType' => InputType::string->value,
'value' => 'Le Gaulois, une marque engagée et responsable',
'validate' => 'required|string|between:3,255',
'required' => true
],
],
'blocks' => [
'has_pictos' => true,
'max_pictos' => 5,
]
],
[
'name' => 'Section 3',
'data' => [
'title' => [
'label' => 'Section 3 : Titre',
'inputType' => InputType::string->value,
'value' => 'Des recettes gourmandes',
'validate' => 'required|string|between:3,255',
'required' => true
],
'description' => [
'label' => 'Section 3 : Description',
'inputType' => InputType::text->value,
'value' => "<p>Manger sain et équilibré tout en se faisant plaisir, c'est la mission de Le Gaulois !</p>",
'validate' => 'required|string|min:15',
'required' => true
],
],
],
[
'name' => 'Section 4',
'data' => [
'title' => [
'label' => 'Section 4 : Titre',
'inputType' => InputType::string->value,
'value' => 'Les actualités Le Gaulois',
'validate' => 'required|string|between:3,255',
'required' => true
],
'description' => [
'label' => 'Section 4 : Description',
'inputType' => InputType::text->value,
'value' => "<p>Pour ne rien manquer des actus Le Gaulois, découvrez la vie de notre marque à travers nos évènements, nos récompenses, nos conseils ... et bien d'autres !</p>",
'validate' => 'required|string|min:15',
'required' => true
],
],
],
[
'name' => 'Section 5',
'data' => [
'title' => [
'label' => 'Section 5 : Titre',
'inputType' => InputType::string->value,
'value' => 'La chaine @legaulois',
'validate' => 'required|string|between:3,255',
'required' => true
],
'description' => [
'label' => 'Section 5 : Description',
'inputType' => InputType::text->value,
'value' => "<p>Tout le Gaulois en vidéos, nos dernières publicités, nos recettes savoureuses, nos conseils nutrition, nos évènements sportifs avec le Tour De France ou caritatifs avec l'association Mécénat Chirurgie Cardiaque … Tout y est!</p>",
'validate' => 'required|string|min:15',
'required' => true
],
],
],
];
}
private function getPictos(): array
{
return [
'Section 2' => [
[
'title' => 'Volaille 100% Française',
'description' => '<p>Être la grande marque alimentaire française</p>',
'picto' => 'assets/images/home/commitments/picto-volaille-francaise-blanc.svg',
'picto_alt' => 'Pictogramme d\'une volaille française',
'picto_title' => 'Volaille dans le territoire français',
'url' => 'https://www.legaulois.fr/engagements/la-marque-100-francaise',
],
[
'title' => 'Élever durablement',
'description' => '<p>Favoriser une démarche d\'élevage durable et locale auprès de nos éleveurs et des volailles</p>',
'picto' => 'assets/images/home/commitments/picto-elever-blanc.svg',
'picto_alt' => 'Pictogramme d\'une volaille',
'picto_title' => 'Volaille avec ses petits',
'url' => 'https://www.legaulois.fr/engagements/elever-durablement',
],
[
'title' => 'Bien nourrir',
'description' => '<p>Agir sur la nutrition et la sécurité de nos produits pour satisfaire les français</p>',
'picto' => 'assets/images/home/commitments/picto-bien-nourrir-blanc.svg',
'picto_alt' => 'Pictogramme d\'une assiette',
'picto_title' => 'Assiette avec des couverts',
'url' => 'https://www.legaulois.fr/engagements/bien-nourrir-les-francais',
],
[
'title' => 'Mieux vivre ensemble',
'description' => '<p>Accompagner nos collaborateurs dans la pratique du sport</p>',
'picto' => 'assets/images/home/commitments/picto-mieux-vivre-blanc.svg',
'picto_alt' => 'Pictogramme de deux mains',
'picto_title' => 'Serrage de mains',
'url' => 'https://www.legaulois.fr/engagements/favoriser-le-mieux-vivre-ensemble',
],
[
'title' => 'Respecter la terre',
'description' => '<p>Minimiser notre empreinte carbone et agir sur le changement climatique</p>',
'picto' => 'assets/images/home/commitments/picto-respect-blanc.svg',
'picto_alt' => 'Pictogramme d\'une plante',
'picto_title' => 'Pousse d\'une plante au creux d\'une main',
'url' => 'https://www.legaulois.fr/engagements/respecter-la-terre',
],
],
];
}
/**
* Run the migrations.
*/
public function up(): void
{
$page = new StaticPage();
$commonSection = $this->getCommonSection(StaticPageType::home);
$data = [
'common_section' => $commonSection
];
$staticPage = $this->createOrUpdateStaticPage(
[
'type' => StaticPageType::home->value,
'seo_title' => 'Des poulets, dindes et canards, nés, élevés et préparés en France',
'seo_description' => 'Bienvenue chez Le Gaulois ! Des volailles et éleveurs 100% français, des recettes de poulet, dinde, canard et végétarienne, et notre Club des Gaulois',
'slug' => 'accueil',
'data' => $data,
],
$data,
StaticPageType::home
);
foreach ($this->getSections() as $sectionConfig) {
$section = $this->createOrUpdateSection($staticPage, $sectionConfig);
$this->createOrUpdateBlocks($staticPage, $section, $sectionConfig);
}
$staticPage->load('sections');
$this->createOrUpdatePictos($staticPage, $this->getPictos());
}
/**
* Reverse the migrations.
*/
public function down(): void
{
$page = StaticPage::query()
->where('type', StaticPageType::home->value)
->with(['sections.blocks', 'sections.pictos'])
->first();
foreach ($page->sections as $section) {
$section->blocks()->delete();
$section->pictos()->delete();
$section->delete();
}
$page->data = [];
$page->saveQuietly();
}
};

View file

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('pictos', function (Blueprint $table) {
$table->string('url')->nullable()->comment('Picto url.')->after('picture_height');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('pictos', function (Blueprint $table) {
$table->dropColumn('url');
});
}
};

View file

@ -221,12 +221,13 @@
class="col-12 d-flex flex-column flex-md-row justify-content-center align-items-center w-75 w-md-100 mx-auto"
>
<a
v-if="textsParameters.btn1Visibility"
class="content-btns btn btn-primary border-0 my-1 my-md-0 mx-md-1 w-100 w-md-fit"
:href="getShowLegendRoute(legendsParameters.modelSelected?.slug as string)"
:href="textsParameters.btn1Url"
title="Accéder à la page de tous les produits de la gamme"
:style="'background-color:' + legendsParameters.modelSelected?.color"
>
{{ (legendsParameters.modelSelected) ? textsParameters.btn1Label + " " + legendsParameters.modelSelected?.label : textsParameters.btn1Label }}
{{ textsParameters.btn1Label }}
</a>
<a
class="content-btns btn btn-secondary border-0 my-1 my-md-0 mx-md-1 w-100 w-md-fit"
@ -317,12 +318,16 @@ const routeParameters = reactive<{
const textsParameters = reactive<{
helperNoItems: string,
helperAllItems: string,
btn1Visibility: boolean,
btn1Label: string,
btn1Url: string,
btn2Label: string,
}>({
helperNoItems: "",
helperAllItems: "",
btn1Visibility: false,
btn1Label: "",
btn1Url: "",
btn2Label: "",
});
@ -357,7 +362,9 @@ onMounted(() => {
routeParameters.ajax = data.routeAjax;
/** texts parameters. */
textsParameters.btn1Visibility = data.texts.btn1Visibility;
textsParameters.btn1Label = data.texts.btn1Label;
textsParameters.btn1Url = data.texts.btn1Url;
textsParameters.btn2Label = data.texts.btn2Label;
textsParameters.helperNoItems = data.texts.helperNoItems;
textsParameters.helperAllItems = data.texts.helperAllItems;

View file

@ -174,6 +174,39 @@
</div>
</fieldset>
</div>
{{-- URL --}}
<div class="col-12 col-lg-6 mb-3">
<fieldset class="h-100 border p-3">
<legend>{{ str(__('validation.custom.url'))->ucfirst() }}</legend>
<div class="row">
<div class="col-12">
@include('bo.partials.inputs.varchar', [
'id' => $idPrefix . '-url',
'label' => str(
__(':field :inter:model', [
'field' => __('validation.custom.url'),
'inter' => $inter,
'model' => $model,
]))->ucfirst(),
'hasWordCounter' => true,
'name' => 'url',
'required' => false,
'model' => $pictoModel ?? null,
'helper' => __('validation.between.string', [
'attribute' => __(':field :inter:model', [
'field' => __('validation.custom.url'),
'inter' => $inter,
'model' => $model,
]),
'min' => 3,
'max' => 255,
]),
'placeholder' => __('Un beau titre'),
])
</div>
</div>
</fieldset>
</div>
<div class="col-12 text-center">
<p><b>{{ __('* Champs obligatoires') }}</b></p>
</div>

View file

@ -34,7 +34,7 @@
</form>
{{-- Sections & block --}}
@if($isCommitmentPage)
@if($hasSections)
@include('bo.pages.static_pages.sections.form_section', [
'sections' => $staticPageModel->sections
])

View file

@ -128,7 +128,8 @@
!$staticPageModel->type->equals(StaticPageType::forLivingTogether) &&
!$staticPageModel->type->equals(StaticPageType::poultryBrand) &&
!$staticPageModel->type->equals(StaticPageType::raiseSustainably) &&
!$staticPageModel->type->equals(StaticPageType::feedProperly))
!$staticPageModel->type->equals(StaticPageType::feedProperly) &&
!$staticPageModel->type->equals(StaticPageType::home))
)
{{-- SUBTITLE --}}
<div class="col-12 col-md-6 mb-3">

View file

@ -49,7 +49,7 @@
<li class="nav-item" role="presentation">
<button class="nav-link active" data-bs-toggle="tab"
data-bs-target="#s{{ $section->id }}-t1" type="button" role="tab">
<i class="bi bi-eye me-1"></i> Générale
<i class="bi bi-eye me-1"></i> Général
</button>
</li>

View file

@ -1,3 +1,9 @@
@php
$sections = $staticPageModel->sections;
$getSection = fn($name) => $sections->firstWhere('name', $name);
@endphp
@extends('fo.layout')
@section('title', $staticPageModel->seo_title)
@ -111,13 +117,18 @@
</div>
</div>
{{-- RANGES/PRODUCTS --}}
@if ($rangeModels->isNotEmpty())
@php
$section = $getSection('Section 1');
$sectionData = $section->data;
$sectionBlocks = $section->blocks[0]->data;
@endphp
@if ($rangeModels->isNotEmpty() && $section->visibility)
<section class="position-relative bg-light gx-0 py-5" id="products">
<div class="container-fluid position-relative z-2 row w-100 mx-auto">
<div class="col-12 text-center">
<h2>{{ str(__('le gaulois, la volaille préférée des français'))->upper() }}</h2>
<h2>{{ $sectionData->title->value }}</h2>
<p class="m-0">
{{ __('Découvrez nos produits de volailles 100% nées, élevées et préparées en France. Autant d\'occasions de multiplier de bons repas et de partager de savoureux moments !') }}
{!! clean($sectionData->description->value) !!}
</p>
</div>
<div class="col-12">
@ -133,7 +144,9 @@
'contentSwiper' => true,
'texts' => [
'helperNoItems' => __('Aucun produit disponible dans cette gamme'),
'btn1Label' => str(__('découvrir tous nos produits'))->upper(),
'btn1Visibility' => $sectionBlocks->visibility->value,
'btn1Label' => $sectionBlocks->cta_label->value,
'btn1Url' => $sectionBlocks->cta_url->value,
'btn2Label' => str(__('découvrir tous nos produits'))->upper(),
],
];
@ -143,8 +156,13 @@
</div>
</section>
@endif
@php
$section = $getSection('Section 2');
$sectionData = $section->data;
$sectionPictos = $section->pictos;
@endphp
{{-- DEMARCHE --}}
@if ($demarcheSlideModels->isNotEmpty())
@if ($demarcheSlideModels->isNotEmpty() && $section->visibility)
<section class="position-relative bg-light mt-2 overflow-hidden" id="demarche">
<div class="line-border-medium position-absolute start-50 translate-middle-x h-100 w-100 top-0 pt-4">
<div class="bg-green-wood line-border-medium h-100 w-100" id="demarche-bg"></div>
@ -165,8 +183,8 @@
href="#FIXME:lien vers la démarche grâce à la relation">
<img class="img-fluid h-100"
src="{{ $demarcheSlideModel->picture }}#FIXME:image de la démarche grâce à la relation"
title="#FIXME:titre de l'image de la démarche"
alt="#FIXME:alt de l'image de la démarche">
title="{{ $demarcheSlideModel->picture_alt }}"
alt="{{ $demarcheSlideModel->picture_title }}">
</a>
<div class="slide-content position-relative pt-lg-0 pt-2">
<div class="line-border-medium bg-light position-absolute w-100 h-100 start-0 top-0">
@ -213,12 +231,18 @@
</div>
</div>
@include('fo.partials.commitments.list-pages', [
'sectionData' => $sectionData,
'commitments' => $sectionPictos,
'showBtn' => true,
])
</section>
@endif
{{-- RECIPES --}}
@if ($recipeModels->isNotEmpty())
@php
$section = $getSection('Section 3');
$sectionData = $section->data;
@endphp
@if ($recipeModels->isNotEmpty() && $section->visibility)
@include('fo.partials.cards-slider', [
'id' => 'recipe',
'title' => 'des recettes gourmandes',
@ -230,14 +254,18 @@
])
@endif
{{-- NEWS --}}
@if ($newsModels->isNotEmpty())
@php
$section = $getSection('Section 4');
$sectionData = $section->data;
@endphp
@if ($newsModels->isNotEmpty() && $section->visibility)
<section class="py-4" id="news">
<div class="container">
<div class="row my-2">
<div class="col-12 col-xl-7">
<h2 class="text-xl-start px-xl-0 px-5 text-center">{{ __('Les actualités le gaulois') }}</h2>
<h2 class="text-xl-start px-xl-0 px-5 text-center">{{ $sectionData->title->value }}</h2>
<p class="text-xl-start px-xl-0 px-4 text-center">
{{ __('Pour ne rien manquer des actus Le Gaulois, découvrez la vie de notre marque à travers nos évènements, nos récompenses, nos conseils ... et bien d\'autres !') }}
{!! clean($sectionData->description->value) !!}
</p>
</div>
</div>
@ -305,11 +333,14 @@
</section>
@endif
{{-- SLIDER VIDEO PUB --}}
@if ($videoModels->isNotEmpty())
@php
$section = $getSection('Section 5');
$sectionData = $section->data;
@endphp
@if ($videoModels->isNotEmpty() && $section->visibility)
@include('fo.partials.videos-slider', [
'title' => 'la chaine @LEGAULOIS',
'text' =>
'Tout le Gaulois en vidéos, nos dernières publicités, nos recettes savoureuses, nos conseils nutrition, nos évènements sportifs avec le Tour De France ou caritatifs avec l\'association Mécénat Chirurgie Cardiaque … Tout y est!',
'title' => $sectionData->title->value,
'text' => $sectionData->description->value,
'videoModels' => $videoModels,
'btnRouteName' => 'https://www.youtube.com/@legaulois',
'btnRouteTitle' => 'Saga publicitaire',

View file

@ -2,10 +2,10 @@
'card card-custom d-flex flex-column justify-content-center align-items-center text-secondary border-0 bg-transparent text-center',
'swiper-slide' => isset($isSlide) && $isSlide,
])>
<img class="card-img-top" src="{{ $commitment['picto'] }}" title="{{ $commitment['picto_title'] }}" alt="{{ $commitment['picto_alt'] }}">
<img class="card-img-top" src="{{ asset($commitment->picture) }}" title="{{ $commitment->picture_title }}" alt="{{ $commitment->picture_alt }}">
<div class="card-body">
<p class="h3 fs-5">{{ str($commitment['title'])->upper() }}</p>
<p class="m-0">{{ str($commitment['text'])->ucFirst() }}</p>
<a class="stretched-link" href="{{ $commitment['url'] }}"></a>
<p class="h3 fs-5">{{ $commitment->title }}</p>
<p class="m-0">{!! clean($commitment->description) !!}</p>
<a class="stretched-link" href="{{ $commitment->url }}"></a>
</div>
</div>

View file

@ -2,59 +2,14 @@
<div class="row">
<div class="col-12">
<h2 class="text-secondary fs-2 text-center">
{{ str(__('le gaulois, une marque engagée et responsable'))->ucFirst() }}
{{ $sectionData->title->value }}
</h2>
</div>
<div class="col-12 position-relative my-4">
@php
$arrayCommitments = [
[
'picto' => Vite::asset('resources/assets/images/home/commitments/picto-volaille-francaise-blanc.svg'),
'picto_alt' => __('Pictogramme d\'une volaille française'),
'picto_title' => __('Volaille dans le territoire français'),
'title' => __('volaille 100% française'),
'text' => __('être la grande marque alimentaire française'),
'url' => route('fo.commitments.brand'),
],
[
'picto' => Vite::asset('resources/assets/images/home/commitments/picto-elever-blanc.svg'),
'picto_alt' => __('Pictogramme d\'une volaille'),
'picto_title' => __('Volaille avec ses petits'),
'title' => __('élever durablement'),
'text' => __('favoriser une démarche d\'élevage durable et locale auprès de nos
éleveurs et des volailles'),
'url' => route('fo.commitments.raise'),
],
[
'picto' => Vite::asset('resources/assets/images/home/commitments/picto-bien-nourrir-blanc.svg'),
'picto_alt' => __('Pictogramme d\'une assiette'),
'picto_title' => __('Assiette avec des couverts'),
'title' => __('bien nourrir'),
'text' => __('agir sur la nutrition et la sécurité de nos produits pour satisfaire les français'),
'url' => route('fo.commitments.eat'),
],
[
'picto' => Vite::asset('resources/assets/images/home/commitments/picto-mieux-vivre-blanc.svg'),
'picto_alt' => __('Pictogramme de deux mains'),
'picto_title' => __('Serrage de mains'),
'title' => __('mieux vivre ensemble'),
'text' => __('accompagner nos collaborateurs dans la pratique du sport'),
'url' => route('fo.commitments.life'),
],
[
'picto' => Vite::asset('resources/assets/images/home/commitments/picto-respect-blanc.svg'),
'picto_alt' => __('Pictogramme d\'une plante'),
'picto_title' => __('Pousse d\'une plante au creux d\'une main'),
'title' => __('respecter la terre'),
'text' => __('minimiser notre empreinte carbone et agir sur le changement climatique'),
'url' => route('fo.commitments.respect'),
],
];
@endphp
<div class="d-sm-none">
<div class="swiper swiper-commitments mx-5" id="swiper-commitments">
<div class="swiper-wrapper w-fit">
@foreach ($arrayCommitments as $commitment)
@foreach ($commitments as $commitment)
@include('fo.partials.commitment-card', ['isSlide' => true])
@endforeach
</div>
@ -71,7 +26,7 @@
</button>
</div>
<div class="d-none d-sm-flex w-100 justify-content-center mx-auto flex-wrap">
@foreach ($arrayCommitments as $commitment)
@foreach ($commitments as $commitment)
@include('fo.partials.commitment-card')
@endforeach
</div>

View file

@ -3,7 +3,7 @@
<hgroup class="col-12 col-lg-6 position-relative z-1 text-lg-start text-center mb-3">
<h2>{{ str(__($title))->upper() }}</h2>
<p class="m-0">
{{ __($text) }}
{!! clean($text) !!}
</p>
</hgroup>
<div class="col-12 col-lg-6 position-relative text-lg-end text-center z-1">