Merge branch 'develop' into trunk
This commit is contained in:
commit
44ca6a3055
26 changed files with 450 additions and 32 deletions
|
|
@ -7,9 +7,11 @@ use App\Enums\TagTypeEnum;
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Advice\StoreAdviceRequest;
|
||||
use App\Http\Requests\Bo\Advice\UpdateAdviceRequest;
|
||||
use App\Http\Requests\Bo\UpdateOrderRequest;
|
||||
use App\Models\Advice;
|
||||
use App\Models\Product;
|
||||
use App\Models\Tag;
|
||||
use App\Traits\Controllers\ManagesOrder;
|
||||
use App\Traits\Controllers\UpdateModelPublished;
|
||||
use Illuminate\Contracts\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
|
|
@ -20,6 +22,7 @@ use Illuminate\Support\Str;
|
|||
class AdviceController extends Controller
|
||||
{
|
||||
use UpdateModelPublished;
|
||||
use ManagesOrder;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use App\Http\Requests\Bo\BrigadeSections\UpdateBrigadeSectionsRequest;
|
|||
use App\Models\BrigadeSection;
|
||||
use App\Traits\Controllers\ChangesModelOrder;
|
||||
use App\Traits\Controllers\UpdateModelPublished;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Uwil\Laravel\Common\Controllers\Controller;
|
||||
|
|
@ -69,7 +70,7 @@ class BrigadeSectionController extends Controller
|
|||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\BrigadeSections\StoreBrigadeSectionRequest $request
|
||||
* @param \App\Http\Requests\Bo\BrigadeSections\StoreBrigadeSectionsRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreBrigadeSectionsRequest $request): \Illuminate\Http\RedirectResponse
|
||||
|
|
@ -111,7 +112,7 @@ class BrigadeSectionController extends Controller
|
|||
* @param \App\Models\BrigadeSection $brigadeSection
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(UpdateBrigadeSectionsRequest $request, BrigadeSection $brigadeSection): \Illuminate\Http\RedirectResponse
|
||||
public function update(UpdateBrigadeSectionsRequest $request, BrigadeSection $brigadeSection): RedirectResponse
|
||||
{
|
||||
return DB::transaction(function () use ($request, $brigadeSection) {
|
||||
$brigadeSection->fill($request->validated());
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use App\Enums\TagTypeEnum;
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Products\StoreProductRequest;
|
||||
use App\Http\Requests\Bo\Products\UpdateProductRequest;
|
||||
use App\Http\Requests\Bo\UpdateOrderRequest;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Models\Product;
|
||||
use App\Models\Range;
|
||||
|
|
@ -13,6 +14,7 @@ use App\Models\Subrange;
|
|||
use App\Models\Tag;
|
||||
use App\Traits\Controllers\UpdateModelPublished;
|
||||
use App\Traits\Controllers\ChangesModelOrder;
|
||||
use App\Traits\Controllers\ManagesOrder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
|
@ -21,6 +23,7 @@ class ProductController extends Controller
|
|||
{
|
||||
use ChangesModelOrder;
|
||||
use UpdateModelPublished;
|
||||
use ManagesOrder;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use App\Http\Controllers\Controller;
|
|||
use App\Http\Requests\Fo\Contact\ContactRequest;
|
||||
use App\Mail\ContactFormMail;
|
||||
use App\Models\StaticPage;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use LDC\LDCCRM\Exceptions\ApiErrorException;
|
||||
|
|
@ -35,6 +37,14 @@ class ContactController extends Controller
|
|||
*/
|
||||
public function contactSubmit(ContactRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$recipients = config('mail.contactsList.contactform');
|
||||
|
||||
if (empty($recipients)) {
|
||||
Log::warning('Aucun destinataire configuré pour le formulaire de contact.');
|
||||
return back()
|
||||
->withErrors(['global' => __('Une erreur est survenue, veuillez réessayer plus tard.')])
|
||||
->withInput();
|
||||
}
|
||||
$redirection = back()->with('success', trans(
|
||||
'Votre message a bien été envoyé, merci de votre intérêt, nous répondrons dans les meilleurs délais.'
|
||||
));
|
||||
|
|
@ -57,14 +67,32 @@ class ContactController extends Controller
|
|||
}
|
||||
} //end if
|
||||
|
||||
foreach (config('mail.contactsList.contactform') as $recipient) {
|
||||
foreach ($recipients as $recipient) {
|
||||
Mail::to($recipient)->send(new ContactFormMail($request));
|
||||
}
|
||||
|
||||
Session::forget('saveProductInBasket');
|
||||
|
||||
Session::forget('contact_draft');
|
||||
|
||||
Session::flash('matomo_generate_lead');
|
||||
|
||||
return $redirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data form as draft
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function saveDraft(Request $request): \Illuminate\Http\JsonResponse
|
||||
{
|
||||
$request->session()->put('contact_draft', $request->only([
|
||||
'firstname', 'lastname', 'email', 'phone',
|
||||
'activity', 'society', 'postcode', 'city',
|
||||
'message', 'callTime', 'opt_in',
|
||||
]));
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
72
app/Http/Requests/Bo/UpdateOrderRequest.php
Normal file
72
app/Http/Requests/Bo/UpdateOrderRequest.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateOrderRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Attribute model needed.
|
||||
*
|
||||
* @var Model $orderable
|
||||
*/
|
||||
private Model $orderable;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->orderable = collect($this->route()->parameters())
|
||||
->first(fn($param) => $param instanceof Model);
|
||||
|
||||
$groupField = $this->orderable->getOrderGroupField();
|
||||
|
||||
if ($groupField) {
|
||||
$this->orderable::query()->where($groupField, $this->orderable->{$groupField});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'priority_order' => [
|
||||
'required',
|
||||
'integer',
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
function ($attribute, $value, $fail) {
|
||||
if ($this->orderable && intval($value) < 1) {
|
||||
$fail(trans('validation.custom.min_priority'));
|
||||
}
|
||||
},
|
||||
sprintf('max:%s', $this->orderable->max('priority_order')),
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
function ($attribute, $value, $fail) {
|
||||
if ($this->orderable && intval($value) === $this->orderable->priority_order) {
|
||||
$fail(trans('validation.custom.same_priority'));
|
||||
}
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ use App\Enums\Advices\AdviceTypeEnum;
|
|||
use App\Enums\Advices\AdviceDifficultyEnum;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Traits\Models\Orderable;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
|
|
@ -46,6 +47,7 @@ use Uwil\Laravel\Redirections\Traits\IsRedirectable;
|
|||
* @property integer|null $preparation_time Time of preparation.
|
||||
* @property \App\Enums\Advices\AdviceDifficultyEnum|null $difficulty Difficulty.
|
||||
* @property boolean $published Status.
|
||||
* @property integer $priority_order Priority Order.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Created date.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
|
||||
*
|
||||
|
|
@ -65,6 +67,7 @@ class Advice extends Authenticatable implements SeoInterface, Sitemapable, Redir
|
|||
use HasFactory;
|
||||
use SeoTrait;
|
||||
use IsRedirectable;
|
||||
use Orderable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
|
@ -95,7 +98,8 @@ class Advice extends Authenticatable implements SeoInterface, Sitemapable, Redir
|
|||
'cooking_time',
|
||||
'preparation_time',
|
||||
'difficulty',
|
||||
'published'
|
||||
'published',
|
||||
'priority_order'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -119,6 +123,7 @@ class Advice extends Authenticatable implements SeoInterface, Sitemapable, Redir
|
|||
{
|
||||
static::creating(function (self $model) {
|
||||
self::manageFields($model);
|
||||
self::bootOrderable($model);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
self::manageFields($model);
|
||||
|
|
@ -133,6 +138,7 @@ class Advice extends Authenticatable implements SeoInterface, Sitemapable, Redir
|
|||
FileStorageHelper::removeFile($model, 'picture');
|
||||
FileStorageHelper::removeFile($model, 'sticker');
|
||||
FileStorageHelper::removeFile($model, 'sticker_sm');
|
||||
self::setOrder($model);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -375,4 +381,15 @@ class Advice extends Authenticatable implements SeoInterface, Sitemapable, Redir
|
|||
$params = ['publishedAdvice' => $model];
|
||||
return SitemapHelper::generateUrl($route, $params, $model);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Group ordering by type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOrderGroupField(): string
|
||||
{
|
||||
return 'type';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class BrigadeSection extends Model
|
|||
* @param self $model
|
||||
* @param boolean $isDeletion
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
* @throws \Illuminate\Validation\ValidationException Returns if fails.
|
||||
*/
|
||||
private static function assertLimitPublishedSections(self $model, bool $isDeletion = false)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use App\Lib\Helpers\FileStorageHelper;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Sitemap\Contracts\Sitemapable;
|
||||
use App\Enums\Products\ProductSpeciesEnum;
|
||||
use App\Traits\Models\Orderable;
|
||||
use Uwil\SitemapGenerator\Lib\SitemapHelper;
|
||||
use Uwil\Seo\Laravel\Interfaces\SeoInterface;
|
||||
use Uwil\Laravel\Redirections\Traits\IsRedirectable;
|
||||
|
|
@ -42,6 +43,7 @@ use Uwil\Laravel\Redirections\Interfaces\RedirectableInterface;
|
|||
* @property boolean $is_egalim Is egalim or not.
|
||||
* @property boolean $published Status.
|
||||
* @property integer $order Order.
|
||||
* @property integer $priority_order Priority Order.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Created date.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
|
||||
*
|
||||
|
|
@ -56,6 +58,7 @@ class Product extends Model implements SeoInterface, Sitemapable, RedirectableIn
|
|||
use HasFactory;
|
||||
use SeoTrait;
|
||||
use IsRedirectable;
|
||||
use Orderable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
|
@ -83,7 +86,8 @@ class Product extends Model implements SeoInterface, Sitemapable, RedirectableIn
|
|||
'is_vip',
|
||||
'is_egalim',
|
||||
'order',
|
||||
'published'
|
||||
'published',
|
||||
'priority_order',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -113,7 +117,7 @@ class Product extends Model implements SeoInterface, Sitemapable, RedirectableIn
|
|||
// * EVENT CLOSURES.
|
||||
static::creating(function (self $model) {
|
||||
self::manageFields($model);
|
||||
$model->order = \intval(self::query()->max('order')) + 1;
|
||||
self::bootOrderable($model);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
self::manageFields($model);
|
||||
|
|
@ -131,6 +135,7 @@ class Product extends Model implements SeoInterface, Sitemapable, RedirectableIn
|
|||
$model,
|
||||
!is_array($model->getOriginal('slider_pictures')) ? [] : $model->getOriginal('slider_pictures'),
|
||||
);
|
||||
self::setOrder($model);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
55
app/Traits/Controllers/ManagesOrder.php
Normal file
55
app/Traits/Controllers/ManagesOrder.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Controllers;
|
||||
|
||||
use App\Http\Requests\Bo\UpdateOrderRequest;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
trait ManagesOrder
|
||||
{
|
||||
/**
|
||||
* Updates the order of a model and reorders all impacted elements.
|
||||
*
|
||||
* Shifts by +1 or -1 all elements located between the old
|
||||
* and new position of the model, then assigns the new position
|
||||
* to the target model. The whole operation runs inside a
|
||||
* database transaction to ensure data consistency.
|
||||
*
|
||||
* @param UpdateOrderRequest $request The validated request containing the 'priority' field.
|
||||
* @param Model $model The model to reorder (must use the Orderable trait).
|
||||
* @return RedirectResponse
|
||||
*
|
||||
* @throws \Throwable Returns if save fail.
|
||||
*/
|
||||
public function changeOrderPriority(UpdateOrderRequest $request, Model $model): RedirectResponse
|
||||
{
|
||||
$newPriority = (int) $request->validated('priority_order');
|
||||
|
||||
return DB::transaction(function () use ($model, $newPriority): RedirectResponse {
|
||||
|
||||
$query = $model::query()
|
||||
->whereKeyNot($model)
|
||||
->whereBetween('priority_order', [
|
||||
min($model->priority_order, $newPriority),
|
||||
max($model->priority_order, $newPriority),
|
||||
]);
|
||||
|
||||
$groupField = $model->getOrderGroupField();
|
||||
if ($groupField) {
|
||||
$query->where($groupField, $model->{$groupField});
|
||||
}
|
||||
|
||||
$model->priority_order < $newPriority
|
||||
? $query->decrement('priority_order', 1)
|
||||
: $query->increment('priority_order', 1);
|
||||
|
||||
$model->priority_order = $newPriority;
|
||||
$model->saveOrFail();
|
||||
|
||||
return redirect()->back()->with('success', trans('crud.messages.order_changed'));
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
61
app/Traits/Models/Orderable.php
Normal file
61
app/Traits/Models/Orderable.php
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
trait Orderable
|
||||
{
|
||||
/**
|
||||
* Returns the grouping field used for ordering.
|
||||
* Returns null if the order is global
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOrderGroupField(): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* shifts down by 1 all elements positioned after the delete.
|
||||
*
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
public static function setOrder(Model $model): void
|
||||
{
|
||||
$groupField = $model->getOrderGroupField();
|
||||
$query = $model::query()->where('priority_order', '>', $model->priority_order);
|
||||
|
||||
if ($groupField) {
|
||||
$query->where($groupField, $model->{$groupField});
|
||||
}
|
||||
|
||||
$query->each(function (Model $item): void {
|
||||
$item->priority_order -= 1;
|
||||
$item->saveOrFail();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the next order value on creation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function bootOrderable(): void
|
||||
{
|
||||
static::creating(function (Model $model): void {
|
||||
if (!$model->priority_order) {
|
||||
$groupField = $model->getOrderGroupField();
|
||||
$query = static::query();
|
||||
|
||||
if ($groupField) {
|
||||
$query->where($groupField, $model->{$groupField});
|
||||
}
|
||||
|
||||
$model->priority_order = ($query->max('priority_order') ?? 0) + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -120,6 +120,6 @@ return [
|
|||
*/
|
||||
|
||||
'contactsList' => [
|
||||
'contactform' => explode(',', env('MAIL_LIST_CONTACTFORM') ?? ''),
|
||||
'contactform' => array_filter(explode(',', env('MAIL_LIST_CONTACTFORM', '') ?? '')),
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ class SlideFactory extends Factory
|
|||
'picture_alt' => $picture_alt,
|
||||
'picture_title' => $picture_title,
|
||||
'picture_thumbnail_alt' => $picture_alt,
|
||||
'picture_thumbnail_title' => $picture_title
|
||||
'picture_thumbnail_title' => $picture_title,
|
||||
'published' => $this->faker->boolean(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Product;
|
||||
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('products', function (Blueprint $table) {
|
||||
$table->unsignedInteger('priority_order')->comment('Ordre de priorite')->default(0)->after('order');
|
||||
});
|
||||
|
||||
$order = 1;
|
||||
|
||||
Product::query()
|
||||
->orderBy('order')
|
||||
->each(function (Product $product) use (&$order) {
|
||||
$product->priority_order = $order++;
|
||||
$product->saveOrFail();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('products', function (Blueprint $table) {
|
||||
$table->dropColumn('priority_order');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Advices\AdviceTypeEnum;
|
||||
use App\Models\Advice;
|
||||
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('advice', function (Blueprint $table) {
|
||||
$table->unsignedInteger('priority_order')->comment('Ordre de priorite')->default(0)->after('published');
|
||||
});
|
||||
|
||||
// loop through all types and in each type we order
|
||||
Advice::query()->select('type')->pluck('type')
|
||||
->each(function (AdviceTypeEnum $type) {
|
||||
$order = 1;
|
||||
Advice::query()
|
||||
->where('type', $type)
|
||||
->orderBy('created_at')
|
||||
->each(function (Advice $advice) use (&$order) {
|
||||
$advice->priority_order = $order++;
|
||||
$advice->saveOrFail();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('advice', function (Blueprint $table) {
|
||||
$table->dropColumn('priority_order');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -21,7 +21,7 @@ class ExpertiseProductSeeder extends Seeder
|
|||
ExpertiseProduct::factory(rand(0, 3))->make()->each(
|
||||
// @phpstan-ignore-next-line
|
||||
function (ExpertiseProduct $expertiseProduct) use ($enum) {
|
||||
$expertiseProduct->page = $enum;
|
||||
$expertiseProduct->page = ExpertiseProductPageEnum::make($enum->value);
|
||||
$expertiseProduct->saveQuietly();
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -141,5 +141,10 @@ return [
|
|||
'seo_title' => 'SEO title',
|
||||
'seo_description' => 'SEO description',
|
||||
'type' => 'type',
|
||||
'new_priority' => 'New priority',
|
||||
'priority' => 'priority',
|
||||
'same_priority' => 'the priority value must be different from the current value.',
|
||||
'min_priority' => 'the priority value must be greater than 0.'
|
||||
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -151,7 +151,11 @@ return [
|
|||
'seo_title' => 'titre SEO',
|
||||
'seo_description' => 'description SEO',
|
||||
'type' => 'type',
|
||||
'url' => 'Le champ :field doit être une URL valide.'
|
||||
'url' => 'Le champ :field doit être une URL valide.',
|
||||
'new_priority' => 'Nouvelle priorité',
|
||||
'priority' => 'priorité',
|
||||
'same_priority' => 'La valeur de priorité doit être différente de la valeur actuelle.',
|
||||
'min_priority' => 'La valeur de priorité doit être supérieure à 0.'
|
||||
],
|
||||
'attributes' => [
|
||||
'advantages' => 'Avantages',
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import {
|
|||
faTrash,
|
||||
faUndoAlt,
|
||||
faWandMagic,
|
||||
faChevronDown
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
config.autoReplaceSvg = false;
|
||||
|
|
@ -72,7 +73,7 @@ library.add(faListUl);
|
|||
library.add(faCircleCheck);
|
||||
library.add(faCircleExclamation);
|
||||
library.add(faCircleInfo);
|
||||
|
||||
library.add(faChevronDown);
|
||||
// Front
|
||||
library.add(faChevronLeft);
|
||||
library.add(faChevronRight);
|
||||
|
|
|
|||
|
|
@ -19,3 +19,6 @@ window.CookieNotice = CookieNotice;
|
|||
import "./base/common";
|
||||
import "./base/matomo";
|
||||
|
||||
// TS script
|
||||
import "./pages/contact"
|
||||
|
||||
|
|
|
|||
30
resources/ts/fo/pages/contact.ts
Normal file
30
resources/ts/fo/pages/contact.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const plusButton = document.querySelector<HTMLAnchorElement>('.btn-plus-wrapper');
|
||||
const form = document.getElementById('contact-form') as HTMLFormElement | null;
|
||||
const csrfToken = document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')?.content;
|
||||
const saveDraftUrl = form?.dataset.saveDraftUrl;
|
||||
|
||||
if (!plusButton || !form || !csrfToken || !saveDraftUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
plusButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const href = plusButton.href;
|
||||
const formData = new FormData(form);
|
||||
const data = Object.fromEntries(formData.entries());
|
||||
|
||||
fetch(saveDraftUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
}).finally(() => {
|
||||
window.location.href = href;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<td class="text-center align-middle">
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<b class="mx-3">{{ $value ?? '' }}</b>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa-solid fa-chevron-down"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<form class="px-4 py-3" action="{{ $route }}" method="POST" novalidate>
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<div class="mb-3">
|
||||
<label for="priority-{{ $model->getKey() }}" class="form-label">{{ __('validation.custom.new_priority') }}</label>
|
||||
<input type="number" class="form-input" id="priority-{{ $model->getKey() }}" placeholder="1" name="priority_order" min="1">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">{{ _('Sauvegarder') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
<thead>
|
||||
@include('bo.modules.table-col-sorter', [
|
||||
'cols' => [
|
||||
'priority_order' => Str::ucfirst(__('validation.custom.priority')),
|
||||
'title' => __('validation.attributes.title'),
|
||||
'picture' => __('validation.attributes.picture'),
|
||||
'published' => __('validation.attributes.published'),
|
||||
|
|
@ -32,6 +33,11 @@
|
|||
<tbody>
|
||||
@foreach ($adviceModels as $adviceModel)
|
||||
<tr class="border-bottom">
|
||||
@include('bo.modules.change-model-order-select', [
|
||||
'model' => $adviceModel,
|
||||
'value' => $adviceModel->priority_order,
|
||||
'route' => route('bo.advice.change-priority', $adviceModel)
|
||||
])
|
||||
<td class="text-center align-middle">{{ $adviceModel->title }}</td>
|
||||
<td class="text-center align-middle">
|
||||
@if($adviceModel->picture)
|
||||
|
|
@ -46,10 +52,8 @@
|
|||
'model' => $adviceModel,
|
||||
])
|
||||
<td class="text-center align-middle"><span class="badge bg-secondary">{{ $adviceModel->updated_at }}</span></td>
|
||||
@php
|
||||
$routeName = request()->route()->getName();
|
||||
@endphp
|
||||
@if (empty(request()->search) && Session::get("$routeName.sort_col") === 'order')
|
||||
@if (empty(request()->search) && request()->range)
|
||||
@php $routeName = request()->route()->getName(); @endphp
|
||||
@include('bo.modules.change-model-order', [
|
||||
'routeName' => 'advice',
|
||||
'models' => $adviceModels,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
$cols = [];
|
||||
$routeName = request()->route()->getName();
|
||||
$cols = array_merge($cols, [
|
||||
'priority_order' => Str::ucfirst(__('validation.custom.priority')),
|
||||
'label' => Str::ucfirst(__('validation.attributes.label')),
|
||||
'species' => Str::ucfirst(__('validation.attributes.species')),
|
||||
'picture' => Str::ucfirst(__('validation.attributes.picture')),
|
||||
|
|
@ -46,12 +47,17 @@
|
|||
}
|
||||
@endphp
|
||||
@include('bo.modules.table-col-sorter', [
|
||||
'cols' => $cols,
|
||||
'cols' => $cols,
|
||||
])
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($productModels as $productModel)
|
||||
<tr class="border-bottom">
|
||||
@include('bo.modules.change-model-order-select', [
|
||||
'model' => $productModel,
|
||||
'value' => $productModel->priority_order,
|
||||
'route' => route('bo.products.change-priority', $productModel)
|
||||
])
|
||||
<td class="text-center align-middle">{{ $productModel->label }}</td>
|
||||
<td class="text-center align-middle">{{ $productModel->species->label() }}</td>
|
||||
<td class="text-center align-middle">
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
@php
|
||||
$draft = Session::get('contact_draft', []);
|
||||
$val = fn(string $key) => old($key, $draft[$key] ?? '');
|
||||
@endphp
|
||||
@extends('fo.layout', ['page' => 'contact'])
|
||||
|
||||
@section('title', $staticPageModel->seo_title)
|
||||
|
|
@ -29,27 +33,27 @@
|
|||
</div>
|
||||
<div class="contact-wrapper position-relative bg-ardoise-radial pt-sm-4 pt-4">
|
||||
<form class="form-contact" id="contact-form" action="{{ route('fo.contact.submit') }}" method="post"
|
||||
inert="true">
|
||||
inert="true" data-save-draft-url="{{ route('fo.contact.save-draft') }}">
|
||||
@csrf
|
||||
<div class="row wrapper-sm justify-content-center">
|
||||
<div class="col-12 col-sm-6 d-flex justify-content-center justify-content-sm-end flex-wrap">
|
||||
<input class="form-control black-square mb-3 p-3" id="firstname" name="firstname" type="text"
|
||||
value="{{ old('firstname') }}" placeholder="{{ __('Prénom') }}">
|
||||
value="{{ $val('firstname') }}" placeholder="{{ __('Prénom') }}">
|
||||
@include('fo.modules.input-error', ['inputName' => 'firstname'])
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 d-flex justify-content-center justify-content-sm-start flex-wrap">
|
||||
<input class="form-control black-square mb-3 p-3" id="lastname" name="lastname" type="text"
|
||||
value="{{ old('lastname') }}" placeholder="{{ __('Nom *') }}" required>
|
||||
value="{{ $val('lastname') }}" placeholder="{{ __('Nom *') }}" required>
|
||||
@include('fo.modules.input-error', ['inputName' => 'lastname'])
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 d-flex justify-content-center justify-content-sm-start flex-wrap">
|
||||
<input class="form-control black-square mb-3 p-3" id="email" name="email" type="email"
|
||||
value="{{ old('email') }}" placeholder="{{ __('Adresse e-mail *') }}" required>
|
||||
value="{{ $val('email') }}" placeholder="{{ __('Adresse e-mail *') }}" required>
|
||||
@include('fo.modules.input-error', ['inputName' => 'email'])
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 d-flex justify-content-center justify-content-sm-start flex-wrap">
|
||||
<input class="form-control black-square mb-3 p-3" id="phone" name="phone" type="text"
|
||||
value="{{ old('phone') }}" placeholder="{{ __('N° de téléphone *') }}" required>
|
||||
value="{{ $val('phone') }}" placeholder="{{ __('N° de téléphone *') }}" required>
|
||||
@include('fo.modules.input-error', ['inputName' => 'phone'])
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 d-flex justify-content-center justify-content-sm-start flex-wrap">
|
||||
|
|
@ -57,28 +61,28 @@
|
|||
name="activity" role="button">
|
||||
{{ __('Votre activité') }} *
|
||||
<option class="option-activity-item disabled p-3 px-3"
|
||||
@if (!old('activity')) selected @endif disabled>
|
||||
@if (!old('activity') && empty($draft['activity'])) selected @endif disabled>
|
||||
{{ __('Votre activité') }} *</option>
|
||||
@foreach (\App\Enums\Contact\ContactActivityEnum::toArray() as $key => $activity)
|
||||
<option class="option-activity-item px-3" value="{{ $activity->value }}"
|
||||
@if (old('activity') == $activity->value) selected @endif>{{ $activity->label }}</option>
|
||||
@if (old('activity',$draft['activity'] ?? '') == $activity->value) selected @endif>{{ $activity->label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@include('fo.modules.input-error', ['inputName' => 'activity'])
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 d-flex justify-content-center justify-content-sm-start flex-wrap">
|
||||
<input class="form-control black-square mb-3 p-3" id="society" name="society" type="text"
|
||||
value="{{ old('society') }}" placeholder="{{ __('Société') }}">
|
||||
value="{{ $val('society') }}" placeholder="{{ __('Société') }}">
|
||||
@include('fo.modules.input-error', ['inputName' => 'society'])
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 d-flex justify-content-center justify-content-sm-start flex-wrap">
|
||||
<input class="form-control black-square mb-3 p-3" id="postcode" name="postcode" type="text"
|
||||
value="{{ old('postcode') }}" placeholder="{{ __('Code postal') }}">
|
||||
value="{{ $val('postcode') }}" placeholder="{{ __('Code postal') }}">
|
||||
@include('fo.modules.input-error', ['inputName' => 'postcode'])
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 d-flex justify-content-center justify-content-sm-start flex-wrap">
|
||||
<input class="form-control black-square mb-3 p-3" id="city" name="city" type="text"
|
||||
value="{{ old('city') }}" placeholder="{{ __('Ville') }}">
|
||||
value="{{ $val('city') }}" placeholder="{{ __('Ville') }}">
|
||||
@include('fo.modules.input-error', ['inputName' => 'city'])
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -87,7 +91,7 @@
|
|||
<div class="col">
|
||||
@include('fo.modules.input-error', ['inputName' => 'message'])
|
||||
<textarea class="form-control black-square mb-3 p-3" id="message" name="message" cols="30" rows="6"
|
||||
placeholder="{{ __('Votre message') }} ({{ __('min. 20 caractères.') }}) *" required>{{ old('message') }}</textarea>
|
||||
placeholder="{{ __('Votre message') }} ({{ __('min. 20 caractères.') }}) *" required>{{ $val('message') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row wrapper-sm mb-3">
|
||||
|
|
@ -115,7 +119,7 @@
|
|||
<div class="col">
|
||||
<input class="input-radio-contact d-none" id="call-time-{{ Str::slug($contactTime->value) }}"
|
||||
name="callTime" type="radio" value="{{ $contactTime->value }}"
|
||||
@if (old('callTime') == $contactTime->value) checked @endif>
|
||||
@if (old('callTime', $draft['callTime'] ?? '') == $contactTime->value) checked @endif>
|
||||
<label class="label-time-contact butn-cinq position-relative w-100 m-0"
|
||||
for="call-time-{{ Str::slug($contactTime->value) }}">
|
||||
{{ $contactTime->label }}
|
||||
|
|
@ -128,7 +132,7 @@
|
|||
<div class="col col-md-8 lh-sm px-0 py-2">
|
||||
<div class="form-check text-start">
|
||||
<input class="form-check-input @if ($errors->has('opt_in')) is-invalid @endif me-1" id="check-optin"
|
||||
name="opt_in" type="checkbox" @checked(old('opt_in'))>
|
||||
name="opt_in" type="checkbox" @checked(old('opt_in', $draft['opt_in'] ?? false))>
|
||||
<label class="form-check-label w-fit" for="check-optin" role="button">
|
||||
{{ __('Je souhaite m’inscrire à la newsletter') }} {{ Config::get('theme.site_name') }}
|
||||
</label>
|
||||
|
|
@ -206,5 +210,6 @@
|
|||
window.WebCookieNotice.acceptCookie('recaptcha');
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
@endpush
|
||||
@endpush
|
||||
|
|
@ -61,6 +61,12 @@ Route::prefix('bo')
|
|||
])->where('direction', 'up|down')->name('products.change-order');
|
||||
Route::post('/products/{product}/change-published', [ProductController::class, 'changePublished'])
|
||||
->name('products.change-published');
|
||||
Route::patch('/products/{product}/change-priority', [ProductController::class, 'changeOrderPriority'])
|
||||
->name('products.change-priority');
|
||||
Route::patch('/products/{product}/change-order/{direction}', [
|
||||
BrigadeSectionController::class,
|
||||
'changeOrder'
|
||||
])->where('direction', 'up|down')->name('products.change-order');
|
||||
|
||||
// * NEWS
|
||||
Route::resource('news', NewsController::class)->except(['show'])
|
||||
|
|
@ -83,6 +89,8 @@ Route::prefix('bo')
|
|||
Route::get('advice/{type}/create', [AdviceController::class, 'create'])->name('advice.create');
|
||||
Route::post('/advice/{advice}/change-published', [AdviceController::class, 'changePublished'])
|
||||
->name('advice.change-published');
|
||||
Route::patch('/advice/{advice}/change-priority', [AdviceController::class, 'changeOrderPriority'])
|
||||
->name('advice.change-priority');
|
||||
|
||||
// * EXPERTISE PRODUITS VIP
|
||||
Route::resource('vips', ExpertiseProductController::class)->except(['show']);
|
||||
|
|
@ -95,7 +103,10 @@ Route::prefix('bo')
|
|||
Route::resource('brigade_sections', BrigadeSectionController::class)->except(['show'])
|
||||
->withoutMiddleware('htmlsanitize')
|
||||
->middleware('htmlsanitize:content');
|
||||
Route::post('/brigade_sections/{brigade_section}/change-published', [BrigadeSectionController::class, 'changePublished'])
|
||||
Route::post(
|
||||
'/brigade_sections/{brigade_section}/change-published',
|
||||
[BrigadeSectionController::class, 'changePublished']
|
||||
)
|
||||
->name('brigade_sections.change-published');
|
||||
Route::patch('/brigade_sections/{brigade_section}/change-order/{direction}', [
|
||||
BrigadeSectionController::class,
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ Route::name('fo.')
|
|||
Route::get('/', [ContactController::class, 'index'])->name('show');
|
||||
Route::post('/mail', [ContactController::class, 'contactSubmit'])->name('submit');
|
||||
});
|
||||
Route::post('contactez-nous/save-draft', [ContactController::class, 'saveDraft'])->name('contact.save-draft');
|
||||
|
||||
Route::get('/restauration-sociale', [ActivitiesController::class, 'sociale'])->name('group');
|
||||
Route::get('/restauration-commerciale', [ActivitiesController::class, 'commerciale'])->name('trade');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue