feat: ✨ add offers integration part in recruitment page

This commit is contained in:
Alexis Gousseau 2024-07-15 14:56:46 +02:00
parent d01bf1d824
commit 1808006e18
9 changed files with 373 additions and 78 deletions

View file

@ -6,6 +6,7 @@ use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\BrandAdvantage;
use App\Models\KeyNumber;
use App\Models\Offer;
use App\Models\Profession;
use App\Models\StaticPage;
use App\Models\Video;
@ -57,9 +58,19 @@ class StaticPageController extends Controller
public function recruitment(StaticPage $staticPageModel): \Illuminate\Contracts\View\View
{
$staticPageModel = StaticPage::query()->where('type', StaticPageType::recruitment)->first();
$offerModels = Offer::query()
->where('published', true)
->orderBy('updated_at', 'DESC')
->take(6)
->get();
$professionModels = Profession::query()->where('published', true)->take(5)->get();
$brandAdvantageModels = BrandAdvantage::query()->where('published', true)->orderBy('order')->get();
return view('fo.pages.recruitment', compact('staticPageModel', 'professionModels', 'brandAdvantageModels'));
return view('fo.pages.recruitment', compact(
'staticPageModel',
'offerModels',
'professionModels',
'brandAdvantageModels'
));
}
/**

View file

@ -0,0 +1,109 @@
<?php
use App\Enums\InputType;
use App\Enums\StaticPageType;
use App\Models\StaticPage;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
// RECRUITMENT.
$staticPage = StaticPage::query()->where('type', StaticPageType::recruitment)->first();
$staticPage->data = [
"offers_title" => [
"label" => "Titre des offres",
"inputType" => InputType::string->value,
"value" => "le titre des offres",
"validate" => "required|string|between:3,255",
'required' => true
],
"offers_description" => [
"label" => "Description des offres",
"inputType" => InputType::text->value,
"value" => "la description des offres",
"validate" => "required|string|min:15",
'required' => true
],
"jobs_title" => [
"label" => "Titre des métiers",
"inputType" => InputType::string->value,
"value" => "le titre des métiers",
"validate" => "required|string|between:3,255",
'required' => true
],
"jobs_description" => [
"label" => "Description des métiers",
"inputType" => InputType::text->value,
"value" => "la description des métiers",
"validate" => "required|string|min:15",
'required' => true
],
"testimonies_title" => [
"label" => "Titre des témoignages",
"inputType" => InputType::string->value,
"value" => "le titre des témoignages",
"validate" => "required|string|between:3,255",
'required' => true
],
"testimonies_description" => [
"label" => "Description des témoignages",
"inputType" => InputType::text->value,
"value" => "la description des témoignages",
"validate" => "required|string|min:15",
'required' => true
],
"advantages_title" => [
"label" => "Titre des avantages",
"inputType" => InputType::string->value,
"value" => "le titre des avantages",
"validate" => "required|string|between:3,255",
'required' => true
],
"advantages_description" => [
"label" => "Description des avantages",
"inputType" => InputType::text->value,
"value" => "la description des avantages",
"validate" => "required|string|min:15",
'required' => true
],
"actualities_title" => [
"label" => "Titre des actualités",
"inputType" => InputType::string->value,
"value" => "le titre des actualités",
"validate" => "required|string|between:3,255",
'required' => true
],
"actualities_description" => [
"label" => "Description des actualités",
"inputType" => InputType::text->value,
"value" => "la description des actualités",
"validate" => "required|string|min:15",
'required' => true
],
];
$staticPage->saveQuietly();
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::table('static_pages', function () {
// RECRUITMENT.
$staticPage = StaticPage::query()->where('type', StaticPageType::recruitment)->first();
$staticPage->data = [];
$staticPage->saveQuietly();
});
}
};

View file

@ -1,11 +1,30 @@
.jobs {
.card {
&-professions {
max-width: calc(300px + (2 * 0.5rem));
#recruitment {
&-offers {
&-count {
top: -1rem;
right: -1rem;
width: 2rem;
height: 2rem;
}
&-body {
height: 13rem;
.swiper-recruitment-offers img {
width: 100%;
@include media-breakpoint-up(md) {
width: 50%;
}
}
}
&-jobs {
.card {
&-professions {
max-width: calc(300px + (2 * 0.5rem));
}
&-body {
height: 13rem;
}
}
}
}

View file

@ -141,6 +141,41 @@ document.addEventListener("DOMContentLoaded", function () {
// SWIPER
const swiperConfig = {
"swiper-recruitment-offers": {
modules: [Navigation, Keyboard, Pagination],
grabCursor: true,
slidesPerView: 1.3,
initialSlide: 0,
spaceBetween: 10,
navigation: {
nextEl: ".swiper-recruitment-offers-button-next",
prevEl: ".swiper-recruitment-offers-button-prev",
},
keyboard: {
enabled: true,
},
pagination: {
el: ".swiper-recruitment-offers-pagination",
clickable: true,
renderBullet: function (index: number, className: string) {
return "<button class=\"btn btn-tertiary rounded-circle border-0 mx-2 p-0 " + className + "\" title=\"Aller à la slide " + index + "\"></button>";
},
},
breakpoints: {
576: {
slidesPerView: 2,
spaceBetween: 25,
},
768: {
slidesPerView: 2,
spaceBetween: 35,
},
992: {
slidesPerView: 2,
spaceBetween: 55,
},
},
},
"swiper-recruitment-jobs": {
modules: [Navigation, Keyboard, Pagination],
grabCursor: true,

View file

@ -215,22 +215,35 @@
</div>
{{-- DESCRIPTION --}}
<div class="col-12 mb-3">
<fieldset class="border p-3">
<legend>{{ str(__('validation.attributes.description'))->ucfirst() }}</legend>
<div class="row mb-3">
<fieldset class="h-100 border p-3">
<legend>{{ str(__('validation.attributes.description'))->ucFirst() }}</legend>
<div class="row">
<div class="col-12">
@include('bo.partials.inputs.text-html', [
@include('bo.partials.inputs.varchar-multiline', [
'id' => 'offerDescription',
'label' => str(
__(':field :inter:model', [
'label' => __(':field :inter:model', [
'field' => str(__('validation.attributes.description'))->ucFirst(),
'inter' => __('validation.custom.of_the.vowel'),
'model' => __('models.classes.offer'),
]),
'hasWordCounter' => true,
'name' => 'description',
'required' => true,
'model' => $offerModel ?? null,
'helper' => __('validation.between.string', [
'attribute' => __(':field :inter:model', [
'field' => __('validation.attributes.description'),
'inter' => __('validation.custom.of_the.vowel'),
'model' => __('models.classes.offer'),
]))->ucfirst(),
'name' => 'description',
'model' => $offerModel ?? null,
'required' => true,
'helper' => __('crud.minicrud.text_warning'),
]),
'min' => 3,
'max' => 255,
]),
'placeholder' => __(':field :inter:model', [
'field' => str(__('validation.attributes.description'))->ucFirst(),
'inter' => __('validation.custom.of_the.vowel'),
'model' => __('models.classes.offer'),
]),
])
</div>
</div>

View file

@ -80,11 +80,9 @@
</fieldset>
</div>
@endif
@if (
!$staticPageModel->type->equals(StaticPageType::recruitment) &&
!$staticPageModel->type->equals(StaticPageType::recipe) &&
!$staticPageModel->type->equals(StaticPageType::product) &&
!$staticPageModel->type->equals(StaticPageType::home))
@if (!$staticPageModel->type->equals(StaticPageType::recipe) &&
!$staticPageModel->type->equals(StaticPageType::product) &&
!$staticPageModel->type->equals(StaticPageType::home))
{{-- TITLE --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="h-100 border p-3">

View file

@ -8,68 +8,130 @@
@section('content')
{{-- HEADER --}}
@include('fo.layouts.header-banner')
<div class="container">
<div class="row">
<div class="col-12">
<h1 class="f-intro-head-h-base text-center">{{ __($staticPageModel->title) }}</h1>
</div>
<div class="col-12 ck-content">
{!! clean($staticPageModel->content) !!}
</div>
</div>
</div>
{{-- PROFESSIONS --}}
<section class="jobs col-12" id="jobs">
<div class="row w-100 justify-content-center mx-auto overflow-hidden">
<div class="col-12">
<h1 class="f-intro-head-h-base text-center">{{ __('Nos métiers') }}</h1>
</div>
<div class="col-12 col-xxl-10 position-relative mb-5 p-0">
<div class="swiper swiper-recruitment-jobs mt-lg-0 mt-5 overflow-visible" id="swiper-recruitment-jobs">
<div class="swiper-wrapper">
@foreach ($professionModels as $professionModel)
<div class="swiper-slide">
@include('fo.partials.job-card', [
'title' => $professionModel->title,
'jobs_list' => $professionModel->jobs_list,
'picture' => [
'src' => asset($professionModel->picture),
'alt' => $professionModel->picture_alt,
'title' => $professionModel->picture_title,
],
'job_url' => $professionModel->job_url,
])
</div>
@endforeach
{{-- OFFERS --}}
<section id="recruitment-offers" class="position-relative line-border-large bg-light py-5">
<div class="container-fluid">
<div class="row w-100 justify-content-center mx-auto">
<div class="col-12">
<h1 class="f-intro-head-h-base text-center">{{ $staticPageModel->data['offers_title']['value'] }}</h1>
<p>{{ $staticPageModel->data['offers_description']['value'] }}</p>
</div>
<div class="col-12 col-xxl-10 position-relative overflow-hidden mb-5 p-0">
<div id="swiper-recruitment-offers"
class="swiper swiper-recruitment-offers overflow-visible mt-lg-0 mt-5">
<div class="swiper-wrapper w-fit">
@foreach ($offerModels as $offerModel)
<div class="swiper-slide">
@include('fo.partials.offer-card', [
'title' => $offerModel->title,
'location' => $offerModel->location,
'contract' => $offerModel->contract,
'function' => $offerModel->function,
'description' => $offerModel->description,
'picture' => [
'src' => asset($offerModel->picture),
'alt' => $offerModel->picture_alt,
'title' => $offerModel->picture_title,
],
])
</div>
@endforeach
</div>
</div>
<div
class="swiper-pagination swiper-recruitment-offers-pagination d-flex justify-content-center align-items-center w-100 mt-4">
</div>
<button
class="swiper-button swiper-button-prev-custom swiper-recruitment-offers-button-prev rounded-circle z-1 border-0 bg-white">
<img src="{{ Vite::asset('resources/assets/images/common/arrow-brown.svg') }}"
title="{{ __('Navigation du slider (gauche)') }}" alt="{{ __('flèche vers la gauche') }}">
</button>
<button
class="swiper-button swiper-button-next-custom swiper-recruitment-offers-button-next rounded-circle z-1 border-0 bg-white">
<img src="{{ Vite::asset('resources/assets/images/common/arrow-brown.svg') }}"
title="{{ __('Navigation du slider (droite)') }}" alt="{{ __('flèche vers la droite') }}">
</button>
</div>
<div class="swiper-pagination swiper-recruitment-jobs-pagination d-flex justify-content-center align-items-center w-100 mt-4">
<div class="col-12 text-center">
<a href="FIXME: link to the ldc recrutment page" class="btn btn-secondary position-relative"
title="{{ __('Accéder à la page des recrutements LDC') }}">
{{ __('voir plus d\'offres') }}
<span id="recruitment-offers-count"
class="position-absolute bg-warning text-secondary rounded-pill p-2">25</span>{{-- FIXME: replace number by the real count --}}
</a>
</div>
<button
class="swiper-button swiper-button-prev-custom swiper-recruitment-jobs-button-prev rounded-circle z-1 mx-3 border-0 bg-white">
<img src="{{ Vite::asset('resources/assets/images/common/arrow-brown.svg') }}" title="{{ __('Navigation du slider (gauche)') }}"
alt="{{ __('flèche vers la gauche') }}">
</button>
<button
class="swiper-button swiper-button-next-custom swiper-recruitment-jobs-button-next rounded-circle z-1 mx-3 border-0 bg-white">
<img src="{{ Vite::asset('resources/assets/images/common/arrow-brown.svg') }}" title="{{ __('Navigation du slider (droite)') }}"
alt="{{ __('flèche vers la droite') }}">
</button>
</div>
</div>
</section>
{{-- REASONS TO JOIN US --}}
<section class="joinus mb-3">
{{-- PROFESSIONS --}}
<section id="recruitment-jobs" class="position-relative">
<div class="container-fluid">
<div class="row w-100 justify-content-center mx-auto">
<div class="col-12">
<h1 class="f-intro-head-h-base text-center">{{ $staticPageModel->data['jobs_title']['value'] }}</h1>
<p>{{ $staticPageModel->data['jobs_description']['value'] }}</p>
</div>
<div class="col-12 col-xxl-10 position-relative overflow-hidden mb-5 p-0">
<div class="swiper swiper-recruitment-jobs mt-lg-0 mt-5 overflow-visible" id="swiper-recruitment-jobs">
<div class="swiper-wrapper">
@foreach ($professionModels as $professionModel)
<div class="swiper-slide">
@include('fo.partials.job-card', [
'title' => $professionModel->title,
'jobs_list' => $professionModel->jobs_list,
'picture' => [
'src' => asset($professionModel->picture),
'alt' => $professionModel->picture_alt,
'title' => $professionModel->picture_title,
],
'job_url' => $professionModel->job_url,
])
</div>
@endforeach
</div>
</div>
<div
class="swiper-pagination swiper-recruitment-jobs-pagination d-flex justify-content-center align-items-center w-100 mt-4">
</div>
<button
class="swiper-button swiper-button-prev-custom swiper-recruitment-jobs-button-prev rounded-circle z-1 border-0 bg-white">
<img src="{{ Vite::asset('resources/assets/images/common/arrow-brown.svg') }}"
title="{{ __('Navigation du slider (gauche)') }}" alt="{{ __('flèche vers la gauche') }}">
</button>
<button
class="swiper-button swiper-button-next-custom swiper-recruitment-jobs-button-next rounded-circle z-1 border-0 bg-white">
<img src="{{ Vite::asset('resources/assets/images/common/arrow-brown.svg') }}"
title="{{ __('Navigation du slider (droite)') }}" alt="{{ __('flèche vers la droite') }}">
</button>
</div>
</div>
</div>
</section>
{{-- TESTIMONIES --}}
<section id="recruitment-testimonies" class="line-border-large bg-light py-5">
<div class="container pt-5 pt-lg-0">
<div class="row">
<div class="col-12">
<h1 class="f-intro-head-h-base text-center">{{ $staticPageModel->data['testimonies_title']['value'] }}
</h1>
<p>{{ $staticPageModel->data['testimonies_description']['value'] }}</p>
</div>
<div class="col-12 ck-content">
{{-- content --}}
</div>
</div>
</div>
</section>
{{-- ADVANTAGES --}}
<section id="recruitment-advantages" class="joinus mb-3">
<div class="line-border-large bg-tertiary py-5">
<div class="container">
<div class="row">
<div class="col-12">
<h1 class="f-intro-head-h-base text-center">{{ __("Pourquoi nous rejoindre\u{00A0}?") }}</h1>
<p>{{ __('Lorem, ipsum dolor sit amet consectetur adipisicing elit. Excepturi commodi ipsa quas laudantium consequatur explicabo quasi voluptatum vitae dolorem dolores necessitatibus numquam, praesentium quod doloribus! Atque quisquam ullam modi exercitationem.') }}
</p>
<h1 class="f-intro-head-h-base text-center">
{{ $staticPageModel->data['advantages_title']['value'] }}</h1>
<p>{{ $staticPageModel->data['advantages_description']['value'] }}</p>
</div>
</div>
<div class="row justify-content-center">
@foreach ($brandAdvantageModels as $brandAdvantageModel)
<div @class([
'col-12 my-3 bottom-left-shadow',
@ -80,7 +142,8 @@
<div class="row hstack py-2">
<div @class(['col-3', 'col-lg-4' => !$brandAdvantageModel->is_large])>
<img class="img-fluid" src="{{ asset($brandAdvantageModel->picto) }}"
title="{{ $brandAdvantageModel->picto_title }}" alt="{{ $brandAdvantageModel->picto_alt }}">
title="{{ $brandAdvantageModel->picto_title }}"
alt="{{ $brandAdvantageModel->picto_alt }}">
</div>
<div @class(['col-9', 'col-lg-8' => !$brandAdvantageModel->is_large])>
<p class="f-bourton-base fs-5 m-0">{{ $brandAdvantageModel->sustitle }}</p>
@ -116,4 +179,19 @@
</div>
</div> --}}
</section>
{{-- ACTUALITIES --}}
<section id="recruitment-actualities" class="line-border-large bg-light py-5">
<div class="container pt-5 pt-lg-0">
<div class="row">
<div class="col-12">
<h1 class="f-intro-head-h-base text-center">{{ $staticPageModel->data['actualities_title']['value'] }}
</h1>
<p>{{ $staticPageModel->data['actualities_description']['value'] }}</p>
</div>
<div class="col-12 ck-content">
{{-- content --}}
</div>
</div>
</div>
</section>
@endsection

View file

@ -0,0 +1,34 @@
<div class="news-small-card bottom-left-shadow border-0 bg-transparent">
<a class="text-decoration-none" href="#CHANGEME: article.show" target="_blank"
title="{{ __('Accéder à l\'article : :name', ['name' => '#CHANGEME']) }}">
<div class="line-border-small bg-white p-md-3">
<div class="row w-100 mx-auto align-items-center">
<div class="col-12 d-flex flex-column flex-md-row justify-content-center align-items-center p-0">
<img class="img-fluid"
src="{{ $picture['src'] }}"
title="{{ $picture['title'] }}" alt="{{ $picture['alt'] }}">
<div class="d-flex flex-column justify-content-start justify-content-md-center align-items-start w-100 mb-2 mt-3 m-md-0 ms-md-2 px-3 px-md-0">
<h4>{{ $title }}</h4>
<p class="mb-0">
{{ Str::limit($description, 180) }}
@if(Str::length($description) >= 180)
<span class="text-quad text-decoration-underline">{{ __('Lire plus') }}</span>
@endcan
</p>
</div>
</div>
<div class="col-12 d-flex flex-wrap p-3 pt-0 p-md-0 mt-md-1">
<div class="bg-quad text-uppercase f-bourton-base text-white fs-5 mt-2 me-3 px-2 py-1">
{{ $contract }}
</div>
<div class="bg-secondary text-uppercase f-bourton-base text-white fs-5 mt-2 me-3 px-2 py-1">
{{ $location }}
</div>
<div class="bg-tertiary text-uppercase f-bourton-base fs-5 mt-2 me-3 px-2 py-1">
{{ $function }}
</div>
</div>
</div>
</div>
</a>
</div>

View file

@ -323,9 +323,7 @@ Route::middleware(['throttle:web'])->prefix('bo')->name('bo.')->group(function (
->name('products.json-paginate');
// * OFFERS.
Route::resource('offers', OfferController::class)
->withoutMiddleware('htmlsanitize')
->middleware('htmlsanitize:description');
Route::resource('offers', OfferController::class);
Route::patch('/offers/{offer}/change-published', [
OfferController::class, 'changePublished'
])->name('offers.change-published');