Merge branch 'ocb-search' into 'develop'
feat: ✨ add ocb breeders search
See merge request ldc-web/projets/le-gaulois!185
This commit is contained in:
commit
629f397745
11 changed files with 406 additions and 3 deletions
|
|
@ -6,6 +6,7 @@ use App\Enums\Settings\SettingsType;
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Breeder;
|
||||
use App\Models\GeneralSetting;
|
||||
use App\Models\OcbBreeder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
|
@ -59,7 +60,7 @@ class BreederController extends Controller
|
|||
/**
|
||||
* Get all the breeders views in a given department.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getDepartmentBreedersViews(Request $request): array
|
||||
|
|
@ -86,4 +87,15 @@ class BreederController extends Controller
|
|||
{
|
||||
return GeneralSetting::query()->where('type', SettingsType::breederMap)->first()->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the breeders views in a given department.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function searchOcbBreedersCards(Request $request): array
|
||||
{
|
||||
return OcbBreeder::getOcbBreederCards($request->page, $request->search);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers\Fo;
|
|||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\NewsBreeding;
|
||||
use App\Models\OcbBreeder;
|
||||
use App\Models\StaticPage;
|
||||
|
||||
class BreedingController extends Controller
|
||||
|
|
@ -23,6 +24,19 @@ class BreedingController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the given breeding page.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function OcbBreederPage(): \Illuminate\Contracts\View\View
|
||||
{
|
||||
$ocbBreederModels = OcbBreeder::getOcbBreederCards();
|
||||
return view('fo.pages.breeding.oui-c-est-bon', [
|
||||
'ocbBreederModels' => $ocbBreederModels,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the given breeding article.
|
||||
*
|
||||
|
|
|
|||
62
app/Models/OcbBreeder.php
Normal file
62
app/Models/OcbBreeder.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Breeders\OCBType;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property string $first_name OCB breeder first name.
|
||||
* @property string $last_name OCB breeder last name.
|
||||
* @property string $department OCB breeder department.
|
||||
* @property string $postal_code OCB breeder postal code.
|
||||
* @property \App\Enums\Breeders\OCBType $type OCB breeder type.
|
||||
* @property-read \Carbon\Carbon $created_at Created date.
|
||||
* @property-read \Carbon\Carbon $updated_at Updated date.
|
||||
*/
|
||||
class OcbBreeder extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<integer, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'first_name',
|
||||
'last_name',
|
||||
'department',
|
||||
'postal_code',
|
||||
'type',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string,string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'type' => OCBType::class
|
||||
];
|
||||
|
||||
// * METHODS.
|
||||
|
||||
/**
|
||||
* Created at nice date.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public static function getOcbBreederCards(int $page = 1, string|null $search = null): array
|
||||
{
|
||||
return OcbBreeder::query()
|
||||
->when($search, function (Builder $query) use ($search) {
|
||||
$query->where('first_name', 'like', '%' . $search . '%')
|
||||
->orWhere('last_name', 'like', '%' . $search . '%');
|
||||
})
|
||||
->paginate(perPage: 9, page: $page)
|
||||
->through(function (OcbBreeder $ocbBreederModel) {
|
||||
return view('components.fo.breeding.card-ocb-breeder', ['ocbBreederModel' => $ocbBreederModel])->render();
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('ocb_breeders', function (Blueprint $table) {
|
||||
$table->id()->comment('OCB breeder id.');
|
||||
$table->string('first_name')->comment('OCB breeder first name.');
|
||||
$table->string('last_name')->comment('OCB breeder last name.');
|
||||
$table->string('department')->comment('OCB breeder department.');
|
||||
$table->string('postal_code')->comment('OCB breeder postal code.');
|
||||
$table->tinyInteger('type')->comment('OCB breeder type.');
|
||||
$table->timestamp('created_at')->nullable()->comment('OCB breeder created at date.');
|
||||
$table->timestamp('updated_at')->nullable()->comment('OCB breeder updated at date.');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('ocb_breeders');
|
||||
}
|
||||
};
|
||||
241
resources/ts/fo/components/SearchBreederOcbComponent.vue
Normal file
241
resources/ts/fo/components/SearchBreederOcbComponent.vue
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
<template>
|
||||
<div class="search-breeder-ocb-component">
|
||||
<div class="row justify-content-center">
|
||||
<!-- SEARCH INPUT -->
|
||||
<div class="col-12 col-md-6 my-5">
|
||||
<div class="input-group">
|
||||
<input
|
||||
ref="inputSearch"
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="search-breeder-ocb"
|
||||
:placeholder="placeholder"
|
||||
>
|
||||
<button
|
||||
v-if="inputSearch?.value.length"
|
||||
class="btn btn-tertiary"
|
||||
:title="trans.methods.__('Effacer la recherche')"
|
||||
@click="resetSearch()"
|
||||
type="button"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon="fa-solid fa-xmark"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-secondary"
|
||||
:title="trans.methods.__('Lancer la recherche')"
|
||||
@click="searchBreederOCB()"
|
||||
type="button"
|
||||
>
|
||||
{{ trans.methods.__('rechercher') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- SEARCH RESULTS -->
|
||||
<div class="col-12">
|
||||
<!-- RESULT CONTENT -->
|
||||
<div
|
||||
v-if="cards.length"
|
||||
class="row justify-content-center"
|
||||
>
|
||||
<template v-if="!loading">
|
||||
<template
|
||||
v-for="(card, cardIndex) in cards"
|
||||
:key="cardIndex"
|
||||
>
|
||||
<div
|
||||
class="col-12 col-sm-6 col-lg-4 mb-3"
|
||||
v-html="card"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="loading || paginationParameters.loading">
|
||||
<div class="col-12 col-sm-6 col-lg-4 mb-3">
|
||||
<div class="placeholder-glow bg-light position-relative line-border-small bg-white d-flex flex-column justify-content-start p-3">
|
||||
<span class="placeholder col-8" />
|
||||
<span class="placeholder col-5 mt-2" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<!-- MESSAGES & PAGINATION -->
|
||||
<template v-if="!loading">
|
||||
<div
|
||||
v-if="paginationParameters.page < paginationParameters.lastPage"
|
||||
class="col-12 text-center mb-5"
|
||||
>
|
||||
<button
|
||||
id="btn-arrow"
|
||||
class="btn btn-secondary"
|
||||
type="button"
|
||||
@click="searchBreederOCB(true)"
|
||||
>
|
||||
{{ trans.methods.__("charger les résultats suivants") }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="!cards.length"
|
||||
class="col-12 text-center mb-5"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon="fa-solid fa-triangle-exclamation"
|
||||
class="no-result-icon"
|
||||
/>
|
||||
<p class="text-body-primary text-center fst-italic my-2">
|
||||
{{ trans.methods.__('Aucun élément n\'est disponible') }}
|
||||
</p>
|
||||
<button
|
||||
class="btn btn-sm btn-secondary"
|
||||
type="button"
|
||||
:title="trans.methods.__('Effacer la recherche')"
|
||||
@click="resetSearch()"
|
||||
>
|
||||
{{ trans.methods.__('Effacer la recherche') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="col-12 mb-5"
|
||||
>
|
||||
<p class="text-body-primary text-center fst-italic m-0">
|
||||
{{ trans.methods.__('Tous les éléments sont chargés') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineOptions, onMounted, useAttrs, ref, computed, reactive } from "vue";
|
||||
import parseLaravelErrorMessages from "./../../modules/parseLaravelErrorMessages";
|
||||
import trans from "../../modules/trans";
|
||||
|
||||
defineOptions({
|
||||
name: "SearchBreederOcbComponent"
|
||||
});
|
||||
|
||||
// * ATTRIBUTES
|
||||
const attrs = useAttrs();
|
||||
|
||||
// * REFS
|
||||
const inputSearch = ref<HTMLInputElement|null>(null);
|
||||
|
||||
// * DATA
|
||||
const routeUrl = ref<string>("");
|
||||
const cards = ref<Array<string>>([]);
|
||||
const placeholder = ref<string>("");
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
/** Pagination parameters. */
|
||||
const paginationParameters = reactive<{
|
||||
page: number,
|
||||
itemPerPage: number,
|
||||
lastPage: number,
|
||||
loading: boolean,
|
||||
}>({
|
||||
page: 1,
|
||||
itemPerPage: 1,
|
||||
lastPage: 1,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
// * COMPUTED
|
||||
const isUsedWithProps = computed<boolean>(() => attrs.json === undefined);
|
||||
|
||||
// * LIFECYCLE
|
||||
onMounted(() => {
|
||||
const json = String(attrs.json ?? "{}"),
|
||||
data = JSON.parse(json);
|
||||
if (!isUsedWithProps.value) {
|
||||
routeUrl.value = data.routeUrl;
|
||||
cards.value = data.cards.data;
|
||||
placeholder.value = data.placeholder;
|
||||
|
||||
/** Pagination parameters. */
|
||||
paginationParameters.page = data.cards.current_page;
|
||||
paginationParameters.itemPerPage = data.cards.per_page;
|
||||
paginationParameters.lastPage = data.cards.last_page;
|
||||
}
|
||||
});
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Reset all filters activated.
|
||||
* @return void
|
||||
*/
|
||||
function resetSearch(): void {
|
||||
inputSearch.value!.value = "";
|
||||
searchBreederOCB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get models by text search.
|
||||
* @return void
|
||||
*/
|
||||
function searchBreederOCB(concat: boolean = false): void {
|
||||
if (!inputSearch.value) {
|
||||
throw new Error("Input doesn't exist");
|
||||
}
|
||||
if (!routeUrl.value) {
|
||||
throw new Error(`Route ${routeUrl.value} doesn't exist`);
|
||||
}
|
||||
paginationParameters.page = (concat) ? paginationParameters.page + 1 : 1;
|
||||
(concat) ? paginationParameters.loading = true : loading.value = true;
|
||||
window.axios
|
||||
.post(routeUrl.value, {"search": inputSearch.value.value, "page": paginationParameters.page})
|
||||
.then((response) => {
|
||||
paginationParameters.page = response.data.current_page;
|
||||
paginationParameters.lastPage = response.data.last_page;
|
||||
cards.value = (concat) ? cards.value.concat(response.data.data) : response.data.data;
|
||||
}).then(() => {
|
||||
(concat) ? paginationParameters.loading = false : loading.value = false;
|
||||
})
|
||||
.catch(ajaxErrorHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return error message after the ajax call.
|
||||
* @return void
|
||||
*/
|
||||
function ajaxErrorHandler(e: any): void {
|
||||
let message = trans.methods.__("Une erreur est survenue");
|
||||
if (e.response.status === 422) {
|
||||
message = parseLaravelErrorMessages.methods.parseValidationErrors(
|
||||
e.response?.data?.errors ?? {}
|
||||
) || message;
|
||||
if (window.vueDebug) {
|
||||
console.warn(e.response.data.errors);
|
||||
}
|
||||
} else if (e.response.status === 419) {
|
||||
console.log(trans.methods.__("Votre session a expiré, la page va être rechargée"));
|
||||
setTimeout(() => window.location.reload(), 2000);
|
||||
} else if (e.response.status === 403) {
|
||||
console.log(trans.methods.__("Vous n'êtes pas autorisé a effectuer cette action"));
|
||||
setTimeout(() => window.location.reload(), 2000);
|
||||
} else {
|
||||
if (window.vueDebug) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
console.log(message);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scopped>
|
||||
.placeholder-glow {
|
||||
.placeholder:first-of-type {
|
||||
height: 2rem;
|
||||
}
|
||||
.placeholder:last-of-type {
|
||||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
.no-result-icon {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import { config, dom, library } from "@fortawesome/fontawesome-svg-core";
|
||||
import { faUser } from "@fortawesome/free-regular-svg-icons";
|
||||
import {
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
faCircleCheck,
|
||||
faCircleExclamation,
|
||||
faCircleInfo,
|
||||
|
|
@ -34,7 +36,9 @@ library.add(
|
|||
faCircleQuestion,
|
||||
faCircleXmark,
|
||||
faHeart,
|
||||
farHeart
|
||||
farHeart,
|
||||
faArrowLeft,
|
||||
faArrowRight,
|
||||
);
|
||||
|
||||
dom.watch();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
<div class="bg-light position-relative line-border-small bg-white d-flex flex-column justify-content-start p-3">
|
||||
<p class="h3">{{ sprintf('%s %s', $ocbBreederModel->first_name, $ocbBreederModel->last_name) }}</p>
|
||||
<small class="f-bakerie-rough text-success fw-bold h4">{{ sprintf('Éleveur du/de %s - %s', $ocbBreederModel->department, $ocbBreederModel->postal_code) }}</small>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<div class="container position-relative pb-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12">
|
||||
<h2 class="h1 f-intro-head-h-base section-title pb-2 text-center">
|
||||
{{ __('Retrouvez tous nos éleveurs Oui c\'est Bon !') }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 text-center">
|
||||
<p class="mb-0">{{ __('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') }}</p>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
@php
|
||||
$data = [
|
||||
'cards' => $ocbBreederModels,
|
||||
'routeUrl' => route('fo.breeders.search-ocb'),
|
||||
'placeholder' => __('Nom de l\'éleveur'),
|
||||
];
|
||||
@endphp
|
||||
<div class="search-breeder-ocb" data-json='@json($data)'></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
<div class="bg-green-wood h-100 w-100" id="demarche-bg"></div>
|
||||
</div>
|
||||
<x-fo.breeding.section.oui-c-est-bon.cahier-des-charges-section />
|
||||
<x-fo.breeding.section.oui-c-est-bon.search-breeders-section :ocbBreederModels="$ocbBreederModels" />
|
||||
<x-fo.breeding.section.oui-c-est-bon.our-breeder-section />
|
||||
</section>
|
||||
{{-- <section class="pb-3 pt-5" id="breeder-map-section">
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
'{{ route('fo.breeders.get-department-breeders-views', ['department' => 'DEPARTMENT']) }}';
|
||||
window.__SYSTEM._routes['fo.breeders.get-manageable-text-component'] =
|
||||
'{{ route('fo.breeders.get-manageable-text-component') }}';
|
||||
window.__SYSTEM._routes['fo.breeders.search-ocb'] = '{{ route('fo.breeders.search-ocb') }}';
|
||||
window.__SYSTEM._routes['fo.news.filtered'] = '{{ route('fo.news.filtered', ['legend_slug' => 'LEGENDSLUG']) }}';
|
||||
// CLUB
|
||||
window.__SYSTEM._routes['club.departaimants.save-state'] = '{{ route('club.departaimants.save-state', ['collectiontype' => 'TYPE']) }}';
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ Route::middleware(['throttle:web'])->name('fo.')->group(function () {
|
|||
->name('breeding.')
|
||||
->group(function () {
|
||||
Route::view('/', 'fo.pages.breeding.index')->name('index');
|
||||
Route::view('/oui-c-est-bon', 'fo.pages.breeding.oui-c-est-bon')->name('oui-c-est-bon');
|
||||
Route::get('/oui-c-est-bon', [BreedingController::class, 'OCBbreederPage'])->name('oui-c-est-bon');
|
||||
Route::view('/sortant-a-l-exterieur', 'fo.pages.breeding.sortant-a-l-exterieur')
|
||||
->name('sortant-a-l-exterieur');
|
||||
Route::get('/{breeding_static_page:slug}', [BreedingController::class, 'breedingPage'])
|
||||
|
|
@ -164,5 +164,9 @@ Route::middleware(['throttle:web'])->name('fo.')->group(function () {
|
|||
->name('get-department-breeders-views');
|
||||
Route::get('/get-manageable-text-component', [BreederController::class, 'getManageableTextComponent'])
|
||||
->name('get-manageable-text-component');
|
||||
|
||||
// * OCB BREEDERS.
|
||||
Route::post('/recherche-ocb-eleveurs', [BreederController::class, 'searchOcbBreedersCards'])
|
||||
->name('search-ocb');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue