diff --git a/app/Enums/InputType.php b/app/Enums/InputType.php index ad7c44bc..49edf721 100644 --- a/app/Enums/InputType.php +++ b/app/Enums/InputType.php @@ -8,20 +8,22 @@ enum InputType: int { use BaseEnum; - case boolean = 1; - case dropdownArray = 2; - case email = 3; - case image = 4; - case imageArray = 5; - case labelArray = 6; - case miniblocArray = 7; - case number = 8; - case selectEnum = 9; - case password = 10; - case string = 11; - case text = 12; - case url = 13; - case videoVimeo = 14; - case videoYoutube = 15; - case video = 16; + case boolean = 1; + case dropdownArray = 2; + case email = 3; + case image = 4; + case imageArray = 5; + case labelArray = 6; + case miniblocArray = 7; + case number = 8; + case selectEnum = 9; + case password = 10; + case string = 11; + case text = 12; + case url = 13; + case videoVimeo = 14; + case videoYoutube = 15; + case video = 16; + case slider = 17; + case selectImageVideo = 18; } diff --git a/app/Enums/StaticPageType.php b/app/Enums/StaticPageType.php index 2126e6ec..7964fb7e 100644 --- a/app/Enums/StaticPageType.php +++ b/app/Enums/StaticPageType.php @@ -33,6 +33,7 @@ enum StaticPageType: int case exitOutside = 23; case ouiCBon = 24; case ourFarms = 25; + case tdf = 26; /** * Optionnal labels definition. @@ -64,7 +65,8 @@ enum StaticPageType: int self::rules->name => 'Règlement du jeu', self::exitOutside->name => 'Sortir à l\'extérieur', self::ouiCBon->name => 'Oui c\'est bon', - self::ourFarms->name => 'Nos élevages' + self::ourFarms->name => 'Nos élevages', + self::tdf->name => 'Tour de France' ]; /** diff --git a/app/Http/Controllers/Bo/StaticPageController.php b/app/Http/Controllers/Bo/StaticPageController.php index 71f467a2..910b146c 100644 --- a/app/Http/Controllers/Bo/StaticPageController.php +++ b/app/Http/Controllers/Bo/StaticPageController.php @@ -43,9 +43,19 @@ class StaticPageController extends Controller public function update(UpdateStaticPageRequest $request, StaticPage $static_page): \Illuminate\Http\RedirectResponse { return DB::transaction(function () use ($request, $static_page) { + $validated = $request->validated(); + $data = $static_page->data; + + foreach ($validated['data'] as $key => $input) { + if (isset($input['value']) && is_array($input['value'])) { + $data[$key]['value'] = $input['value']; + } + } + + $static_page->data = $data; $static_page->fill(array_replace_recursive( $static_page->getFillableFields($static_page), - $request->validated() + $validated )); if ($static_page->saveOrFail()) { @@ -73,7 +83,7 @@ class StaticPageController extends Controller ->map(fn($page) => $page->data['common_section'] ?? null) ->filter() ->first(); - + return view('bo.pages.static_pages.common_section.edit', [ 'commonSection' => $commonSection, ]); @@ -88,17 +98,17 @@ class StaticPageController extends Controller public function updateCommonSection(UpdateCommonSectionRequest $request): RedirectResponse { $pages = StaticPage::whereIn('type', StaticPageType::getBreedingValues())->get(); - + try { DB::transaction(function () use ($pages, $request) { foreach ($pages as $page) { $data = $page->data ?? []; - + $data['common_section'] = [ 'title' => $request->validated('title'), 'content' => $request->validated('content'), ]; - + $page->data = $data; $page->saveOrFail(); } @@ -110,7 +120,7 @@ class StaticPageController extends Controller ->with('error', __('crud.messages.cannot_be_updated', [ 'model' => Str::ucfirst(__('validation.custom.common_section_part'))])); } - + return redirect() ->route('bo.static_page.sections.edit') ->with('success', __('crud.messages.has_been_updated', [ diff --git a/app/Http/Controllers/Club/ShopController.php b/app/Http/Controllers/Club/ShopController.php index 707e9e64..9adc5f92 100644 --- a/app/Http/Controllers/Club/ShopController.php +++ b/app/Http/Controllers/Club/ShopController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Club; +use App\Enums\Shop\ContactFormCivility; use App\Enums\Shop\OrderStatus; use App\Enums\Shop\OrderType; use App\Exceptions\CBPayementError; @@ -69,6 +70,7 @@ class ShopController extends Controller $order->payementType = OrderType::make(intval($request->payementMethod)); $order->status = OrderStatus::waiting; $order->customer_id = $customerModel->getKey(); + $order->civility = ContactFormCivility::m; // FIXME: Remove this field $order->fill($request->coords); $order->saveOrFail(); } catch (QueryException $e) { diff --git a/app/Http/Controllers/Fo/BreedingController.php b/app/Http/Controllers/Fo/BreedingController.php index ed06f424..f74f38db 100644 --- a/app/Http/Controllers/Fo/BreedingController.php +++ b/app/Http/Controllers/Fo/BreedingController.php @@ -39,13 +39,16 @@ class BreedingController extends Controller $ocbBreederModels = OcbBreeder::getOcbBreederCards(); $staticPage = StaticPage::query()->where('type', StaticPageType::ouiCBon)->first(); $categoryApproachModels = CategoryApproach::all(); - $sections = SectionApproach::query()->where('page_id', $staticPage->id)->get(); - $sectionApproachModels = CollectionHelper::mapItemsToCards($sections->toArray(), $categoryApproachModels->toArray()); + $sections = SectionApproach::query() + ->with('category') + ->where('page_id', $staticPage->id) + ->get() + ->groupBy(fn($section) => $section->category?->name); return view('fo.pages.breeding.oui-c-est-bon', [ 'ocbBreederModels' => $ocbBreederModels, 'staticPageModel' => $staticPage, 'categoryApproachModels' => $categoryApproachModels, - 'sectionApproachModels' => $sectionApproachModels + 'sections' => $sections ]); } @@ -75,24 +78,27 @@ class BreedingController extends Controller ]); } - /** - * Display the given breeding page. - * - * @return \Illuminate\Contracts\View\View - */ + /** + * Display the given breeding page. + * + * @return \Illuminate\Contracts\View\View + */ public function exitOutsidePage(): \Illuminate\Contracts\View\View { $staticPage = StaticPage::query()->where('type', StaticPageType::exitOutside)->first(); $sliderImages = CollectionHelper::mapSlidesToFields($staticPage->data); $categoryApproachModels = CategoryApproach::all(); - $sections = SectionApproach::query()->where('page_id', $staticPage->id)->get(); - $sectionApproachModels = CollectionHelper::mapItemsToCards($sections->toArray(), $categoryApproachModels->toArray()); + $sections = SectionApproach::query() + ->with('category') + ->where('page_id', $staticPage->id) + ->get() + ->groupBy(fn($section) => $section->category?->name); return view('fo.pages.breeding.sortant-a-l-exterieur', [ - 'staticPageModel' => $staticPage, - 'sliderImages' => $sliderImages, + 'staticPageModel' => $staticPage, + 'sliderImages' => $sliderImages, 'categoryApproachModels' => $categoryApproachModels, - 'sectionApproachModels' => $sectionApproachModels + 'sections' => $sections, ]); } diff --git a/app/Http/Controllers/Fo/StaticPageController.php b/app/Http/Controllers/Fo/StaticPageController.php index 83b7eb97..50cea2ef 100644 --- a/app/Http/Controllers/Fo/StaticPageController.php +++ b/app/Http/Controllers/Fo/StaticPageController.php @@ -141,4 +141,15 @@ class StaticPageController extends Controller $videoModels = Video::get6PublishedVideosOrderedWithProducts(); return view('fo.pages.saga', compact('staticPageModel', 'videoModels')); } + + /** + * TDF page. + * + * @return \Illuminate\Contracts\View\View + */ + public function tdf(): \Illuminate\Contracts\View\View + { + $staticPageModel = StaticPage::getCachedStaticPage(StaticPageType::tdf); + return view('fo.pages.tdf', compact('staticPageModel')); + } } diff --git a/app/Http/Requests/Bo/UpdateMiniCrudRequest.php b/app/Http/Requests/Bo/UpdateMiniCrudRequest.php index 0b0fdde8..24c4411b 100644 --- a/app/Http/Requests/Bo/UpdateMiniCrudRequest.php +++ b/app/Http/Requests/Bo/UpdateMiniCrudRequest.php @@ -5,6 +5,7 @@ namespace App\Http\Requests\Bo; use App\Enums\InputType; use App\Lib\Helpers\ToolboxHelper; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Arr; use Illuminate\Validation\ValidationException; abstract class UpdateMiniCrudRequest extends FormRequest @@ -203,6 +204,36 @@ abstract class UpdateMiniCrudRequest extends FormRequest $rules = array_merge($rules, [ "data.$iptName.value" => 'boolean' ]); + break; + case InputType::slider->value: + foreach ($input['columns'] as $column) { + $columnId = $column['id']; + $validation = $column['validation'] ?? ''; + + if (!empty($validation)) { + $rules["data.$iptName.value.*.$columnId"] = $validation; + } + } + break; + case InputType::selectImageVideo->value: + if (Arr::exists($this->input("data.$iptName.value"), 'picture')) { + $rules = array_merge($rules, [ + "data.$iptName.value.picture" => "required|array", + "data.$iptName.value.picture.path" => "required|image|mimes:png,jpg,jpeg|dimensions:width=620,height=348", + "data.$iptName.value.picture.alt" => "required|string|between:3,255", + "data.$iptName.value.picture.title" => "required|string|between:3,255", + ]); + } + + if (Arr::exists($this->input("data.$iptName.value"), 'video')) { + $rules = array_merge($rules, [ + "data.$iptName.value.video" => "required|array", + "data.$iptName.value.video.id" => "required|string", + "data.$iptName.value.video.alt" => "required|string|between:3,255", + "data.$iptName.value.video.title" => "required|string|between:3,255", + ]); + } + break; } //end switch } //end foreach diff --git a/app/Lib/Helpers/CollectionHelper.php b/app/Lib/Helpers/CollectionHelper.php index b0fe5aee..cdbe8b8f 100644 --- a/app/Lib/Helpers/CollectionHelper.php +++ b/app/Lib/Helpers/CollectionHelper.php @@ -118,89 +118,5 @@ class CollectionHelper ]) ->values() ->toArray(); - } - - /** - * Transform and merge 2 arrays to get homogeneous result - * - * @param array $items sections array - * @param array $categories categories array - * @return array - */ - public static function mapItemsToCards(array $items, array $categories): array - { - $categoriesById = collect($categories)->keyBy('id'); - $result = []; - - foreach ($items as $item) { - $category = $categoriesById->get($item['category_id']); - if (!$category) { - continue; - } - $categoryKey = $category['name']; - $parsed = self::parseContent($item['content']); - - $result[$categoryKey][] = [ - 'category' => $item['title'], - 'icon' => [ - 'path' => $item['picture'], - 'alt' => $item['picture_alt'], - 'description' => $item['picture_title'], - ], - 'content' => [ - [ - 'title' => $parsed['title'], - 'points' => $parsed['points'], - 'asterisc' => $parsed['asterisc'], - ], - ], - 'class' => 'card-template-sm', - ]; - } - return $result; - } - - - private static function parseContent(string $html): array - { - $dom = new \DOMDocument(); - @$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8')); - - // Extract h3 - $title = ''; - $h3 = $dom->getElementsByTagName('h3'); - if ($h3->length > 0) { - $title = $h3->item(0)->textContent; - } - - // Extract li - $points = []; - $liNodes = $dom->getElementsByTagName('li'); - foreach ($liNodes as $li) { - $points[] = trim($li->textContent); - } - - // Extract last
* - $lastAsterisc = null; - $pNodes = $dom->getElementsByTagName('p'); - foreach ($pNodes as $p) { - $text = trim($p->textContent); - if (str_starts_with($text, '*')) { - $lastAsterisc = $text; - } - } - // if nothing to extract - if (empty($title) && empty($points)) { - $body = $dom->getElementsByTagName('body')->item(0); - $rawText = trim($body ? $body->textContent : strip_tags($html)); - if (!empty($rawText)) { - $points[] = $rawText; - } - } - return [ - 'title' => $title, - 'points' => $points, - 'asterisc' => $lastAsterisc ?? '', - ]; - } + } } diff --git a/app/Providers/ViewServiceProvider.php b/app/Providers/ViewServiceProvider.php index 0050dee3..2b495c7f 100644 --- a/app/Providers/ViewServiceProvider.php +++ b/app/Providers/ViewServiceProvider.php @@ -136,6 +136,7 @@ class ViewServiceProvider extends ServiceProvider ['name' => 'exitOutsideStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::exitOutside)], ['name' => 'ouiCestBonStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::ouiCBon)], ['name' => 'ourFarmsStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::ourFarms)], + ['name' => 'tdfStaticPageModel', 'value' => StaticPage::getCachedStaticPage(StaticPageType::tdf)], ])->map(function ($array) { View::share($array['name'], (function () use ($array) { return $array['value'] ?: null; diff --git a/app/Traits/Models/MiniCrudModel.php b/app/Traits/Models/MiniCrudModel.php index 268c94cb..f6021055 100644 --- a/app/Traits/Models/MiniCrudModel.php +++ b/app/Traits/Models/MiniCrudModel.php @@ -171,6 +171,20 @@ trait MiniCrudModel case InputType::url->value: ToolboxHelper::assertNoInternalBoUrls('content', $input['value']); break; + case InputType::slider->value: + if (!is_array($data[$o]['value'])) break; + foreach ($data[$o]['value'] as $index => $item) { + if (isset($item['picture']) && $item['picture'] instanceof \Illuminate\Http\UploadedFile) { + $data[$o]['value'][$index]['picture'] = \preg_replace( + '/^\/|^/', + '', + FileStorageHelper::storeFile($this, $item['picture'], true) + ); + } + } + break; + case InputType::selectImageVideo->value: + break; default: throw new \ErrorException(sprintf( 'Unknown input Type %s', diff --git a/database/migrations/2026_04_30_115934_add_data_to_static_page_tdf.php b/database/migrations/2026_04_30_115934_add_data_to_static_page_tdf.php new file mode 100644 index 00000000..20dac34f --- /dev/null +++ b/database/migrations/2026_04_30_115934_add_data_to_static_page_tdf.php @@ -0,0 +1,612 @@ + [ + 'label' => 'Texte de l\'encart', + 'inputType' => InputType::string->value, + 'value' => 'TOUR DE FRANCE 2025', + 'validate' => 'required|string|between:3,20', + 'required' => true + ], + 'banner_title' => [ + 'label' => 'Titre de la bannière', + 'inputType' => InputType::string->value, + 'value' => 'Le Gaulois en selle pour le tour de France', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'banner_picture' => [ + 'label' => 'Logo de la bannière', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 100, + 'height' => 300 + ], + 'alt' => 'Logo volaille officielle de Le Gaulois et le Tour de France', + 'title' => 'Logo volaille officielle de Le Gaulois et le Tour de France', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'banner_button_title' => [ + 'label' => 'Titre du bouton n°1', + 'inputType' => InputType::string->value, + 'value' => 'Le Gaulois et le Tour de France', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'banner_button_title_2' => [ + 'label' => 'Titre du bouton n°2', + 'inputType' => InputType::string->value, + 'value' => 'Au cœur de la caravane le Gaulois', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'banner_picture_author' => [ + 'label' => 'Nom du photographe', + 'inputType' => InputType::string->value, + 'value' => 'Crédit photo - ASO Aurélien Vialatte', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'play_title' => [ + 'label' => 'Titre de la section n°2', + 'inputType' => InputType::string->value, + 'value' => 'Jouez et tentez de gagner !', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'play_description' => [ + 'label' => 'Description de la section n°2', + 'inputType' => InputType::text->value, + 'value' => '
Participez à notre jeu 100% gagnant Le Gaulois pendant le Tour de France 2025.
', + 'validate' => 'required|string|min:15', + 'required' => true + ], + 'play_picture' => [ + 'label' => 'Logo de la section n°2', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 200, + 'height' => 125 + ], + 'alt' => '100% Gagnant', + 'title' => '100% Gagnant', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'play_btn_text' => [ + 'label' => 'Texte du bouton', + 'inputType' => InputType::string->value, + 'value' => 'Je tente ma chance !', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'play_btn_url' => [ + 'label' => 'Lien du bouton', + 'inputType' => InputType::url->value, + "value" => "https://www.legauloistourdefrance.fr", + "validate" => "required|string|url:http,https", + 'required' => true + ], + 'prize_title_1' => [ + 'label' => 'Texte du lot n°1', + 'inputType' => InputType::string->value, + 'value' => 'Ensembles TV + Home Cinema', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'prize_picture_1' => [ + 'label' => 'Image du lot n°1', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 130, + 'height' => 100 + ], + 'alt' => 'Ensemble TV-Home cinéma à gagner pendant le jeu TDF 100% gagnant Le Gaulois', + 'title' => '5 Ensembles TV-Home cinéma à gagner', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'prize_title_2' => [ + 'label' => 'Texte du lot n°2', + 'inputType' => InputType::string->value, + 'value' => 'Casques audios oreilles libres', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'prize_picture_2' => [ + 'label' => 'Image du lot n°2', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 130, + 'height' => 100 + ], + 'alt' => 'Casques audios oreilles libres à gagner pendant le jeu TDF 100% gagnant Le Gaulois', + 'title' => '40 Casques audios oreilles libres à gagner', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'prize_title_3' => [ + 'label' => 'Texte du lot n°3', + 'inputType' => InputType::string->value, + 'value' => 'Bons d\'achat', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'prize_picture_3' => [ + 'label' => 'Image du lot n°3', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 130, + 'height' => 100 + ], + 'alt' => 'Bons de réduction à gagner pendant le jeu TDF 100% gagnant Le Gaulois', + 'title' => 'Milliers de bons d\'achat à gagner', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + "play_display_section" => [ + "label" => "Afficher la section n°2", + "inputType" => InputType::boolean->value, + "value" => true, + "validate" => "required|boolean", + 'required' => true + ], + 'find_us_title' => [ + 'label' => 'Titre de la section n°3', + 'inputType' => InputType::string->value, + 'value' => 'Venez nous retrouver sur le parcours du Tour de France', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'find_us_description' => [ + 'label' => 'Description de la section n°3', + 'inputType' => InputType::text->value, + 'value' => 'Participez à notre jeu 100% gagnant Le Gaulois pendant le Tour de France 2025.
', + 'validate' => 'required|string|min:15', + 'required' => true + ], + 'find_us_picture' => [ + 'label' => 'Pictogramme de la section n°3', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 50, + 'height' => 70 + ], + 'alt' => 'Point d\'exclamation', + 'title' => 'Point d\'exclamation', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'find_us_btn_text_1' => [ + 'label' => 'Texte du bouton n°1', + 'inputType' => InputType::string->value, + 'value' => 'Télécharger la pancarte', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'find_us_btn_url_1' => [ + 'label' => 'Lien du bouton n°1', + 'inputType' => InputType::url->value, + "value" => "https://www.legaulois.fr/tdf-downloads/Pancarte.pdf", + "validate" => "required|string|url:http,https", + 'required' => true + ], + 'find_us_btn_text_2' => [ + 'label' => 'Texte du bouton n°2', + 'inputType' => InputType::string->value, + 'value' => 'Télécharger des coloriages', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'find_us_btn_url_2' => [ + 'label' => 'Lien du bouton n°2', + 'inputType' => InputType::url->value, + "value" => "https://www.legaulois.fr/tdf-downloads/COLORIAGE-Avril-2024.pdf", + "validate" => "required|string|url:http,https", + 'required' => true + ], + 'find_us_picture_1' => [ + 'label' => 'Image n°1 de la section n°3', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 300, + 'height' => 220 + ], + 'alt' => 'Chars de la caravane Le Gaulois', + 'title' => 'Caravane Le Gaulois au mileu des supporters du Tour de France', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'find_us_picture_2' => [ + 'label' => 'Image n°2 de la section n°3', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 300, + 'height' => 220 + ], + 'alt' => 'Char Cuisson Express Le Gaulois', + 'title' => 'Char cocotte Cuisson Express traversant un pont lors d\'une étape du Tour de France', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'find_us_picture_author' => [ + 'label' => 'Nom du photographe', + 'inputType' => InputType::string->value, + 'value' => 'Crédit photo - ASO Aurélien Vialatte', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'tdf_title' => [ + 'label' => 'Titre de la section n°4', + 'inputType' => InputType::string->value, + 'value' => 'Le Gaulois et le Tour de France', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'tdf_subtitle_1' => [ + 'label' => 'Sous-titre n°1 de la section n°4', + 'inputType' => InputType::string->value, + 'value' => 'Plus de 10 années', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'tdf_subtitle_2' => [ + 'label' => 'Sous-titre n°2 de la section n°4', + 'inputType' => InputType::string->value, + 'value' => ' de complicité avec le public', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'tdf_description' => [ + 'label' => 'Description de la section n°4', + 'inputType' => InputType::text->value, + 'value' => 'Les chars « Cocottes » sont devenus emblématiques de la caravane du Tour de France. Dix-huit caravaniers passionnés, dont huit femmes et dix hommes de tous âges et venus de différentes régions de France, auront l\'opportunité unique de piloter les mythiques « Cocottes » à travers le pays. Représentant la diversité et l\'esprit de partage, ces caravaniers formeront une véritable famille pendant toute la durée du Tour, unis par leur amour du sport et leur engagement envers Le Gaulois.
Les huit chars Le Gaulois, représentant les produits phares de la marque, offrent au public l\'occasion de découvrir ou redécouvrir la diversité des produits proposés : Crousty Chicken, Oui c\'est Bon !, Cordon Bleu, Filets de Poulet Rôti, Blanc de Poulet Sortant à l\'Extérieur, et Le Filet de Poulet Tranché Cuisson Express seront à l\'honneur. Deux chars multiproduits, mettant en avant la diversité de la gamme, défileront également en tête de cortège avec à leur bord l\'animateur Arthur pour enflammer les foules gauloises. En fin de défilé, le char « Roule ma Poule » viendra clore le spectacle, un clin d\'œil toujours apprécié du public.
Les chars iront à la rencontre des Français pour distribuer plus de 600 000 goodies Le Gaulois dans une atmosphère joyeuse et conviviale. Cette année, la marque offrira des capes supporters, des magnets décapsuleurs, et des milliers de bons de réduction pour découvrir les produits Le Gaulois en magasin.
Le 26 juillet 2019, lors de l\'étape Saint-Jean-de-Maurienne et Tignes, les chars terminent leur parade quand un orage de grêle s\'invite et entraîne une coulée de boue qui bloque la route et l\'accès aux cyclistes. L\'organisation décide l\'arrêt de l\'étape et marquera l\'histoire du Tour de France.
', + 'validate' => 'required|string|min:15', + 'required' => true + ], + 'fun_facts_picture_1' => [ + 'label' => 'Pictogramme de l\'anecdote n°1', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 80, + 'height' => 80 + ], + 'alt' => 'Pictogramme d\'un soleil pour les anecdoctes météo sur le Tour de France', + 'title' => 'Pictogramme d\'un soleil', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'fun_facts_title_2' => [ + 'label' => 'Titre de l\'anecdote n°2', + 'inputType' => InputType::string->value, + 'value' => 'La transmission d\'une passion entre Père & Fils !', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'fun_facts_description_2' => [ + 'label' => 'Description de l\'anecdote n°2', + 'inputType' => InputType::text->value, + 'value' => 'Avec plus de 10 tours de France à son actif, Jean-Marie a transmis sa passion à son fils. Léo assure : « Sur le Tour, nous sommes collègues, mais il y a toujours cette fierté personnelle de partager ces moments ensemble sur les routes de France. ».
', + 'validate' => 'required|string|min:15', + 'required' => true + ], + 'fun_facts_picture_2' => [ + 'label' => 'Pictogramme de l\'anecdote n°2', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 80, + 'height' => 80 + ], + 'alt' => 'Pictogramme d\'une tête d\'enfant pour les liens de parenté au sein de l\'équipe des caravaniers', + 'title' => 'Pictogramme d\'un enfant', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'fun_facts_title_3' => [ + 'label' => 'Titre de l\'anecdote n°3', + 'inputType' => InputType::string->value, + 'value' => 'Histoire de poulet', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'fun_facts_description_3' => [ + 'label' => 'Description de l\'anecdote n°3', + 'inputType' => InputType::text->value, + 'value' => 'Avec un sens de l\'humour aguerri, Cocotte rencontre un sympathique collègue au détour d\'un passage dans un village sur les routes du Tour de France.
', + 'validate' => 'required|string|min:15', + 'required' => true + ], + 'fun_facts_picture_3' => [ + 'label' => 'Pictogramme de l\'anecdote n°3', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 80, + 'height' => 80 + ], + 'alt' => 'Pictogramme d\'un poulet policier pour les histoires poulets-policiers pendants le Tour de France', + 'title' => 'Pictogramme d\'un poulet policier', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'fun_facts_title_4' => [ + 'label' => 'Titre de l\'anecdote n°4', + 'inputType' => InputType::string->value, + 'value' => 'Un pompier en tant que chauffeur d\'un char Le Gaulois !', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'fun_facts_description_4' => [ + 'label' => 'Description de l\'anecdote n°4', + 'inputType' => InputType::text->value, + 'value' => 'Nicolas, pompier engagé, a procédé à une intervention de secours pour une jeune caravanière Le Gaulois. Depuis, Nicolas, rebaptisé Pimpon Le Gaulois, sillonne toujours les routes de France à bord d\'un char Cocotte.
', + 'validate' => 'required|string|min:15', + 'required' => true + ], + 'fun_facts_picture_4' => [ + 'label' => 'Pictogramme de l\'anecdote n°4', + 'inputType' => 4, + 'dimensions' => [ + 'width' => 80, + 'height' => 80 + ], + 'alt' => 'Pictogramme d\'une sirène d\'alarme pour les anecdoctes insolites pendant le Tour de France', + 'title' => 'Pictorgramme d\'une alarme', + 'value' => '', + 'validate' => 'required', + 'required' => true + ], + 'testimonials_title' => [ + 'label' => 'Titre de la section n°7', + 'inputType' => InputType::string->value, + 'value' => 'Les témoignages des caravaniers', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'testimonials_items' => [ + 'label' => 'Slider de la section n°7', + 'inputType' => InputType::slider->value, + 'value' => '', + 'validate' => 'required|array|between:2,6', + 'required' => true, + 'columns' => [ + [ + 'id' => 'picture', + 'type' => 'picture', + 'placeholder' => Str::ucFirst(__('validation.custom.picture')), + 'validation' => 'required', + 'required' => true + ], + [ + 'id' => 'picture_alt', + 'type' => 'text', + 'placeholder' => Str::ucFirst(__('validation.custom.picture_alt')), + 'validation' => 'required|string|between:3,255', + 'required' => true + ], + [ + 'id' => 'picture_title', + 'type' => 'text', + 'placeholder' => Str::ucFirst(__('validation.custom.picture_title')), + 'validation' => 'required|string|between:3,255', + 'required' => true + ], + [ + 'id' => 'picture_author', + 'type' => 'text', + 'placeholder' => Str::ucFirst(__('validation.custom.picture_author')), + 'validation' => 'required|string|between:3,255', + 'required' => true + ], + [ + 'id' => 'title', + 'type' => 'text', + 'placeholder' => Str::ucFirst(__('validation.attributes.title')), + 'validation' => 'required|string|between:3,255', + 'required' => true + ], + [ + 'id' => 'subtitle', + 'type' => 'text', + 'placeholder' => Str::ucFirst(__('validation.custom.subtitle')), + 'validation' => 'nullable|string|between:3,255', + 'required' => false + ], + [ + 'id' => 'description', + 'type' => 'wysiwyg', + 'placeholder' => Str::ucFirst(__('validation.attributes.description')), + 'validation' => 'required|string|min:15', + 'required' => true + ], + [ + 'id' => 'subtitle_2', + 'type' => 'text', + 'placeholder' => Str::ucFirst(__('validation.custom.subtitle')), + 'validation' => 'required|string|between:3,255', + 'required' => true + ], + [ + 'id' => 'description_2', + 'type' => 'wysiwyg', + 'placeholder' => Str::ucFirst(__('validation.attributes.description')), + 'validation' => 'required|string|min:15', + 'required' => true + ], + ], + 'itemLimit' => [2, 6], + 'imageSizes' => [500, 333], + ], + ]; + + $staticPage = StaticPage::where('type', StaticPageType::tdf)->first(); + if ($staticPage) { + $staticPage->data = $data; + $staticPage->saveQuietly(); + } else { + StaticPage::query()->forceCreateQuietly( + [ + 'type' => StaticPageType::tdf->value(), + 'seo_title' => 'Le Gaulois | Volaille officielle du Tour de France', + 'seo_description' => 'Immergez-vous dans l\'ambiance du Tour de France avec Le Gaulois. Découvrez le jeu concours, les photos, anecdotes et témoignages de notre équipe.', + 'title' => 'Le Gaulois Le sponsor du Tour de France', + 'slug' => 'tour-de-france', + 'data' => $data + ] + ); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('static_pages', function () { + $staticPage = StaticPage::query()->where('type', StaticPageType::tdf)->first(); + $staticPage->data = []; + $staticPage->saveQuietly(); + }); + } +}; diff --git a/database/migrations/2026_05_07_144145_add_section_for_oui_c_bon_page.php b/database/migrations/2026_05_07_144145_add_section_for_oui_c_bon_page.php new file mode 100644 index 00000000..10a694c0 --- /dev/null +++ b/database/migrations/2026_05_07_144145_add_section_for_oui_c_bon_page.php @@ -0,0 +1,58 @@ + [ + 'label' => 'Rechercher un éleveur : Titre', + 'inputType' => 11, + 'value' => 'retrouvez tous nos éleveurs oui c\'est bon !', + 'validate' => 'required|string|between:3,255', + 'required' => true + ], + 'search_description' => [ + 'label' => 'Rechercher un éleveur : Description', + 'inputType' => 12, + 'value' => 'Parce que nous sommes fiers de notre savoir-faire français et de l\'origine France de nos produits, nous vous dévoilons les éleveurs partenaires et engagés au quotidien dans notre démarche Oui c\'est Bon Le Gaulois
', + 'validate' => 'required|string|min:15', + 'required' => true + ], + ]; + + $staticPage = StaticPage::where('type', StaticPageType::ouiCBon)->first(); + + if ($staticPage && !empty($staticPage->data)) { + $staticPage->data = array_merge($staticPage->data, $data); + $staticPage->saveQuietly(); + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $staticPage = StaticPage::where('type', StaticPageType::ouiCBon)->first(); + + if ($staticPage && !empty($staticPage->data)) { + $data = $staticPage->data; + + unset( + $data['search_title'], + $data['search_description'] + ); + + $staticPage->data = $data; + $staticPage->saveQuietly(); + } + } +}; diff --git a/public/newsletters/260521/assets/separation_2.png b/public/newsletters/260521/assets/separation_2.png new file mode 100644 index 00000000..cb8f8ce6 Binary files /dev/null and b/public/newsletters/260521/assets/separation_2.png differ diff --git a/public/newsletters/260521/index.html b/public/newsletters/260521/index.html index 3f24d6d0..384a6e8d 100644 --- a/public/newsletters/260521/index.html +++ b/public/newsletters/260521/index.html @@ -661,8 +661,7 @@ LES CROUSTY CHICKEN WINGS