Compare commits

..

1 commit

Author SHA1 Message Date
Jérémy Munsch
a7c0fb9073
Add ldc/web-admin-secure-laravel
- need new envars
  - LDC_WEB_API_ENDPOINT (optional)
  - LDC_STATUS_CHECKER_UUID
  - LDC_WEB_CIDR_MANUAL_ALLOWED_LIST (optional)
2023-06-05 12:48:00 +02:00
65 changed files with 1457 additions and 3162 deletions

View file

@ -46,3 +46,11 @@ GOOGLE_RECAPTCHA_PRIVATE=6LeQxHoUAAAAAOudkLY90DerM_ZtlaFfzTlJJvIo
GOOGLE_RECAPTCHA_VERSION=2
# - MAPS
GOOGLE_MAPS_KEY=
# ADMIN SECURE
# - API ENDPOINT (Optional)
LDC_WEB_API_ENDPOINT=""
# - API KEY
LDC_WEB_API_KEY=""
# - LIST SEPARATED WITH ',' (Optional)
LDC_WEB_CIDR_MANUAL_ALLOWED_LIST=""

View file

@ -1,164 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\News\StoreNewsRequest;
use App\Http\Requests\Bo\News\UpdateNewsRequest;
use App\Models\News;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelStatus;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class NewsController extends Controller
{
use ChangesModelOrder;
use UpdateModelStatus;
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $news */
$news = News::query();
if ($request->search) {
$this->searchQuery(
$news,
$request->search,
null,
'seo_title',
'name',
'title',
'subtitle',
);
}
$this->sortQuery($news);
$searchFields = trans('Name');
$newsListModels = $news->paginate(10);
return view('bo.pages.news.index', compact('newsListModels', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('bo.pages.news.create');
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\News\StoreNewsRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreNewsRequest $request)
{
return DB::transaction(function () use ($request) {
$news = new News();
$news->data = News::getEmptyData();
$news->fill(array_replace_recursive(
\json_decode(\json_encode($news->toArray()), true),
$request->validated()
));
if ($news->saveOrFail()) {
return redirect()
->route('bo.news.edit', $news)
->with('success', trans(
'crud.messages.has_been_created',
['model' => Str::singular(trans('models.classes.news'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_created',
['model' => Str::singular(trans('models.classes.news'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\News $news
* @return \Illuminate\Http\Response
*/
public function edit(News $news)
{
return view('bo.pages.news.edit', [
'newsModel' => $news
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\News\UpdateNewsRequest $request
* @param \App\Models\News $news
* @return \Illuminate\Http\Response
*/
public function update(UpdateNewsRequest $request, News $news)
{
return DB::transaction(function () use ($request, $news) {
$news->fill(array_replace_recursive(
\json_decode(\json_encode($news->toArray()), true),
$request->validated()
));
$news->locales = $request->locales;
if ($news->saveOrFail()) {
return redirect()
->route('bo.news.edit', $news)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => Str::singular(trans('models.classes.news'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_updated',
['model' => Str::singular(trans('models.classes.news'))]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\News $news
* @return \Illuminate\Http\Response
*/
public function destroy(News $news)
{
if ($news->deleteOrFail()) {
return redirect()
->route('bo.news.index')
->with('success', trans(
'crud.messages.has_been_deleted',
['model' => Str::singular(trans('models.classes.news'))]
));
}
return redirect()
->back()
->with('error', trans(
':crud.messages.cannot_be_deleted',
['model' => Str::singular(trans('models.classes.news'))]
));
}
}

View file

@ -1,78 +0,0 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Models\News;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class NewsController extends Controller
{
/**
* News index
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\JsonResponse
*/
public function index(Request $request)
{
if ($request->wantsJson()) {
$currentLocale = app()->currentLocale();
$newsList = News::query()
->where('status', true)
->whereJsonContains('locales', $currentLocale)
->orderBy('order')
->paginate();
return \response()->json(\collect($newsList->items())
->map(fn ($newsModel) => $this->renderNews($newsModel)));
}
return view('fo.pages.news.index');
}
/**
* Product show
*
* @param \App\Models\News $publishedNews
* @return \Illuminate\Contracts\View\View
*/
public function show(News $publishedNews)
{
$currentLocale = app()->currentLocale();
$relatedNewsList = News::query()
->where('id', '!=', $publishedNews->id)
->where('status', true)
->whereJsonContains('locales', $currentLocale)
->orderBy('order', 'DESC')
->limit(2)->get();
return view('fo.pages.news.show', [
'newsModel' => $publishedNews,
'relatedNewsList' => $relatedNewsList
]);
}
/**
* Render a news for JS usage (ajax)
*
* @param \App\Models\News $newsModel
* @return string Base64 rendered view.
*/
private function renderNews(News $newsModel): string
{
$locale = \app()->currentLocale();
return Cache::remember(
"NewsRendered_{$locale}_{$newsModel->id}_{$newsModel->updated_at}",
60,
function () use ($newsModel) {
$pictures = $newsModel->picture_list;
return \base64_encode(\view(
'fo.pages.news.bloc',
[
'newsModel' => $newsModel,
'pictures' => $pictures
]
)->render());
}
);
}
}

View file

@ -1,91 +0,0 @@
<?php
namespace App\Http\Requests\Bo\News;
use App\Http\Requests\Bo\UpdateMiniCrudRequest;
use Illuminate\Support\Str;
class StoreNewsRequest extends UpdateMiniCrudRequest
{
/**
* The model parameter name
*
* @var string
*/
protected $modelName = 'news';
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'slug' => Str::slug(strip_tags($this->name)),
'status' => $this->status ? true : false,
'publish_at' => $this->publish_at ? \str_replace('T', ' ', $this->publish_at) : null,
'unpublish_at' => $this->unpublish_at ? \str_replace('T', ' ', $this->unpublish_at) : null,
'locales' => \collect($this->locales)->filter()->unique()->all()
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'seo_title' => 'required|min:3|max:70',
'seo_description' => 'required|min:50|max:160',
'slug' => 'unique_translation:news',
'title' => 'required|min:3|max:70',
'subtitle' => 'required|min:3|max:70',
'content' => 'required|string|min:15',
'locales' => 'required|array',
'locales.*' => ['required', \sprintf('in:%s', \implode(',', \config('app.locales')))],
'data' => 'present|array',
'data.*' => 'required|array',
'status' => 'required|boolean',
'publish_at' => 'sometimes|nullable|date',
'unpublish_at' => 'sometimes|nullable|date'
];
return \array_merge(
parent::rules(),
$rules
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'seo_title' => trans('titre') . ' seo',
'seo_description' => trans('description') . ' seo',
'slug' => trans('slug of the news'),
'subtitle' => trans('subtitle of the news'),
'content' => trans('content of the news'),
'data' => trans('values of the data'),
'data.*.value' => trans('value of the data'),
];
}
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Http\Requests\Bo\News;
use App\Http\Requests\Bo\News\StoreNewsRequest;
use CodeZero\UniqueTranslation\UniqueTranslationRule;
class UpdateNewsRequest extends StoreNewsRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'slug' => UniqueTranslationRule::for('news')->ignore($this->news->id),
];
return \array_merge(
parent::rules(),
$rules,
);
}
}

View file

@ -4,8 +4,6 @@ namespace App\Http\Requests\Bo;
use App\Enums\InputType;
use App\Lib\Helpers\ToolboxHelper;
use App\Models\Concept;
use App\Models\News;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\ValidationException;
@ -39,16 +37,7 @@ abstract class UpdateMiniCrudRequest extends FormRequest
{
// phpcs:enable
$rules = [];
$data = $this->{$this->modelName}->data ?? null;
if (!$data and $this->route()->getName() === 'bo.concepts.store') {
$data = Concept::getEmptyData();
}
if (
!$data and $this->route()->getName() === 'bo.news.store'
) {
$data = News::getEmptyData();
}
foreach ($data as $iptName => $input) {
foreach ($this->{$this->modelName}->data as $iptName => $input) {
$input = \json_decode(\json_encode($input), true);
switch ($input['inputType']) {
case InputType::number->value:
@ -91,7 +80,7 @@ abstract class UpdateMiniCrudRequest extends FormRequest
};
}
return $rule;
})->all();
});
$rules = array_merge($rules, [
"data.$iptName.value" => $mergeRules
]);
@ -118,17 +107,26 @@ abstract class UpdateMiniCrudRequest extends FormRequest
'maxWith' => $dim['width'],
];
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate(
$required,
$input
),
"data.$iptName.value" => [
function (string $attribute, $value, callable $fail) use ($sizes) {
if ($value) {
return ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
}
return ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
}
],
$this->compileValidate(
implode('|', [
$required,
'image',
'mimes:png,jpg,jpeg',
sprintf(
'dimensions:%s=%d,%s=%d',
$max ? 'max_height' : 'height',
$dim['height'],
$max ? 'max_width' : 'width',
$dim['width']
)
]),
$input
),
"data.$iptName.alt" => $this->compileValidate("$required|string|min:5", $input),
"data.$iptName.title" => $this->compileValidate("$required|string|min:5", $input)
]);
@ -187,46 +185,36 @@ abstract class UpdateMiniCrudRequest extends FormRequest
}
}
$rules = \collect($inputTypeValidate)->unique()->sort(function ($valueA, $valueB) {
$valueA = $this->ruleHasToComeFirst($valueA);
$valueB = $this->ruleHasToComeFirst($valueB);
$test = \collect($inputTypeValidate)->all();
$test = \collect($inputTypeValidate)->sort(function ($valueA, $valueB) {
$valueA = $this->ruleHasToComeFirst($valueA) ? 0 : 2;
$valueB = $this->ruleHasToComeFirst($valueB) ? 1 : 2;
return $valueA - $valueB;
});
})->unique()->all();
$hasRequire = $rules->first(fn ($rule) => $rule === 'required') ? true : false;
$hasRequireCond = $rules->first(fn ($rule) => \strpos($rule, 'required_') !== false) ? true : false;
$hasNullable = $rules->first(fn ($rule) => \strpos($rule, 'nullable') !== false) ? true : false;
$hasSometime = $rules->first(fn ($rule) => \strpos($rule, 'sometimes') !== false) ? true : false;
$hasPresent = $rules->first(fn ($rule) => \strpos($rule, 'present') !== false) ? true : false;
if ($hasRequire and ($hasRequireCond or $hasNullable or $hasSometime or $hasPresent)) {
$rules = $rules->reject(fn ($rule) => $rule === 'required');
}
return $rules->all();
return \collect($inputTypeValidate)->unique()->sort(function ($valueA, $valueB) {
$valueA = $this->ruleHasToComeFirst($valueA) ? 0 : 2;
$valueB = $this->ruleHasToComeFirst($valueB) ? 1 : 2;
return $valueA - $valueB;
})->all();
}
/**
* Does the rulle need to come first ?
*
* @param string $rule
* @return integer
* @return boolean
*/
private function ruleHasToComeFirst(string $rule): int
private function ruleHasToComeFirst(string $rule): bool
{
$rule = \collect(\explode(':', $rule, 2))->filter()->first(null, '');
$rules = \collect([
'required_if',
$rule = \collect(\explode(':', $rule, 1))->filter()->first(null, '');
return \collect([
'nullable', 'present', 'required', 'required_if',
'required_unless', 'required_with', 'required_with_all',
'required_without', 'required_without_all', 'required_with_all',
'missing', 'missing_if', 'missing_unless',
'missing_with', 'missing_with_all',
'nullable', 'sometimes', 'present', 'required'
]);
$itemKey = $rules->search(fn (string $firstRule) => \strpos($rule, $firstRule) === 0);
if (\is_bool($itemKey)) {
return $rules->count() + 1;
}
return $itemKey;
])->first(fn (string $firstRule) => \strpos($rule, $firstRule) === 0) ? true : false;
}
/**

View file

@ -1,43 +0,0 @@
<?php
namespace App\Jobs;
use App\Models\News;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
/**
* Update status attribute reading its
* publish_at and unpublish_at attributes.
*/
class NewsStatusUpdate
{
/**
* Called when the job will be executed.
*
* @return void
*/
public function handle()
{
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\News> */
$newsListToPublish = News::query()->where('publish_at', '<=', Carbon::now()->toDateTime())
->whereNotNull('publish_at')
->where('status', false)->get();
Log::debug(__CLASS__ . " news to publish " . $newsListToPublish->count());
foreach ($newsListToPublish as $news) {
$news->publish_at = null;
$news->status = true;
$news->saveOrFail();
}
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\News> */
$newsListToUnPublish = News::query()->where('unpublish_at', '<=', Carbon::now()->toDateTime())
->whereNotNull('unpublish_at')
->where('status', true)->get();
Log::debug(__CLASS__ . " news to unpublish " . $newsListToPublish->count());
foreach ($newsListToUnPublish as $news) {
$news->unpublish_at = null;
$news->status = false;
$news->saveOrFail();
}
}
}

View file

@ -250,13 +250,13 @@ class Concept extends Model
'label' => "Bloc {$index} - Title",
'inputType' => InputType::string->value,
'value' => $title,
'validate' => ($index > 4 ? "required_if:bloc_{$index}_use_bloc,true" : 'required') . '|min:4|max:100'
'validate' => 'required|min:4|max:100'
];
$data["bloc_{$index}_color"] = [
'label' => "Bloc {$index} - Color",
'inputType' => InputType::selectEnum->value,
'value' => $color,
'validate' => [($index > 4 ? "required_if:bloc_{$index}_use_bloc,true" : 'required'), 'integer', new Enum(ColorsEnum::class)]
'validate' => ['required', 'integer', new Enum(ColorsEnum::class)]
];
$data["bloc_{$index}_text"] = [
'label' => "Bloc {$index} - Text",
@ -271,7 +271,7 @@ class Concept extends Model
'alt' => $imgAlt,
'title' => $imgTitle,
'value' => $img,
'validate' => ($index > 4 ? "required_if:bloc_{$index}_use_bloc,true" : '') . '',
'validate' => "required_if:data.bloc_{$index}_has_video.value,false",
];
$data["bloc_{$index}_video"] = [
@ -280,7 +280,7 @@ class Concept extends Model
'value' => '',
// phpcs:ignore Generic.Files.LineLength.TooLong
'validate' => [
"required_if:data.bloc_{$index}_has_video.value,true",
'required_if:data.bloc_{$i}_has_video.value,true',
'nullable',
'regex:%([0-9]{8,10}|[^\"&?\/\s]{11})%i'
],
@ -293,18 +293,6 @@ class Concept extends Model
'value' => false,
];
if ($index > 4) {
$data["bloc_{$index}_title"]['required'] = false;
$data["bloc_{$index}_title"]['validate'] .= '|nullable';
$data["bloc_{$index}_color"]['required'] = false;
$data["bloc_{$index}_text"]['required'] = false;
$data["bloc_{$index}_text"]['validate'] .= '|nullable';
$data["bloc_{$index}_image"]['required'] = false;
$data["bloc_{$index}_image"]['validate'] .= '|nullable';
$data["bloc_{$index}_video"]['required'] = false;
$data["bloc_{$index}_has_video"]['required'] = false;
}
return $data;
}
}

View file

@ -1,354 +0,0 @@
<?php
namespace App\Models;
use App\Enums\InputType;
use App\Lib\Helpers\FileStorageHelper;
use App\Lib\Helpers\ToolboxHelper;
use App\Traits\Models\HasTranslations;
use App\Traits\Models\MiniCrudModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
/**
* @property string $seo_title
* @property string $seo_description
* @property string $title
* @property string $slug
* @property string $subtitle
* @property string $content
* @property \Illuminate\Support\Collection $picture_list
* @property object $data
* @property integer $order
* @property boolean $status
* @property \Carbon\Carbon|null $publish_at
* @property \Carbon\Carbon|null $unpublish_at
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
*/
class News extends Model
{
use HasFactory;
use HasTranslations;
use MiniCrudModel;
/**
* @var array
*/
protected $fillable = [
'seo_title',
'seo_description',
'title',
'slug',
'subtitle',
'content',
'data',
'locales',
'order',
'status',
'publish_at',
'unpublish_at'
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'data' => 'object',
'locales' => 'array',
'status' => 'bool',
'publish_at' => 'timestamp',
'unpublish_at' => 'timestamp'
];
/**
* The translatable columns.
*
* @var array
*/
public $translatable = [
'seo_title',
'seo_description',
'title',
'slug',
'subtitle',
'content',
'data'
];
// * EVENTS
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted()
{
static::creating(function (self $model) {
// * ASSERTIONS
static::assertTitleIsUnique($model->title);
ToolboxHelper::assertNoInternalBoUrls('content', $model->content);
// * TRANSFORMATIONS
$model->slug = Str::slug($model->title);
$model->content = FileStorageHelper::storeModelFilesFromHtml($model, $model->content);
$model->makeStatusSense();
self::setOrder($model);
});
static::created(function (self $model) {
$model->flashMessages();
});
static::updating(function (self $model) {
// * ASSERTIONS
static::assertTitleIsUnique($model->title);
ToolboxHelper::assertNoInternalBoUrls('content', $model->content);
// * TRANSFORMATIONS
$model->slug = Str::slug($model->title);
$model->content = FileStorageHelper::storeModelFilesFromHtml($model, $model->content);
$model->makeStatusSense();
});
static::updated(function (self $model) {
FileStorageHelper::removeOldModelFilesFromHtml(
$model->content,
ToolboxHelper::getOriginal($model, 'content')
);
$model->flashMessages();
});
static::deleted(function (self $model) {
FileStorageHelper::removeModelFilesOfHtml($model->content);
$model->minicrudDeleted();
});
}
// * METHODS
/**
* Apply function on data typed text while on deleted event
*
* @return void
*/
public function minicrudDeleted()
{
/** @var array */
foreach ($this->data as $input) {
switch ($input['inputType']->value ?? $input['inputType']['value']) {
case InputType::text->value:
FileStorageHelper::removeModelFilesOfHtml($input['text']);
break;
} //end switch
} //end foreach
}
/**
* Get picture_list attribute
*
* @return \Illuminate\Support\Collection
*/
public function getPictureListAttribute(): Collection
{
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
return \collect($this->data)->filter(fn ($value, $key) => \preg_match('/bloc[0-9]+/u', $key) !== false)
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
->groupBy(fn ($value, $key) => \collect(\explode('_', $key))->take(2)->implode('_'))
->filter(fn (Collection $blocks) => \collect($blocks->first())->get('value'))
->map(function (Collection $blocks) {
$pictureBlock = (object)$blocks->last();
return (object)[
'value' => $pictureBlock->value,
'title' => $pictureBlock->title,
'alt' => $pictureBlock->alt
];
})->values();
}
/**
* Adjusts model status using current status and publish dates
*
* @return void
*/
public function makeStatusSense(): void
{
/** @var \Carbon\Carbon|null */
$publish_at = $this->publish_at;
/** @var \Carbon\Carbon|null */
$unpublish_at = $this->unpublish_at;
$wantedStatus = $this->status;
if ($publish_at or $unpublish_at) {
$this->status = $this->planificated_status;
}
if ($wantedStatus !== $this->status) {
$previousMessage = session()->get('bo.info', '');
session()->flash('bo.info', \trans(
'La publication est modifiée en concordance avec les dates enregistrées donc
pour modifier l\'état de publication, veuillez modifier ou enlever les dates correspondantes.'
) . " \n{$previousMessage}");
}
}
/**
* Getter the current status following planification dates planificated_status
*
* @return boolean
*/
public function getPlanificatedStatusAttribute(): bool
{
/** @var \Carbon\Carbon|null */
$publish_at = $this->publish_at;
/** @var \Carbon\Carbon|null */
$unpublish_at = $this->unpublish_at;
if ($publish_at and $unpublish_at) {
return $unpublish_at->greaterThanOrEqualTo(now()) and $publish_at->lessThan(now());
}
if ($publish_at) {
return $publish_at->lessThan(now());
}
if ($unpublish_at) {
return $unpublish_at->greaterThanOrEqualTo(now());
}
return false;
}
/**
* Flash boolean attributes modification messages
*
* @param boolean $old Ignore old attribute comparison.
* @return void
*/
public function flashMessages(bool $old = true)
{
$booleanChecks = [
'status' => [
'bo.info' => trans(':model est maintenant publié', [
'model' => Str::singular(trans('models.classes.products'))
]),
'bo.warning' => trans(':model est maintenant dépublié', [
'model' => Str::singular(trans('models.classes.products'))
])
]
];
$sessionMessages = [];
// * Merge messages
foreach ($booleanChecks as $attribute => $booleanCheck) {
if ($old and $this->getOriginal($attribute) == $this->{$attribute}) {
continue;
}
/** @var string $flashType */
$flashType = \array_keys($booleanCheck)[$this->{$attribute} ? 0 : 1];
if (!\array_key_exists($flashType, $sessionMessages)) {
$sessionMessages[$flashType] = [];
}
$sessionMessages[$flashType][] = $booleanCheck[$flashType];
}
foreach ($sessionMessages as $flashType => $messages) {
session()->flash($flashType, collect($messages)->implode("\n"));
}
}
/**
* Set the slug.
*
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return void
*/
private static function setSlug(Model $model)
{
$model->slug = Str::slug($model->name);
}
/**
* Set order after the last element of the list.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
private static function setOrder(Model $model): void
{
$model->order = \intval(self::query()->max('order')) + 1;
}
/**
* Asserts using validation that the title is unique
*
* @param string|integer|float|boolean|null|array $title
* @param integer|null $id
* @return void
* @throws \Illuminate\Validation\ValidationException If an news title already exists.
*/
private static function assertTitleIsUnique(string|int|float|bool|null|array $title, ?int $id = null)
{
$table = (new self())->getTable();
ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id);
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($title), $id);
}
/**
* Return the structure necessary of data.
*
* @param string $img
* @param string $imgAlt
* @param string $imgTitle
* @return ((string|\App\Enums\InputType)[]|(string|int[]|\App\Enums\InputType)[]|(string|\App\Enums\InputType|int|(string|\Illuminate\Validation\Rules\Enum)[])[])[]
*/
public static function getEmptyData(
string $img = '',
string $imgAlt = '',
string $imgTitle = ''
) {
$data = [];
for ($i = 1; $i <= 3; $i++) {
$data = self::emptyDataBloc(
$i,
$data,
$img,
$imgAlt,
$imgTitle
);
} //end for
return $data;
}
/**
* Empty data bloc
*
* @param string $index
* @param array $data
* @param string $img
* @param string $imgAlt
* @param string $imgTitle
* @return array
*/
public static function emptyDataBloc(
string $index,
array $data,
string $img = '',
string $imgAlt = '',
string $imgTitle = ''
): array {
$data["bloc_{$index}_display_picture"] = [
'label' => "Bloc {$index} - Display picture",
'inputType' => InputType::boolean->value,
'value' => false,
];
$data["bloc_{$index}_image"] = [
'label' => "Bloc {$index} - Image",
'dimensions' => ['width' => 770, 'height' => 770],
'inputType' => InputType::image->value,
'alt' => $imgAlt,
'title' => $imgTitle,
'value' => $img,
'validate' => "required_if:data.bloc_{$index}_display_picture.value,true|nullable",
'required' => false
];
return $data;
}
}

View file

@ -4,7 +4,6 @@ namespace App\Providers;
use App\Lib\Helpers\ToolboxHelper;
use App\Models\Concept;
use App\Models\News;
use App\Models\Product;
use App\Models\Range;
use App\Models\Recipe;
@ -76,20 +75,7 @@ class RouteServiceProvider extends ServiceProvider
$value
)->firstOrFail();
});
Route::bind('publishedNews', function ($value) use ($defaultLocale) {
$currentLocale = app()->currentLocale();
$query = News::query()
->whereJsonContains('locales', $currentLocale)
->where('status', true);
return ToolboxHelper::queryColumnWithLocales(
$defaultLocale,
$query,
'slug',
$value
)->firstOrFail();
});
Route::model('news', News::class);
Route::model('concept', Concept::class);
Route::model('range', Range::class);
Route::model('product', Product::class);

View file

@ -166,7 +166,7 @@ trait MiniCrudModel
case InputType::string->value:
case InputType::text->value:
case InputType::url->value:
ToolboxHelper::assertNoInternalBoUrls('content', $input['value'] ?? '');
ToolboxHelper::assertNoInternalBoUrls('content', $input['value']);
break;
default:
throw new \ErrorException(sprintf(

View file

@ -18,11 +18,11 @@
"codezero/laravel-unique-translation": "^4.1",
"diglactic/laravel-breadcrumbs": "^8.0",
"guzzlehttp/guzzle": "^7.2",
"kwaadpepper/image-resizer": "^2.1",
"kwaadpepper/refresh-sitemap": "^2.0",
"laravel/framework": "^10.0",
"laravel/tinker": "^2.8",
"laravel/ui": "^4.2",
"ldc/web-admin-secure-laravel": "^2.0",
"mews/purifier": "^3.4",
"sentry/sentry-laravel": "^3.3",
"simplesoftwareio/simple-qrcode": "~4",

466
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "fe0dfc9be66d39044403016f1648e00a",
"content-hash": "d2b801d090c7841b2252d7f52c74695e",
"packages": [
{
"name": "bacon/bacon-qr-code",
@ -1026,6 +1026,103 @@
],
"time": "2022-09-10T18:51:20+00:00"
},
{
"name": "dxw/cidr",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/dxw/cidr.git",
"reference": "61d699459e9bddc7222ac1eb3ffc9ffcfdc2f546"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dxw/cidr/zipball/61d699459e9bddc7222ac1eb3ffc9ffcfdc2f546",
"reference": "61d699459e9bddc7222ac1eb3ffc9ffcfdc2f546",
"shasum": ""
},
"require": {
"dxw/result": "^1.0",
"phpseclib/phpseclib": "^2.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.16",
"mockery/mockery": "^1.3",
"peridot-php/leo": "^1.6",
"peridot-php/peridot": "^1.19",
"peridot-php/peridot-dot-reporter": "^1.0",
"vimeo/psalm": "^3.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Dxw\\CIDR\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mallory Adams",
"email": "mallory@dxw.com",
"homepage": "https://www.dxw.com"
}
],
"description": "IP address matching with CIDR ranges",
"homepage": "https://github.com/dxw/cidr",
"support": {
"issues": "https://github.com/dxw/cidr/issues",
"source": "https://github.com/dxw/cidr/tree/master"
},
"time": "2020-05-06T16:46:43+00:00"
},
{
"name": "dxw/result",
"version": "v1.0.3",
"source": {
"type": "git",
"url": "https://github.com/dxw/result.git",
"reference": "eaa71abf0f9bba74cf5c832811a3a66a887e86d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dxw/result/zipball/eaa71abf0f9bba74cf5c832811a3a66a887e86d2",
"reference": "eaa71abf0f9bba74cf5c832811a3a66a887e86d2",
"shasum": ""
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.15",
"peridot-php/leo": "^1.6",
"peridot-php/peridot": "^1.19",
"peridot-php/peridot-dot-reporter": "^1.0",
"vimeo/psalm": "^3.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Dxw\\Result\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mallory Adams",
"email": "mallory@dxw.com",
"homepage": "https://www.dxw.com/"
}
],
"description": "Result values",
"homepage": "https://github.com/dxw/result",
"support": {
"issues": "https://github.com/dxw/result/issues",
"source": "https://github.com/dxw/result/tree/v1.0.3"
},
"time": "2019-07-18T16:52:47+00:00"
},
{
"name": "egulias/email-validator",
"version": "4.0.1",
@ -1887,73 +1984,6 @@
],
"time": "2022-05-21T17:30:32+00:00"
},
{
"name": "intervention/imagecache",
"version": "2.6.0",
"source": {
"type": "git",
"url": "https://github.com/Intervention/imagecache.git",
"reference": "86136575a62d3634b51f196a998fce4a583b49bb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Intervention/imagecache/zipball/86136575a62d3634b51f196a998fce4a583b49bb",
"reference": "86136575a62d3634b51f196a998fce4a583b49bb",
"shasum": ""
},
"require": {
"illuminate/cache": "^5.5|~6|~7|~8|~9|~10",
"illuminate/filesystem": "^5.5|~6|~7|~8|~9|~10",
"intervention/image": "~2.2",
"nesbot/carbon": "^2.39",
"opis/closure": "^3.5",
"php": "~7.2|~8"
},
"require-dev": {
"phpunit/phpunit": "^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Intervention\\Image\\": "src/Intervention/Image"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Oliver Vogel",
"email": "oliver@intervention.io",
"homepage": "http://intervention.io/"
}
],
"description": "Caching extension for the Intervention Image Class",
"homepage": "https://image.intervention.io",
"keywords": [
"cache",
"gd",
"image",
"imagick",
"laravel"
],
"support": {
"issues": "https://github.com/Intervention/imagecache/issues",
"source": "https://github.com/Intervention/imagecache/tree/2.6.0"
},
"funding": [
{
"url": "https://paypal.me/interventionio",
"type": "custom"
},
{
"url": "https://github.com/Intervention",
"type": "github"
}
],
"time": "2023-02-25T19:40:47+00:00"
},
{
"name": "jean85/pretty-package-versions",
"version": "2.0.5",
@ -2013,66 +2043,6 @@
},
"time": "2021-10-08T21:21:46+00:00"
},
{
"name": "kwaadpepper/image-resizer",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/Kwaadpepper/image-resizer.git",
"reference": "2a14ad153b34916f437af50549bad47c018902df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Kwaadpepper/image-resizer/zipball/2a14ad153b34916f437af50549bad47c018902df",
"reference": "2a14ad153b34916f437af50549bad47c018902df",
"shasum": ""
},
"require": {
"illuminate/support": "~10",
"intervention/image": "^2.7.2",
"intervention/imagecache": "^2.6",
"laravel/framework": "~10"
},
"require-dev": {
"slevomat/coding-standard": "^8.11"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Kwaadpepper\\ImageResizer\\ImageResizerServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Kwaadpepper\\ImageResizer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jérémy Munsch",
"email": "github@jeremydev.ovh",
"homepage": "https://jeremydev.ovh"
}
],
"description": "Resize images on the fly and cache them",
"homepage": "https://github.com/kwaadpepper/image-resizer",
"keywords": [
"images",
"laravel",
"resize"
],
"support": {
"issues": "https://github.com/Kwaadpepper/image-resizer/issues",
"source": "https://github.com/Kwaadpepper/image-resizer/tree/2.1.0"
},
"time": "2023-06-08T12:07:24+00:00"
},
{
"name": "kwaadpepper/refresh-sitemap",
"version": "2.0.1",
@ -2521,6 +2491,71 @@
},
"time": "2023-05-09T19:47:28+00:00"
},
{
"name": "ldc/web-admin-secure-laravel",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://lab.s-web.fr/ldc-web/projets/ldc-web-admin-secure-laravel.git",
"reference": "5f0c6b136fd967102bafeefa3048ffd47dbbc9be"
},
"dist": {
"type": "zip",
"url": "https://lab.s-web.fr/api/v4/projects/20/packages/composer/archives/ldc/web-admin-secure-laravel.zip?sha=5f0c6b136fd967102bafeefa3048ffd47dbbc9be",
"reference": "5f0c6b136fd967102bafeefa3048ffd47dbbc9be",
"shasum": ""
},
"require": {
"dxw/cidr": "^3.1",
"illuminate/support": "~10",
"laravel/framework": "~10",
"php": ">=8.2"
},
"require-dev": {
"orchestra/testbench": "^8",
"phpunit/php-code-coverage": "^9",
"phpunit/phpunit": "^9",
"slevomat/coding-standard": "^7",
"squizlabs/php_codesniffer": "^3"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"LDC\\WebAdminSecureLaravel\\AdminSecureServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"LDC\\WebAdminSecureLaravel\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"LDC\\WebAdminSecureLaravel\\Tests\\": "tests"
}
},
"scripts": {
"test": [
"vendor/bin/phpunit"
],
"post-autoload-dump": [
"@php ./vendor/bin/testbench package:discover --ansi"
]
},
"license": [
"MIT"
],
"authors": [
{
"name": "Vincent BRIDIER",
"email": "vincent.bridier@ldc.fr",
"homepage": "https://www.ldc.fr"
}
],
"description": "Restrict access to LDC Network"
},
{
"name": "league/commonmark",
"version": "2.4.0",
@ -3741,71 +3776,6 @@
],
"time": "2023-05-02T11:26:24+00:00"
},
{
"name": "opis/closure",
"version": "3.6.3",
"source": {
"type": "git",
"url": "https://github.com/opis/closure.git",
"reference": "3d81e4309d2a927abbe66df935f4bb60082805ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad",
"reference": "3d81e4309d2a927abbe66df935f4bb60082805ad",
"shasum": ""
},
"require": {
"php": "^5.4 || ^7.0 || ^8.0"
},
"require-dev": {
"jeremeamia/superclosure": "^2.0",
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.6.x-dev"
}
},
"autoload": {
"files": [
"functions.php"
],
"psr-4": {
"Opis\\Closure\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marius Sarca",
"email": "marius.sarca@gmail.com"
},
{
"name": "Sorin Sarca",
"email": "sarca_sorin@hotmail.com"
}
],
"description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
"homepage": "https://opis.io/closure",
"keywords": [
"anonymous functions",
"closure",
"function",
"serializable",
"serialization",
"serialize"
],
"support": {
"issues": "https://github.com/opis/closure/issues",
"source": "https://github.com/opis/closure/tree/3.6.3"
},
"time": "2022-01-27T09:35:39+00:00"
},
{
"name": "php-http/client-common",
"version": "2.7.0",
@ -4266,6 +4236,116 @@
],
"time": "2023-02-25T19:38:58+00:00"
},
{
"name": "phpseclib/phpseclib",
"version": "2.0.42",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "665d289f59e646a259ebf13f29be7f6f54cab24b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/665d289f59e646a259ebf13f29be7f6f54cab24b",
"reference": "665d289f59e646a259ebf13f29be7f6f54cab24b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phing/phing": "~2.7",
"phpunit/phpunit": "^4.8.35|^5.7|^6.0|^9.4",
"squizlabs/php_codesniffer": "~2.0"
},
"suggest": {
"ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
"ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
"ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
"ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.",
"ext-xml": "Install the XML extension to load XML formatted public keys."
},
"type": "library",
"autoload": {
"files": [
"phpseclib/bootstrap.php"
],
"psr-4": {
"phpseclib\\": "phpseclib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jim Wigginton",
"email": "terrafrost@php.net",
"role": "Lead Developer"
},
{
"name": "Patrick Monnerat",
"email": "pm@datasphere.ch",
"role": "Developer"
},
{
"name": "Andreas Fischer",
"email": "bantu@phpbb.com",
"role": "Developer"
},
{
"name": "Hans-Jürgen Petrich",
"email": "petrich@tronic-media.com",
"role": "Developer"
},
{
"name": "Graham Campbell",
"email": "graham@alt-three.com",
"role": "Developer"
}
],
"description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
"homepage": "http://phpseclib.sourceforge.net",
"keywords": [
"BigInteger",
"aes",
"asn.1",
"asn1",
"blowfish",
"crypto",
"cryptography",
"encryption",
"rsa",
"security",
"sftp",
"signature",
"signing",
"ssh",
"twofish",
"x.509",
"x509"
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
"source": "https://github.com/phpseclib/phpseclib/tree/2.0.42"
},
"funding": [
{
"url": "https://github.com/terrafrost",
"type": "github"
},
{
"url": "https://www.patreon.com/phpseclib",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
"type": "tidelift"
}
],
"time": "2023-03-06T12:45:53+00:00"
},
{
"name": "psr/container",
"version": "2.0.2",

View file

@ -15,6 +15,7 @@ return [
|
*/
// 'paths' => ['api/*', 'sanctum/csrf-cookie', '*/storefinder'],
'paths' => ['*'],
'allowed_methods' => ['*'],

View file

@ -1,73 +0,0 @@
<?php
return [
/**
* The php image driver to use
* this can be 'imagick' or 'gd'
*/
'driver' => 'imagick',
/**
* The cache driver to use as defined
* in config/cache of your app
*/
'cache' => 'file',
/**
* The path where to store image in cache
*/
'cachePath' => 'cache/images',
/**
* Image cache life time
* This is given in minutes
* Here the cache for images is
* one hour
*/
'lifeTime' => 535680,
// 1 ans
/**
* Format can specifiy to which format save the image
* JPEG PNG GIF TIF BMP ICO PSD WebP
* GD ✔️ ✔️ ✔️ - - - - ✔️ *
* Imagick ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ ✔️ *
* For WebP support GD driver must be used with PHP 5 >= 5.5.0 or PHP 7
* in order to use imagewebp(). If Imagick is used, it must be compiled
* with libwebp for WebP support.
* example :
* 'small' => [
* 'height' => 500,
* 'width' => 250,
* 'inCanvas' => true,
* ],
* 'smallWebp' => [
* 'height' => 500,
* 'width' => 250,
* 'inCanvas' => true,
* 'format' => 'webp'
* ]
*/
'templates' => [
'foNewsIndexPicture' => [
'height' => 520,
'width' => 768,
'fit' => true,
'resize' => false,
'keepRatio' => false,
'inCanvas' => false,
'trim' => false,
'format' => 'png'
],
'foNewsShowPicture' => [
'height' => 600,
'width' => 600,
'fit' => false,
'resize' => true,
'keepRatio' => true,
'inCanvas' => false,
'trim' => false,
'format' => 'png'
]
]
];

View file

@ -57,9 +57,7 @@ return [
* ]
*
*/
'queryConditions' => [
'status' => true,
],
'queryConditions' => [],
/**
* The default route frequency to use with sitemap generator

View file

@ -1,42 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\News;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
/**
* @extends Factory<\App\Models\News>
*/
final class NewsFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = News::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition(): array
{
$title = fake()->word;
$locales = \collect(\config('app.locales'))->shuffle();
$locales->shift(\rand(0, 1));
return [
'seo_title' => fake()->word,
'seo_description' => fake()->text(150),
'title' => $title,
'slug' => Str::slug($title),
'subtitle' => fake()->word,
'content' => fake()->text(150),
'status' => fake()->boolean(),
'locales' => $locales
];
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -1,47 +0,0 @@
<?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('news', function (Blueprint $table) {
$table->id();
$table->json('seo_title')->comment('Description SEO');
$table->json('seo_description')->comment('Description SEO');
$table->json('title')->comment('Titre de la news');
$table->json('slug')->comment('Slug de la news');
$table->json('subtitle')->comment('Sous-titre de la news');
$table->json('content')->comment('Contenu de la news');
$table->json('data')->comment('Minicrud pour pictures');
$table->json('locales')->comment('Pour quelle locale il faut afficher la news.');
$table->unsignedInteger('order')->comment('Dans quel order il faut afficher la news.');
$table->boolean('status')->comment('La news est-t-elle publiée ?');
$table->timestamp('publish_at')->nullable()
->comment('The date where the post published state should be changed to true.');
$table->timestamp('unpublish_at')->nullable()
->comment('The date where the post published state should be changed to false.');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('news');
}
};

View file

@ -31,7 +31,6 @@ class DatabaseSeeder extends Seeder
UserSeeder::class,
HomeSeeder::class,
NewsSeeder::class,
]);
}
}

View file

@ -1,35 +0,0 @@
<?php
namespace Database\Seeders;
use App\Lib\Helpers\FileStorageHelper;
use App\Models\News;
use Illuminate\Database\Seeder;
class NewsSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$jj = 1;
News::factory(60)->make()->each(function (News $news) use (&$jj) {
$dataImg = FileStorageHelper::storeFile($news, new \SplFileInfo(\resource_path(
'../database/factories/assets/news/default-picture-770x770.jpg'
)));
$news->data = News::getEmptyData(
$dataImg,
fake()->words(4, true),
fake()->words(4, true)
);
$news->order = $jj;
$news->saveOrFail();
$jj = $jj + 1;
});
}
}

1972
package-lock.json generated

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

View file

@ -51,7 +51,6 @@
"Go to recipes": "Zu den Rezepten",
"Goose": "Gans",
"Guinea fowl": "Perlhuhn",
"Headline more news": "Überschriften Sie weitere Neuigkeiten",
"Hello!": "Hallo!",
"Home": "Home",
"Homepage": "Startseite",
@ -77,7 +76,6 @@
"LDC Sablé has used its best efforts to ensure the accuracy of the information on its website (hereinafter the website). However, LDC Sablé is not responsible for the accuracy, errors or omissions on the Website. The use of this information is the sole responsibility of the user.": "LDC Sablé hat sich nach besten Kräften bemüht, die Richtigkeit der Informationen auf seiner Website (im Folgenden die Website) zu gewährleisten. LDC Sablé ist jedoch nicht verantwortlich für die Richtigkeit, Fehler oder Auslassungen auf der Website. Für die Verwendung dieser Informationen ist allein der Benutzer verantwortlich.",
"LDC Sablé makes no warranty, express or implied, and assumes no liability arising out of access to or use of this site, including any degradation or viruses that may infect the user's computer equipment or other property.": "LDC Sablé gibt keine ausdrückliche oder stillschweigende Garantie und übernimmt keine Haftung, die sich aus dem Zugriff oder der Nutzung dieser Site ergeben könnte, einschließlich jeglicher Verschlechterung oder Viren, die Computerausrüstung oder anderes Eigentum des Benutzers infizieren könnten.",
"LDC Sablé may unilaterally and at any time temporarily or permanently suspend access to all or part of the website, in particular to the content, functions or availability times, without liability being requested and this is possible damages of any kind.": "LDC Sablé kann einseitig und jederzeit vorübergehend oder dauerhaft den Zugriff auf die gesamte oder einen Teil der Website, insbesondere auf den Inhalt, die Funktionen oder die Verfügbarkeitszeiten, unterbrechen, ohne dass eine Haftung beantragt wird und dies möglich ist Schäden jeglicher Art.",
"Learn more": "Mehr erfahren",
"Legal information": "Rechtliche hinweise",
"Legal notice": "Impressum",
"Limitation of the retention periods": "Begrenzung der aufbewahrungsfristen",
@ -89,7 +87,6 @@
"Market finder": "Marktfinder",
"may be linked to third party websites. Websites hosted by third parties are beyond our control. If you enter personal data on a third party website, the processing of that data will not take place on our website but on the relevant third party website&nbsp;s website. The personal data you provide will be collected and processed in accordance with the privacy policy of the relevant third party.": "kann mit Webseiten Dritter verbunden sein. Von Dritten gehostete Webseiten entziehen sich unserer Kontrolle. Wenn Sie auf einer Webseite eines Drittanbieters personenbezogene Daten eingeben, erfolgt die Verarbeitung dieser Daten nicht auf unserer Webseite, sondern auf der jeweiligen Webseite&nbsp;des Drittanbieters. Die von Ihnen angegebenen personenbezogenen Daten werden gemäß den Datenschutzbestimmungen des jeweiligen Dritten erhoben und verarbeitet.",
"Name": "Name",
"News": "Aktuelles",
"No commercial exploitation, reproduction, representation, use, adaptation, modification, incorporation, translation, commercialisation, in whole or in part, of these elements (including those that can be downloaded or copied) may take place without the prior written consent of LDC, with the exception of use for private purposes, subject to other or more restrictive provisions of intellectual property law.": "Ohne die vorherige schriftliche Zustimmung der Firma LDC darf keine kommerzielle Verwertung, Reproduktion, Darstellung, Verwendung, Anpassung, Änderung, Aufnahme, Übersetzung, Vermarktung, teilweise oder vollständig dieser Elemente (einschließlich der herunterladbaren oder kopierbaren) erfolgen Ausnahme der Nutzung für den privaten Gebrauch vorbehaltlich anderer oder noch restriktiverer Bestimmungen des Gesetzes über geistiges Eigentum.",
"No product found": "Kein Produkt gefunden",
"Not Found": "Nicht gefunden",

View file

@ -5,7 +5,6 @@ return [
'sliders' => 'Sliders',
'homes' => 'Startseite',
'ranges' => 'Bereiche',
'news' => 'Nachricht',
'products' => 'Produkte',
'concepts' => 'Konzepte',
'recipes' => 'Rezepte',

View file

@ -10,6 +10,5 @@ return [
'stores' => 'stores',
'contact-us' => 'kontakt',
'legals' => 'legals',
'privacy' => 'privacy',
'news' => 'aktuelles'
'privacy' => 'privacy'
];

View file

@ -5,7 +5,6 @@ return [
'sliders' => 'Sliders',
'homes' => 'Homepage',
'ranges' => 'Ranges',
'news' => 'News',
'products' => 'Products',
'concepts' => 'Concepts',
'recipes' => 'Recipes',

View file

@ -10,6 +10,5 @@ return [
'stores' => 'stores',
'contact-us' => 'contact-us',
'legals' => 'legals',
'privacy' => 'privacy',
'news' => 'news'
'privacy' => 'privacy'
];

View file

@ -178,7 +178,7 @@ return [
'minute' => 'minute',
'mobile' => 'mobile',
'month' => 'month',
'name' => 'Name',
'name' => 'name',
'national_code' => 'national code',
'number' => 'number',
'password' => 'password',
@ -208,17 +208,17 @@ return [
'test_name' => 'test name',
'text' => 'text',
'time' => 'time',
'title' => 'Title',
'updated_at' => 'Updated At',
'title' => 'title',
'updated_at' => 'updated at',
'username' => 'username',
'year' => 'year',
// * CUSTOMIZED
'order' => 'Order',
'subtitle' => 'Subtitle',
'label' => 'Label',
'region' => 'Region',
'visibility' => 'Visibility',
'type' => 'Type',
'order' => 'order',
'subtitle' => 'subtitle',
'label' => 'label',
'region' => 'region',
'visibility' => 'visibility',
'type' => 'type',
],
];

View file

@ -6,7 +6,6 @@
// 3. Include remainder of required Bootstrap stylesheets
@import "~/bootstrap/scss/variables";
@import "~/bootstrap/scss/variables-dark";
// 4. Include remainder of required parts
@import "~/bootstrap/scss/maps";

View file

@ -4,7 +4,6 @@
@import "./utilities/variables";
// * VENDOR
@import "toastify-js/src/toastify.css";
@import "./vendor/tailwind";
@import "./vendor/ckEditorContent";
@import "@ldc-web/cookie-notice";
@ -28,14 +27,3 @@
filter: drop-shadow(0 0 0.75rem #888);
}
}
.ck-content.scarlet-titles {
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: scarlet-wood, Arial, Helvetica, sans-serif;
}
}

View file

@ -8,10 +8,8 @@
// * Import bootstrap reboot to counter count theme aggressive reset
@import "~/bootstrap/scss/functions";
@import "~/bootstrap/scss/variables";
@import "~/bootstrap/scss/variables-dark";
@import "~/bootstrap/scss/maps";
@import "~/bootstrap/scss/mixins";
$theme-colors-rgb: map-loop($theme-colors, to-rgb, "$value");
@import "~/bootstrap/scss/root";
@import "~/bootstrap/scss/reboot";
}

View file

@ -1,130 +0,0 @@
<template>
<div class="max-w-screen-2xl mx-auto">
<div
v-if="newsList.length"
class="flex flex-col gap-0 mt-24"
>
<template
v-for="(news, newsIndex) of newsList"
:key="newsIndex"
>
<div
v-html="news"
class="flex flex-wrap flex-row my-3 sm:my-0 sm:even:flex-row sm:odd:flex-row-reverse"
/>
</template>
</div>
<div
v-else
class="text-left"
>
{{ __("No news for the moment") }}
</div>
<div class="flex justify-center mt-12 mb-48">
<button
v-if="!noMorePosts"
:disabled="loading"
class="button brown inline-flex items-center px-4 my-2 mt-6 mr-4 text-sm text-white"
@click.prevent="loadNextPage"
>
<FontAwesomeIcon
v-if="loading"
icon="fa-solid fa-cog"
:spin="true"
/>
{{ __("Load more posts") }}
</button>
</div>
</div>
</template>
<script lang="ts">
import axios from "axios";
import parseLaravelErrorMessages from "./../../modules/parseLaravelErrorMessages";
import route from "./../../modules/route";
import toast from "./../../modules/toast";
import trans from "./../../modules/trans";
import { defineComponent } from "vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
export default defineComponent({
name: "NewsInfiniteLoaderComponent",
mixins: [route, trans, toast, parseLaravelErrorMessages],
mounted() {
this.loadActu();
},
data(): {
pagination: number;
loading: boolean;
noMorePosts: boolean;
newsList: string[];
currentPage: number;
} {
return {
pagination: 15,
loading: false,
noMorePosts: false,
newsList: [],
currentPage: 1,
};
},
methods: {
loadNextPage(): void {
this.currentPage = this.currentPage + 1;
this.loadActu();
},
loadActu(): void {
const newsPaginateRoute = this.route("news.index", {
page: this.currentPage,
});
if (!newsPaginateRoute) {
throw Error("Undefined route 'news.index'");
}
this.loading = true;
axios
.get(newsPaginateRoute)
.then((response) => {
if (response.data.length < this.pagination) {
this.noMorePosts = true;
}
for (const news of response.data) {
this.newsList.push(this.base64decode(String(news)));
}
})
.then(() => (this.loading = false))
.catch((e) => {
console.log(e);
let message = this.__("http-statuses.0");
if (e.response.status === 422) {
message =
this.parseValidationErrors(e.response?.data?.errors ?? {}) ||
message;
if (window.vueDebug) {
console.warn(e.response.data.errors);
}
} else if (e.response.status === 419) {
// * CSRF TOKEN MISMATCH (On ne pourrais plus faire d'appels AJAX sans recharger la page)
setTimeout(() => window.location.reload(), 2000);
} else if (e.response.status === 403) {
// * Access denied
setTimeout(() => window.location.reload(), 2000);
} else {
if (window.vueDebug) {
console.error(e);
}
}
this.errorNotify(this.__(`http-statuses.${e.response.status}`));
this.errorNotify(message);
});
},
base64decode(str: string): string {
let decode = atob(str).replace(
"/[\x80-\uffff]/g",
(m) => `%${m.charCodeAt(0).toString(16).padStart(2, "0")}`
);
return decodeURIComponent(decode);
},
},
components: { FontAwesomeIcon },
});
</script>

View file

@ -38,12 +38,8 @@ import {
faMinus,
faHeader,
faCircleCheck,
faCog,
} from "@fortawesome/free-solid-svg-icons";
import {
faCircleXmark,
faCirclePlay,
} from "@fortawesome/free-regular-svg-icons";
import { faCircleXmark, faCirclePlay } from "@fortawesome/free-regular-svg-icons";
config.autoReplaceSvg = false;
library.add(faEraser);
@ -88,6 +84,5 @@ library.add(faCircleNotch);
library.add(faSliders);
library.add(faCircleCheck);
library.add(faCirclePlay);
library.add(faCog);
dom.watch();

View file

@ -5,7 +5,7 @@
</a>
</li>
<li class="nav-item">
<a class="nav-link @if (Route::is('bo.sliders.*'))active @endif" href="{{ route('bo.sliders.index') }}" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.sliders')]) }}">
<a class="nav-link @if (Route::is('bo.sliders.*'))active @endif" href="{{ route('bo.sliders.index') }}" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.listall', ['model' => __('models.classes.sliders')]) }}">
{{ __('models.classes.sliders') }}
</a>
</li>
@ -15,27 +15,22 @@
</a>
</li>
<li class="nav-item">
<a class="nav-link @if (Route::is('bo.news.*'))active @endif" href="{{ route('bo.news.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.news')]) }}">
{{ __('models.classes.news') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link @if (Route::is('bo.concepts.*'))active @endif" href="{{ route('bo.concepts.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.concepts')]) }}">
<a class="nav-link @if (Route::is('bo.concepts.*'))active @endif" href="{{ route('bo.concepts.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.listall', ['model' => __('models.classes.concepts')]) }}">
{{ __('models.classes.concepts') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link @if (Route::is('bo.ranges.*'))active @endif" href="{{ route('bo.ranges.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.ranges')]) }}">
<a class="nav-link @if (Route::is('bo.ranges.*'))active @endif" href="{{ route('bo.ranges.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.listall', ['model' => __('models.classes.ranges')]) }}">
{{ __('models.classes.ranges') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link @if (Route::is('bo.products.*'))active @endif" href="{{ route('bo.products.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.products')]) }}">
<a class="nav-link @if (Route::is('bo.products.*'))active @endif" href="{{ route('bo.products.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.listall', ['model' => __('models.classes.products')]) }}">
{{ __('models.classes.products') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link @if (Route::is('bo.recipes.*'))active @endif" href="{{ route('bo.recipes.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.recipes')]) }}">
<a class="nav-link @if (Route::is('bo.recipes.*'))active @endif" href="{{ route('bo.recipes.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.listall', ['model' => __('models.classes.recipes')]) }}">
{{ __('models.classes.recipes') }}
</a>
</li>
@ -45,25 +40,25 @@
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item @if (Route::is('bo.stores.*'))active @endif" href="{{ route('bo.stores.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.stores')]) }}">
<a class="dropdown-item @if (Route::is('bo.stores.*'))active @endif" href="{{ route('bo.stores.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.listall', ['model' => __('models.classes.stores')]) }}">
{{ __('models.classes.stores') }}
</a>
</li>
<li>
<a class="dropdown-item @if (Route::is('bo.storegroups.*'))active @endif" href="{{ route('bo.storegroups.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.storegroups')]) }}">
<a class="dropdown-item @if (Route::is('bo.storegroups.*'))active @endif" href="{{ route('bo.storegroups.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.listall', ['model' => __('models.classes.storegroups')]) }}">
{{ __('models.classes.storegroups') }}
</a>
</li>
<li>
<a class="dropdown-item @if (Route::is('bo.storeregions.*'))active @endif" href="{{ route('bo.storeregions.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.storeregions')]) }}">
<a class="dropdown-item @if (Route::is('bo.storeregions.*'))active @endif" href="{{ route('bo.storeregions.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.listall', ['model' => __('models.classes.storeregions')]) }}">
{{ __('models.classes.storeregions') }}
</a>
</li>
<li>
<a class="dropdown-item @if (Route::is('bo.storetypes.*'))active @endif" href="{{ route('bo.storetypes.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.list_all', ['model' => __('models.classes.storetypes')]) }}">
<a class="dropdown-item @if (Route::is('bo.storetypes.*'))active @endif" href="{{ route('bo.storetypes.index') }}?rst=1" role="button" data-bs-toggle="tooltip" title="{{ __('crud.helpers.listall', ['model' => __('models.classes.storetypes')]) }}">
{{ __('models.classes.storetypes') }}
</a>
</li>
</ul>
</li>
@endauth
@endauth

View file

@ -160,7 +160,7 @@
'hasWordCounter' => true,
'name' => 'data['.$k.'][alt]',
'value' => old("data.{$k}.alt", $input['alt'] ?? null),
'required' => is_bool($input['required'] ?? null) ? $input['required'] : true,
'required' => true,
'model' => $model ?? null,
'helper' => __('crud.minicrud.picture_alt_helper') . '. ' . __('validation.min.string', [
'attribute' => __('crud.minicrud.picture_alt_label'),
@ -179,7 +179,7 @@
'hasWordCounter' => true,
'name' => 'data['.$k.'][title]',
'value' => old("data.{$k}.title", $input['title'] ?? null),
'required' => is_bool($input['required'] ?? null) ? $input['required'] : true,
'required' => true,
'model' => $model ?? null,
'helper' => __('crud.minicrud.picture_title_helper') . '. ' . __('validation.min.string', [
'attribute' => __('crud.minicrud.picture_title_label'),
@ -203,7 +203,7 @@
'width' => $input['dimensions']['width'],
'height' => $input['dimensions']['height'],
]),
'required' => is_bool($input['required'] ?? null) ? $input['required'] : true,
'required' => true,
'preview' => true,
])
@include('modules.input-error', [

View file

@ -1,7 +1,7 @@
@extends('bo.layout')
@section('title', __('crud.helpers.list_all', ['model' => __('models.classes.concepts')]))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.concepts')]))
@section('title', __('crud.helpers.listall', ['model' => __('models.classes.concepts')]))
@section('description', __('crud.helpers.listall', ['model' => __('models.classes.concepts')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -45,4 +45,4 @@
</div>
</div>
</div>
@endsection
@endsection

View file

@ -1,36 +0,0 @@
@extends('bo.layout')
@section('title', sprintf('%s - %s', Str::singular(__('models.classes.news')), Str::ucfirst(__('create'))))
@section('description', __('Creation of a new news.'))
@section('breadcrumb', request()->route()->getName())
@section('content')
<div class="card">
<div class="card-header">
<div class="row">
<div class="col">
<h1 class="col h2">
<a href="{{ route('bo.news.index') }}" class="btn btn-primary text-decoration-none m-0" data-bs="tooltip" data-bs-placement="top" title="{{ __('Return to the :model list', ['model' => __('models.classes.news')]) }}" data-bs-toggle="tooltip">
<i class="fa-solid fa-arrow-left"></i>
</a>
<span class="fw-bold">{{ Str::singular(__('models.classes.news')) }}</span>&nbsp;<small class="text-muted">{{ Str::ucfirst(__('create')) }}</small>
</h1>
</div>
</div>
</div>
<div class="card-body bg-light">
<form id="formSubmit" action="{{ route('bo.news.store') }}" method="POST" enctype="multipart/form-data">
@csrf
@include('bo.pages.news.form-inputs')
<div class="row">
<div class="col text-center">
<button type="submit" class="btn btn-primary" title="{{ __('Save :model', ['model' => Str::singular(__('models.classes.news'))]) }}" data-bs-toggle="tooltip">
{{ __('crud.actions.save') }}
</button>
</div>
</div>
</form>
</div>
</div>
@endsection

View file

@ -1,37 +0,0 @@
@extends('bo.layout', ['brParam' => $newsModel])
@section('title', sprintf('%s - %s %s', Str::singular(__('models.classes.news')), Str::ucfirst(__('crud.actions.edit')), $newsModel->seo_title))
@section('description', $newsModel->seo_description)
@section('breadcrumb', request()->route()->getName())
@section('content')
<div class="card">
<div class="card-header">
<div class="row">
<div class="col">
<h1 class="col h2">
<a href="{{ route('bo.news.index') }}" class="btn btn-primary text-decoration-none m-0" data-bs="tooltip" data-bs-placement="top" title="{{ __('Return to the :model list', ['model' => __('models.classes.news')]) }}" data-bs-toggle="tooltip">
<i class="fa-solid fa-arrow-left"></i>
</a>
<span class="fw-bold">{{ Str::singular(__('models.classes.news')) }}</span>&nbsp;<small class="text-muted">{{ Str::ucfirst(__('crud.actions.edit')) }}</small>
</h1>
</div>
</div>
</div>
<div class="card-body bg-light">
<form id="formSubmit" action="{{ route('bo.news.update', $newsModel->id) }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
@include('bo.pages.news.form-inputs', ['model' => $newsModel])
<div class="row">
<div class="col text-center">
<button type="submit" class="btn btn-primary" title="{{ __('Save :model', ['model' => Str::singular(__('models.classes.news'))]) }}" data-bs-toggle="tooltip">
{{ __('crud.actions.save') }}
</button>
</div>
</div>
</form>
</div>
</div>
@endsection

View file

@ -1,259 +0,0 @@
<div class="row">
{{-- SEO_TITLE --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3">
<legend>{{ __('SEO Title') }}</legend>
<div class="row">
<div class="col-12 mb-3">
@include('bo.partials.inputs.varchar', [
'id' => 'newsSeoTitle',
'label' => __('SEO title of the :model', ['model' => Str::singular(__('models.classes.news'))]),
'hasWordCounter' => true,
'name' => 'seo_title',
'required' => true,
'model' => $newsModel ?? null,
'helper' => __('validation.between.string', [
'attribute' => __('SEO title of the :model', ['model' => Str::singular(__('models.classes.news'))]),
'min' => 3,
'max' => 255
]),
'placeholder' => __('A beautiful title'),
])
</div>
</div>
</fieldset>
</div>
{{-- SEO_DESCRIPTION --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3">
<legend>{{ __('SEO Description') }}</legend>
<div class="row">
<div class="col-12">
@include('bo.partials.inputs.varchar-multiline', [
'id' => 'newsSeoDescription',
'label' => __('SEO description of the :model', ['model' => Str::singular(__('models.classes.news'))]),
'name' => 'seo_description',
'model' => $newsModel ?? null,
'required' => true,
'helper' => __('validation.between.string', [
'attribute' => __('SEO description of the :model', ['model' => Str::singular(__('models.classes.products'))]),
'min' => 50,
'max' => 160
]),
'placeholder' => __('A description of the content for search engines.'),
])
</div>
</div>
</fieldset>
</div>
</div>
<div class="row">
{{-- TITLE --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3">
<legend>{{ __('Title') }}</legend>
<div class="row">
<div class="col-12 mb-3">
@include('bo.partials.inputs.varchar', [
'id' => 'newsTitle',
'label' => __('Title of the :model', ['model' => Str::singular(__('models.classes.news'))]),
'hasWordCounter' => true,
'name' => 'title',
'required' => true,
'model' => $newsModel ?? null,
'helper' => __('validation.between.string', [
'attribute' => __('Title of the :model', ['model' => Str::singular(__('models.classes.news'))]),
'min' => 3,
'max' => 255
]),
'placeholder' => __('A beautiful title'),
])
</div>
</div>
</fieldset>
</div>
{{-- SUBTITLE --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3">
<legend>{{ __('Subtitle') }}</legend>
<div class="row">
<div class="col-12 mb-3">
@include('bo.partials.inputs.varchar', [
'id' => 'newsSubtitle',
'label' => __('Subtitle of the :model', ['model' => Str::singular(__('models.classes.news'))]),
'hasWordCounter' => true,
'name' => 'subtitle',
'required' => true,
'model' => $newsModel ?? null,
'helper' => __('validation.between.string', [
'attribute' => __('Subtitle of the :model', ['model' => Str::singular(__('models.classes.news'))]),
'min' => 3,
'max' => 255
]),
'placeholder' => __('A beautiful subtitle'),
])
</div>
</div>
</fieldset>
</div>
{{-- CONTENT --}}
<div class="col-12 mb-3">
<fieldset class="border bg-white p-3">
<legend>{{ __('Content') }}</legend>
<div class="row mb-3">
<div class="col-12">
@include('bo.partials.inputs.text-html', [
'id' => 'newContent',
'label' => __('Description of the :model', ['model' => Str::singular(__('models.classes.news'))]),
'name' => 'content',
'model' => $newsModel ?? null,
'required' => true,
'helper' => __('Proofread your content before publishing it (minimum 15 characters).'),
])
</div>
</div>
</fieldset>
</div>
{{-- MINICRUD --}}
<div class="col-12 mb-3">
<button class="btn btn-primary col-form-label rounded-0 dropdown-toggle w-100" type="button" data-bs-toggle="collapse" data-bs-target="#minicrud" aria-expanded="false" aria-controls="minicrud">
<b>{{ __('ALL BLOCS') }}</b>
</button>
<div class="collapse" id="minicrud">
<div class="card card-body bg-light rounded-0">
<div class="row">
@include('bo.modules.minicrud-form-inputs', [
'data' => $newsModel->data ?? \App\Models\News::getEmptyData()
])
</div>
</div>
</div>
</div>
{{-- PUBLICATION --}}
<div class="col-12 mb-3">
<fieldset class="border h-100 bg-white p-3">
<legend>{{ __('Publication') }}</legend>
<div class="row mb-3">
<div class="col">
<div class="form-group">
@include('bo.partials.inputs.publish-dates', [
'label' => __('Date de publication (Europe/Paris)'),
'id' => 'newsPublishedAt',
'name' => 'publish_at',
'model' => $newsModel ?? null,
])
</div>
</div>
<div class="col">
<div class="form-group">
@include('bo.partials.inputs.publish-dates', [
'label' => __('Date de fin de publication (Europe/Paris)'),
'id' => 'newsUnPublishedAt',
'name' => 'unpublish_at',
'model' => $newsModel ?? null,
])
</div>
</div>
</div>
</fieldset>
</div>
{{-- LOCALES --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3">
<legend>{{ __('Languages in which to display the news') }}</legend>
<div class="row">
<div class="col-12">
@php
$itemsList = collect(config('app.locales'))->map(fn($locale) => (object)[
'label' => $locale, 'value' => $locale,
]);
$values = collect(old('locales', $newsModel->locales ?? []))->values();
$selectedItemList = collect($itemsList)->filter(fn($locale) =>
$values->filter(fn($value) => $value === $locale->value)->count())->values();
@endphp
@include('bo.partials.inputs.select-multiple-enum', [
'id' => 'newsLocales',
'label' => __('Locales in which to display the news'),
'name' => 'locales',
'itemsSelected' => $selectedItemList,
'itemsList' => $itemsList,
'itemLimit' => [0, count($itemsList)],
'placeholder' => __('Search a specific Locales'),
'helper' => __('Select Locales(s) among this list in which the news should display (minimum 1, maximum :max)', [
'max' => count($itemsList)
]),
'required' => true,
])
</div>
</div>
</fieldset>
</div>
{{-- STATUS --}}
<div class="col-12 col-md-6 mb-3">
<fieldset class="border h-100 bg-white p-3">
<legend>{{ __('crud.minicrud.visibility_label') }}</legend>
<div class="row">
<div class="col-12">
@include('bo.partials.inputs.tinyint-boolean', [
'id' => 'newsStatus',
'name' => 'status',
'model' => $newsModel ?? null,
'label' => __('crud.minicrud.visibility_label'),
'helper' => __('crud.minicrud.visibility_helper'),
])
</div>
</div>
</fieldset>
</div>
</div>
@push('scripts')
<script nonce="{{ $nonce }}">
document.addEventListener('DOMContentLoaded', function() {
const hasLoadedComponentsMarker = 'data[bloc_3_image][value]';
const displayBloc = function(index) {
const switchInput = document.querySelector(`input[name="data[bloc_${index}_display_picture][value]"]`);
const inputList = [
`data[bloc_${index}_image][value]`,
];
const inputs = [];
inputList.forEach((name) => {
const input = document.querySelector(`input[name="${name}"]`) ??
document.querySelector(`select[name="${name}"]`);
inputs.push(input);
});
inputs.forEach((input) => {
// * hide.
if (!switchInput.checked) {
input.closest('.minicrud-input ').classList.add('d-none');
}
});
switchInput.addEventListener('change', function() {
inputs.forEach((input) => {
// * Toggle hide.
input.closest('.minicrud-input ').classList.toggle('d-none');
});
});
};
const waitForLoaded = () => {
setTimeout(() => {
if (!document.querySelector(`input[name="${hasLoadedComponentsMarker}"]`)) {
waitForLoaded();
return;
}
displayBloc(1);
displayBloc(2);
displayBloc(3);
}, 300);
};
waitForLoaded();
});
</script>
@endpush

View file

@ -1,64 +0,0 @@
<div class="table-responsive">
<table class="table fix-action m-0">
<thead>
@php
$cols = [
'title' => __('validation.attributes.title'),
'subtitle' => __('validation.attributes.subtitle'),
'order' => __('validation.attributes.order'),
'updated_at' => __('validation.attributes.updated_at')];
@endphp
@include('bo.modules.table-col-sorter', ['cols' => $cols, 'mobileHide' => ['status']])
</thead>
<tbody>
@if($newsListModels->count())
@foreach ($newsListModels as $newsModels)
<tr>
<td class="align-middle">{{ $newsModels->title }}</td>
<td class="align-middle">{{ $newsModels->subtitle }}</td>
@php $routeName = request()->route()->getName(); @endphp
@if(empty(request()->search) and empty($noOrder) and Session::get("$routeName.sort_col") === 'order')
<td class="d-flex">
<form action="{{ route('bo.news.change-order', ['news' => $newsModels, 'action' => 'up']) }}" method="POST" class="text-center @if($loop->first and $newsListModels->onFirstPage()) invisible @endif">
@csrf
<button type="submit" class="btn btn-sm @if(($loop->first and $newsListModels->onFirstPage())) disabled @endif" title="{{ __('Change the order of appearance upwards') }}" data-bs-toggle="tooltip">
<i class="fas fa-arrow-alt-circle-up"></i>
</button>
</form>
<form action="{{ route('bo.news.change-order', ['news' => $newsModels, 'action' => 'down']) }}" method="POST" class="text-center @if($loop->last and $newsListModels->currentPage() === $newsListModels->lastPage()) invisible @endif">
@csrf
<button type="submit" class="btn btn-sm @if($loop->last and $newsListModels->currentPage() === $newsListModels->lastPage()) disabled @endif" title="{{ __('Change the order of appearance downwards') }}" data-bs-toggle="tooltip">
<i class="fas fa-arrow-alt-circle-down"></i>
</button>
</form>
</td>
@endif
<td class="align-middle">
<span class="badge bg-warning text-dark text-wrap">{{ $newsModels->updated_at->formatLocalized('%d/%m/%Y à %H:%M') }}</span>
</td>
<td class="align-middle">
<form action="{{ route('bo.news.destroy', $newsModels->id) }}" method="POST" class="btn-group confirmDeleteJS" novalidate>
<a class="btn btn-sm btn-primary" href="{{ route('bo.news.edit', $newsModels->id) }}" title="{{ __('crud.helpers.update_model', ['model' => Str::singular(__('models.classes.news'))]) }}" data-bs-toggle="tooltip">
<i class="fa-solid fa-pencil"></i>
</a>
@method('DELETE')
@csrf
<div class="btn-group" role="group">
<button class="btn btn-sm btn-danger" title="{{ __('crud.helpers.delete_model', ['model' => Str::singular(__('models.classes.news'))]) }}" data-bs-toggle="tooltip">
<i class="fa-solid fa-trash"></i>
</button>
</div>
</form>
</td>
</tr>
@endforeach
@else
<tr>
<td colspan="6" class="text-center">
<span class="h3">{{ __('crud.helpers.no_results', ['models' => __('models.classes.news') ])}}</span>
</td>
</tr>
@endif
</tbody>
</table>
</div>

View file

@ -1,48 +0,0 @@
@extends('bo.layout')
@section('title', __('crud.helpers.list_all', ['model' => __('models.classes.news')]))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.news')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
<div class="container">
<div class="row">
<div class="col">
<div class="card">
<div class="card-header">
<div class="row">
<div class="col">
<h1 class="h2 d-inline"><span class="fw-bold">{{ __('models.classes.news') }}</span>&nbsp;<small class="text-muted">{{ Str::ucfirst(__('crud.helpers.list')) }}</small>&nbsp;&nbsp;</h1>
</div>
<div class="col-md-3 d-flex align-items-center">
<div class="input-group justify-content-end">
<a href="{{ route('bo.news.create') }}" class="btn btn-primary" title="{{ __('crud.helpers.create_model', ['model' => Str::singular(__('models.classes.news'))]) }}" data-bs-toggle="tooltip">
<i class="fa-solid fa-plus"></i>
</a>
</div>
</div>
</div>
</div>
<div class="card-body">
@include('bo.modules.search-bar')
<div class="row justify-content-center">
<div class="col-12 col-md-6 p-0">
{{ $newsListModels->withQueryString()->onEachSide(1)->links() }}
</div>
</div>
<div class="row">
<div class="col">
@include('bo.pages.news.index-list')
</div>
</div>
<div class="row justify-content-center">
<div class="col-12 col-md-6 p-0">
{{ $newsListModels->withQueryString()->onEachSide(1)->links() }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -1,7 +1,7 @@
@extends('bo.layout')
@section('title', __('crud.helpers.list_all', ['model' => __('models.classes.products')]))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.products')]))
@section('title', __('crud.helpers.listall', ['model' => __('models.classes.products')]))
@section('description', __('crud.helpers.listall', ['model' => __('models.classes.products')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -45,4 +45,4 @@
</div>
</div>
</div>
@endsection
@endsection

View file

@ -1,7 +1,7 @@
@extends('bo.layout')
@section('title', __('crud.helpers.list_all', ['model' => __('models.classes.ranges')]))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.ranges')]))
@section('title', __('crud.helpers.listall', ['model' => __('models.classes.ranges')]))
@section('description', __('crud.helpers.listall', ['model' => __('models.classes.ranges')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -45,4 +45,4 @@
</div>
</div>
</div>
@endsection
@endsection

View file

@ -1,7 +1,7 @@
@extends('bo.layout')
@section('title', sprintf('%s - %s', __('models.classes.recipes'), Str::ucfirst(__('crud.helpers.list'))))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.recipes')]))
@section('description', __('crud.helpers.listall', ['model' => __('models.classes.recipes')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -45,4 +45,4 @@
</div>
</div>
</div>
@endsection
@endsection

View file

@ -1,7 +1,7 @@
@extends('bo.layout')
@section('title', __('crud.helpers.list_all', ['model' => __('models.classes.sliders')]))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.sliders')]))
@section('title', __('crud.helpers.listall', ['model' => __('models.classes.sliders')]))
@section('description', __('crud.helpers.listall', ['model' => __('models.classes.sliders')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -45,4 +45,4 @@
</div>
</div>
</div>
@endsection
@endsection

View file

@ -1,7 +1,7 @@
@extends('bo.layout')
@section('title', __('crud.helpers.list_all', ['model' => __('models.classes.storegroups')]))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.storegroups')]))
@section('title', __('crud.helpers.listall', ['model' => __('models.classes.storegroups')]))
@section('description', __('crud.helpers.listall', ['model' => __('models.classes.storegroups')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -45,4 +45,4 @@
</div>
</div>
</div>
@endsection
@endsection

View file

@ -1,7 +1,7 @@
@extends('bo.layout')
@section('title', sprintf('%s - %s', __('models.classes.storeregions'), Str::ucfirst(__('crud.helpers.list'))))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.storeregions')]))
@section('description', __('crud.helpers.listall', ['model' => __('models.classes.storeregions')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -45,4 +45,4 @@
</div>
</div>
</div>
@endsection
@endsection

View file

@ -1,7 +1,7 @@
@extends('bo.layout')
@section('title', __('crud.helpers.list_all', ['model' => __('models.classes.stores')]))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.stores')]))
@section('title', __('crud.helpers.listall', ['model' => __('models.classes.stores')]))
@section('description', __('crud.helpers.listall', ['model' => __('models.classes.stores')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -45,4 +45,4 @@
</div>
</div>
</div>
@endsection
@endsection

View file

@ -1,7 +1,7 @@
@extends('bo.layout')
@section('title', __('crud.helpers.list_all', ['model' => __('models.classes.storetypes')]))
@section('description', __('crud.helpers.list_all', ['model' => __('models.classes.storetypes')]))
@section('title', __('crud.helpers.listall', ['model' => __('models.classes.storetypes')]))
@section('description', __('crud.helpers.listall', ['model' => __('models.classes.storetypes')]))
@section('breadcrumb', request()->route()->getName())
@section('content')
@ -45,4 +45,4 @@
</div>
</div>
</div>
@endsection
@endsection

View file

@ -20,7 +20,6 @@
@include('fo.vendor/ldcweb-cookies')
@stack('scripts-constants')
@include('modules.script-constants')
@include('modules.script-routes')
@stack('scripts')
@vite(['resources/ts/fo.ts'])
<script nonce="{{ $nonce }}">

View file

@ -1,37 +0,0 @@
<!DOCTYPE html>
<html dir="ltr" lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
@include('fo.layouts.head')
<meta name="p:domain_verify" content="5f89c5d6c41555aa17ff8d26cc6661f2" />
</head>
<body class="relative w-full min-h-screen font-roboto bg-hellgrau text-p lg:text-p-lg mt-[87px] scroll-smooth">
<a name="top" id="top"></a>
@include('fo.layouts.header')
@yield('content')
@include('fo.layouts.footer')
@include('fo.layouts.navigation')
@include('fo.vendor.ldcweb-cookies')
@stack('scripts-constants')
@include('modules.script-constants')
@stack('scripts')
@vite(['resources/ts/fo.ts'])
<script nonce="{{ $nonce }}">
let mobileMenuOpen = false;
function toggleMobileMenuOpen() {
mobileMenuOpen = !mobileMenuOpen;
const navMenu = document.getElementById('navigationModule');
navMenu.classList.toggle('hidden');
navMenu.classList.toggle('flex');
}
</script>
</body>
</html>

View file

@ -27,10 +27,8 @@
</ul>
</li>
<li class="mr-6 mb-4 relative">
@php $firstConcept = \App\Models\Concept::query()->first(); @endphp
@if($firstConcept)
@php $firstConcept = \App\Models\Concept::query()->firstOrFail(); @endphp
<a href="{{ route('concept.show', ['concept' => $firstConcept]) }}">{{ __('Our concept') }}</a>
@endif
<ul class="xinvisible xgroup-hover:visible pl-8 lg:pl-0 lg:absolute lg:left-0 lg:right-auto w-max pt-2">
@foreach ($conceptListGlobal as $concept)
<li class="mt-2">

View file

@ -3,7 +3,7 @@
<div class="max-w-7xl w-full mx-auto relative flex items-center">
<a href="{{ route('home') }}" class="inline-block mx-auto absolute left-0 right-0 top-0 "><img src="{{ Vite::asset('resources/assets/images/Icons/logo-nature-respect.svg') }}" alt="Unternehmenslogo" class="w-16 sm:w-[120px] block mx-auto"></a>
<nav class="text-lg text-braun absolute right-8 sm:right-12 flex items-center top-[8px]">
<a id="menu-burger-link" href="#" class="md:mr-7 font-hobo font-semibold text-p-lg inline-flex items-center"><img class="w-4 mr-4" src="{{ Vite::asset('resources/assets/images/Icons/menu-burger.svg') }}">{{ __('Menu') }}</a>
<a id="menu-burger-link" href="#" class="sm:mr-7 font-hobo font-semibold text-p-lg inline-flex items-center"><img class="w-4 mr-4" src="{{ Vite::asset('resources/assets/images/Icons/menu-burger.svg') }}">{{ __('Menu') }}</a>
@push('scripts')
<script nonce="{{ $nonce }}">
document.addEventListener('DOMContentLoaded', function () {
@ -13,7 +13,7 @@
@endpush
<a href="https://www.instagram.com/nature_and_respect/" class="hidden sm:inline-block mr-2 ml-[36px]" target="_blank"><img src="{{ Vite::asset('resources/assets/images/Icons/instagram-icon.svg') }}" alt="Instagram" class="w-8"></a>
<a href="https://www.pinterest.de/natureandrespect" class="hidden sm:inline-block mr-0" target="_blank"><img src="{{ Vite::asset('resources/assets/images/Icons/pinterest-icon.svg') }}" alt="Pinterest" class="w-8"></a>
<div class="ml-4 font-hobo text-p-lg hidden lg:inline-block">
<div class="ml-4 font-hobo text-p-lg">
@foreach (config('app.locales') as $locale)
@if (!$loop->first)
|

View file

@ -2,66 +2,53 @@
<nav class="main fixed top-0 left-0 z-[150] flex-col w-full h-screen font-semibold text-center bg-weiss p-0 hidden" id="navigationModule">
<div class="relative w-full h-screen max-w-7xl mx-auto text-braun flex items-center">
<a id="menu-burger-link-close" class="absolute right-10 top-[16px] sm:right-[11.25rem] md:right-[13rem] lg:right-[18.6rem] xl:right-[17rem] sm:top-[34px] font-hobo text-p-lg cursor-pointer inline-flex items-center"><img class="w-4 mr-4" src="{{ Vite::asset('resources/assets/images/Icons/menu-close.svg') }}"> {{ __('Menu') }}</a>
@push('scripts')
<script nonce="{{ $nonce }}">
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('menu-burger-link-close').addEventListener('click', () => toggleMobileMenuOpen());
});
</script>
@endpush
<a href="">
<img src="{{ Vite::asset('resources/assets/images/Icons/logo-nature-respect.svg') }}" alt="{{ __('Unternehmenslogo') }}" class="w-16 sm:w-[120px] absolute left-0 right-0 mx-auto top-2 sm:top-6">
</a>
<a id="menu-burger-link-close" class="absolute right-10 top-[16px] sm:right-[144px] sm:top-[34px] font-hobo text-p-lg cursor-pointer inline-flex items-center"><img class="w-4 mr-4" src="{{ Vite::asset('resources/assets/images/Icons/menu-close.svg') }}"> {{ __('Menu') }}</a>
@push('scripts')
<script nonce="{{ $nonce }}">
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('menu-burger-link-close').addEventListener('click', () => toggleMobileMenuOpen());
});
</script>
@endpush
<a href="">
<img src="{{ Vite::asset('resources/assets/images/Icons/logo-nature-respect.svg') }}" alt="{{ __('Unternehmenslogo') }}" class="w-16 sm:w-[120px] absolute left-0 right-0 mx-auto top-2 sm:top-6">
</a>
<ul id="frontend_menu" class="font-hobo text-left md:text-h3-md lg:text-h3-lg xl:text-h3 ml-6 md:ml-12">
<li class="mb-2 block leading-[180%] relative">
<a href="">{{ __('Home') }}</a>
</li>
<li class="mb-2 block leading-[180%] relative">
@php $concept = \App\Models\Concept::query()->first(); @endphp
@if($concept)
<a href="{{ route('concept.show', ['concept' => $concept]) }}">{{ __('Our concept') }}</a>
@endif
<ul class="pl-8 sm:absolute sm:top-0 sm:left-full sm:pl-24 w-max">
@foreach ($conceptListGlobal as $concept)
<li class="mb-2 block leading-[180%]">
<a class="block" href="{{ route('concept.show', ['concept' => $concept]) }}">{{ $concept->name }}</a>
</li>
@endforeach
</ul>
</li>
<li class="mb-2 block leading-[180%] relative">
<a href="{{ route('news.index') }}">{{ __('News') }}</a>
</li>
<li class="mb-2 block leading-[180%] relative">
<a href="{{ route('products.index') }}">{{ __('Our products') }}</a>
</li>
<li class="mb-2 block leading-[180%] relative">
<a href="{{ route('recipes.index') }}">{{ __('Our recipes') }}</a>
</li>
<li class="mb-2 block leading-[180%] relative">
<a href="{{ route('storefinder.index') }}">{{ __('Store finder') }}</a>
</li>
<li class="mb-2 block leading-[180%] relative">
<a href="{{ route('contact-form') }}">{{ __('Contact Us') }}</a>
</li>
</ul>
<ul id="frontend_menu" class="font-hobo text-left md:text-h3-md lg:text-h3-lg xl:text-h3 ml-6 md:ml-12">
<li class="mb-2 block leading-[180%] relative">
<a href="">{{ __('Home') }}</a>
</li>
<li class="mb-2 block leading-[180%] relative">
@php $concept = \App\Models\Concept::query()->firstOrFail(); @endphp
<a href="{{ route('concept.show', ['concept' => $concept]) }}">{{ __('Our concept') }}</a>
<ul class="pl-8 sm:absolute sm:top-0 sm:left-full sm:pl-24 w-max">
@foreach ($conceptListGlobal as $concept)
<li class="mb-2 block leading-[180%]">
<a class="block" href="{{ route('concept.show', ['concept' => $concept]) }}">{{ $concept->name }}</a>
</li>
@endforeach
</ul>
</li>
<li class="mb-2 block leading-[180%] relative">
<a href="{{ route('products.index') }}">{{ __('Our products') }}</a>
</li>
<li class="mb-2 block leading-[180%] relative">
<a href="{{ route('recipes.index') }}">{{ __('Our recipes') }}</a>
</li>
<li class="mb-2 block leading-[180%] relative">
<a href="{{ route('storefinder.index') }}">{{ __('Store finder') }}</a>
</li>
<li class="mb-2 block leading-[180%] relative">
<a href="{{ route('contact-form') }}">{{ __('Contact Us') }}</a>
</li>
</ul>
<div class="absolute bottom-16 right-8 flex items-center font-hobo">
{{ __('Follow us') }}
<div class="absolute bottom-8 right-8 flex items-center font-hobo">
{{ __('Follow us') }}
<a href="https://www.instagram.com/nature_and_respect/" class="inline-block mr-2 ml-5" target="_blank"><img src="{{ Vite::asset('resources/assets/images/Icons/instagram-icon.svg') }}" alt="Instagram" class="w-6"></a>
<a href="https://www.pinterest.de/natureandrespect" class="inline-block mr-0" target="_blank"><img src="{{ Vite::asset('resources/assets/images/Icons/pinterest-icon.svg') }}" alt="Pinterest" class="w-6"></a>
</div>
<div class="absolute bottom-8 right-8 font-hobo text-p-lg">
@foreach (config('app.locales') as $locale)
@if (!$loop->first)
|
@endif
<a href="{{ route('home', [], true, $locale) }}" class="@if (app()->currentLocale() === $locale) text-hellgruen @endif">{{ Str::upper($locale) }}</a>
@endforeach
</div>
<a href="https://www.instagram.com/nature_and_respect/" class="inline-block mr-2 ml-5" target="_blank"><img src="{{ Vite::asset('resources/assets/images/Icons/instagram-icon.svg') }}" alt="Instagram" class="w-6"></a>
<a href="https://www.pinterest.de/natureandrespect" class="inline-block mr-0" target="_blank"><img src="{{ Vite::asset('resources/assets/images/Icons/pinterest-icon.svg') }}" alt="Pinterest" class="w-6"></a>
</div>
</div>
</nav>

View file

@ -1,35 +0,0 @@
@if ($relatedNewsList->count() > 0)
<aside class="w-full text-braun py-16 mb-24">
<div class="container max-w-7xl mx-auto px-4 sm:px-8">
<div class="w-full mb-12">
<span class="block font-scarletwood text-h2-md lg:text-h2-lg xl:text-h2">{{
__('Headline more news')
}}</span>
</div>
</div>
<div class="container max-w-7xl mx-auto px-4 sm:px-8">
<div class="w-full flex flex-col sm:flex-row gap-8 mt-12">
@foreach($relatedNewsList as $newsModel)
<article class="flex flex-col basis-full sm:basis-1/2 justify-start">
<div class="m-4 hover:scale-105 ease-in-out duration-300">
<a href="{{ route('news.show', $newsModel) }}" title="{{ __('Learn more') }}">
@php $picture = $newsModel->picture_list->first(); @endphp
@if ($picture)
<img src="{{ asset(resize($picture->value, 'foNewsIndexPicture')) }}" alt="{{ $picture->alt }}" title="{{ $picture->title }}">
@else
<img src="{{ Vite::asset('resources/assets/images/news_placeholder.png') }}" alt="{{ $newsModel->title }}" title="{{ $newsModel->title }}">
@endif
</a>
</div>
<div class="m-4">
<div class="font-scarletwood text-h3-md lg:text-h2-lg xl:text-h2">
<a href="{{ route('news.show', $newsModel) }}" title="{{ __('Learn more') }}">{{ $newsModel->title }}</a>
</div>
<div>{!! Str::limit(htmlentities(strip_tags($newsModel->content)), 250) !!}</div>
</div>
</article>
@endforeach
</div>
</div>
</aside>
@endif

View file

@ -1,20 +0,0 @@
<div class="basis-full sm:basis-1/2 flex">
@if(count($pictures))
@php $picture = collect($pictures)->first(); @endphp
<img src="{{ asset(resize($picture->value, 'foNewsIndexPicture')) }}" alt="{{ $picture->alt }}" title="{{ $picture->title }}">
@else
<img src="{{ Vite::asset('resources/assets/images/news_placeholder.png') }}" alt="{{ $newsModel->title }}" title="{{ $newsModel->title }}">
@endif
</div>
<div class="bg-hellgruen basis-full sm:basis-1/2 order-2 text-weiss flex items-center">
<div class="p-6 sm:pl-6 sm:py-4 lg:pl-16 lg:py-8 xl:pl-32 xl:py-12 max-w-xl">
<h5 class="my-0.5">{{ $newsModel->updated_at->formatLocalized('%d.%m.%Y') }}</h5>
<h3 class="text-h2-sm md:text-h2-md lg:text-h2-lg xl:text-h2 mb-2 lg:mb-4 font-scarletwood">{{
$newsModel->title
}}</h3>
<div>{!! Str::limit(htmlentities(strip_tags($newsModel->content)), 250) !!}</div>
<a title="{{ __('Learn more') }}" class="button brown inline-flex items-center px-4 my-2 mt-6 mr-4 text-sm text-white" href="{{ route('news.show', ['publishedNews' => $newsModel->slug]) }}">
<span>{{ __('Learn more') }}</span>
</a>
</div>
</div>

View file

@ -1,24 +0,0 @@
<!-- Header -->
<div class="relative">
<img class="w-full" src="{{ Vite::asset('resources/assets/images/Replace_me.png') }}">
<div class="mx-auto max-w-7xl h-full w-full absolute top-0 left-0 right-0">
<div class="relative h-full w-full">
<svg class="absolute right-4 -bottom-3 sm:-bottom-4 md:-bottom-5 lg:-bottom-6 h-[40px] sm:h-[50px] md:h-[60px] text-weinrot" viewBox="0 0 313 45" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_219_44709)">
@php
$color = '#b4bd00';
@endphp
<path d="M312.425 3.98605C311.085 6.46369 309.583 8.59519 308.89 10.7631C308.128 13.095 307.319 13.6416 300.85 14.7346C301.474 15.937 301.474 17.1394 302.814 18.2143C304.131 19.2527 306.811 20.1272 307.088 21.0745C306.51 21.8214 305.748 22.5501 305.355 23.2971C304.593 24.7181 304.339 26.1937 303.16 27.5783C302.837 27.9609 299.995 28.1613 297.847 28.5256C300.088 29.309 302.698 30.2017 305.956 31.3494C304.616 31.7684 302.837 32.315 300.85 32.9162C302.028 33.2259 303.415 33.572 305.078 34.0092C301.543 35.4485 297.477 36.9423 309.075 37.9625C305.702 38.3998 304.085 38.6002 302.283 38.837C304.57 39.4018 305.864 39.7297 307.157 40.0394C304.223 40.9503 302.028 42.1162 298.678 42.5717C285.464 44.3206 272.249 45.5594 256.746 44.7214C243.809 44.0291 230.109 44.5574 216.778 44.4299C209.015 44.357 201.322 44.0291 193.559 43.8287C192.52 43.7923 191.434 43.8287 190.371 43.8287C178.265 43.9562 166.136 44.0473 154.053 44.2477C149.34 44.3206 144.72 44.8125 140.007 44.8489C125.983 44.9582 111.959 44.9582 97.9358 44.9582C81.2554 44.9582 64.5981 45.0311 47.9639 44.8854C41.726 44.8307 35.5344 44.3024 29.4352 43.8469C28.049 43.7376 27.0556 43.0453 26.2008 42.5899C25.138 41.9705 24.3063 41.2964 23.0587 40.4402C20.425 41.4422 18.5305 42.1709 16.6823 42.8632C12.6624 40.5495 9.12758 38.5091 5.5235 36.4504C3.97559 35.576 4.738 34.574 1.66529 33.6813C-1.82328 32.6611 0.972194 31.4587 6.2859 30.8211C4.55317 29.6734 2.63562 28.6167 1.43426 27.5054C0.71806 26.8678 0.879782 26.1209 1.0415 25.4286C1.68839 22.6595 2.47389 19.8721 3.2594 17.103C3.32871 16.8479 3.65215 16.6293 3.76767 16.3743C4.32214 15.2812 12.593 14.3885 4.738 13.095C9.54344 11.4918 1.27253 11.2186 1.45736 10.3623C4.32214 8.57697 7.55657 7.01023 9.38172 5.29774C10.6986 4.05892 12.8703 3.74921 16.8209 3.67634C40.6864 3.25733 64.5288 2.63792 88.4174 2.38287C119.306 2.07316 150.241 1.76346 181.153 1.83633C217.564 1.92742 253.974 2.4193 290.361 2.8201C296.669 2.89297 302.929 3.29376 309.19 3.58525C310.6 3.65812 311.94 3.91318 312.425 3.96783" fill="{{ $color }}" />
</g>
<defs>
<clipPath id="clip0_219_44709">
<rect width="312.305" height="44.0104" fill="white" transform="translate(0.695312 0.984375)" />
</clipPath>
</defs>
</svg>
<div class="absolute right-4 -bottom-1 sm:-bottom-2 lg:-bottom-3 text-white w-[280px] sm:w-[340px] md:w-[420px] flex justify-center font-scarletwood text-p sm:text-p-lg md:text-2xl">
{{ __('News') }}
</div>
</div>
</div>
</div>

View file

@ -1,29 +0,0 @@
@extends('fo.layout-frame')
{{-- FIXME: Add titles here --}}
@section('title', __('News'))
@section('description', __('News'))
@section('content')
@include('fo.pages.news.header')
<!-- Breadcrumb -->
<div x-show="false" class="container mx-auto max-w-7xl">
<ul class="px-4 sm:px-8 py-4 md:py-8 text-braun text-small md:text-size-17">
<li class="inline-block pr-1 after:content-['/'] after:pl-1"><a href="{{ route('home') }}" class="hover:underline">{{ __('models.classes.homes') }}</a></li>
<li class="inline-block font-bold font-robotobold">{{ __('models.classes.news') }}</li>
</ul>
</div>
<div class="container mx-auto max-w-7xl px-4 sm:px-8 mt-16 mb-8">
<h1 class="text-h1-md lg:text-h1-lg xl:text-h1 font-scarletwood text-braun mb-6">
{{ __('Lorem ipsum dolor sit') }}
<span class="block text-h2-md lg:text-h2-lg xl:text-h2">{{
__('Lorem ipsum dolor sit amet, consetetur')
}}</span>
</h1>
</div>
{{-- NEWS VUE JS --}}
<div id="news-infinite-loader" class="my-8"></div>
@endsection

View file

@ -1,90 +0,0 @@
@extends('fo.layout-frame')
@section('title', $newsModel->seo_title)
@section('description', $newsModel->seo_description)
@section('content')
@include('fo.pages.news.header')
<article>
<!-- Breadcrumb -->
<div class="container mx-auto max-w-7xl">
<ul class="px-4 sm:px-8 py-4 md:py-8 text-braun text-small md:text-size-17">
<li class="inline-block pr-1 after:content-['/'] after:pl-1"><a href="{{ route('home') }}" class="hover:underline">{{ __('models.classes.homes') }}</a></li>
<li class="inline-block pr-1 after:content-['/'] after:pl-1"><a href="{{ route('news.index') }}" class="hover:underline">{{ __('models.classes.news') }}</a></li>
<li class="inline-block font-bold font-robotobold">{{ $newsModel->title }}</li>
</ul>
</div>
<div class="container mx-auto max-w-7xl px-4 sm:px-8 mt-16 mb-8">
<h1 class="text-h1-md lg:text-h1-lg xl:text-h1 font-scarletwood text-braun mb-6">
{{ $newsModel->title }}
<span class="block text-h2-md lg:text-h2-lg xl:text-h2">{{
$newsModel->subtitle
}}</span>
</h1>
</div>
@php $pictureList = $newsModel->picture_list; @endphp
@php $hasOnePicture = $pictureList->count() === 1; @endphp
@php $hasTwoPictures = $pictureList->count() === 2; @endphp
@php $hasMorePictures = $pictureList->count() > 2; @endphp
<div class="w-full bg-hellgruen text-weiss py-16 mb-24">
<div class="container max-w-7xl mx-auto px-4 sm:px-8">
<div class="w-full mb-12 ck-content scarlet-titles">
@if($hasOnePicture)
@php $picture = collect($pictureList)->first(); @endphp
<img class="m-3 float-right" src="{{ asset(resize($picture->value, 'foNewsShowPicture')) }}" alt="{{ $picture->alt }}" title="{{ $picture->title }}">
@endif
{!! clean($newsModel->content) !!}
</div>
@if($hasTwoPictures)
<div class="w-full flex flex-col sm:flex-row gap-2 mt-12">
<div class="flex flex-wrap basis-full sm:basis-1/2">
<img class="" src="{{ asset(resize($pictureList->get(0)->value, 'foNewsShowPicture')) }}" alt="{{ $pictureList->get(0)->alt }}" title="{{ $pictureList->get(0)->title }}">
</div>
<div class="flex flex-wrap basis-full sm:basis-1/2">
<img class="" src="{{ asset(resize($pictureList->get(1)->value, 'foNewsShowPicture')) }}" alt="{{ $pictureList->get(1)->alt }}" title="{{ $pictureList->get(1)->title }}">
</div>
</div>
@endif
@if($hasMorePictures)
<section class="splide">
<div class="splide__track">
<ul class="splide__list">
@foreach ($pictureList as $picture)
<li class="splide__slide">
<img class="object-contain p-10" src="{{ asset(resize($picture->value, 'foNewsShowPicture')) }}" alt="{{ $picture->alt }}" title="{{ $picture->title }}">
</li>
@endforeach
</ul>
</div>
</section>
@push('scripts')
<script nonce="{{ $nonce }}">
document.addEventListener('DOMContentLoaded', function() {
new Splide('.splide', {
type: 'loop',
perPage: 2,
autoplay: 'play',
pagination: false,
pauseOnHover: true,
autoWidth: false,
breakpoints: {
640: {
perPage: 1
}
}
}).mount();
});
</script>
@endpush
@endif
</div>
</div>
</article>
@include('fo.pages.news.aside-news', ['relatedNewsList' => $relatedNewsList])
@endsection

View file

@ -1,6 +1,6 @@
@include('modules.script-constants')
<script @if(isset($nonce)) nonce="{{ $nonce }}" @endif>
if (!window.__SYSTEM._routes) {
window.__SYSTEM._routes = {};
}
window.__SYSTEM._routes['news.index'] = '{{ route('news.index') }}';
</script>

View file

@ -1,26 +1,14 @@
@php $nonce = $nonce ?? uniqid(); @endphp
@extends('fo.layout-minimal')
@extends('fo.layout-frame')
@section('title', __('Not Found'))
@section('code', '404')
@section('message', __('Not Found'))
@section('submessage', __('We could not find the page'))
@section('image')
<img src="{{ Vite::asset('resources/assets/images/Icons/404-grafik.svg') }}" class="inline max-w-sm w-full px-4" alt="{{ config('app.name') }}">
@endsection
@section('description', __('Not Found'))
@section('content')
<div class="mx-auto max-w-7xl py-6 px-4 sm:px-8 mb-24 mt-32 text-center pt-16 pb-16">
@yield('image')
<h1 class="font-scarletwood text-hellgruen text-[100px] leading-[100px] mt-8">@yield('code', __('Oh no'))</h1>
<p class="font-scarletwood text-hellgruen">@yield('message')</p>
<p class="font-scarletwood text-hellgruen">@yield('submessage')</p>
<a href="{{ app('router')->has('home') ? route('home') : url('/') }}" class="button brown inline-flex items-center px-4 my-2 mt-12 mr-6 text-sm text-white">
<span>{{ __('Go to page :page', [
'page' => __('home')
]) }}</span>
<img class="inline max-w-sm w-full px-4" src="{{ Vite::asset('resources/assets/images/Icons/404-grafik.svg') }}">
<h1 class="font-scarletwood text-hellgruen text-[100px] leading-[100px] mt-8">404</h1>
<p class="font-scarletwood text-hellgruen">{{ __('We could not find the page') }}</p>
<a href="/" class="button brown inline-flex items-center px-4 my-2 mt-12 mr-6 text-sm text-white">
<span>{{ __('Back to home page') }}</span>
</a>
</div>
@endsection

View file

@ -3,7 +3,6 @@
use App\Http\Controllers\Bo\ConceptController;
use App\Http\Controllers\Bo\DashboardController;
use App\Http\Controllers\Bo\HomeController;
use App\Http\Controllers\Bo\NewsController;
use App\Http\Controllers\Bo\RangeController;
use App\Http\Controllers\Bo\ProductController;
use App\Http\Controllers\Bo\RecipeController;
@ -55,13 +54,6 @@ Route::prefix('bo')
->withoutMiddleware('htmlsanitize')
->middleware(['htmlsanitize:part1_text_1,part1_text_2,part1_text_3,part2_text']);
// * NEWS
Route::resource('news', NewsController::class)->except(['show'])
->withoutMiddleware('htmlsanitize')
->middleware(['htmlsanitize:content']);
Route::post('/news/{news}/change-order', [NewsController::class, 'changeOrder'])
->name('news.change-order');
// * CONCEPTS
Route::resource('concepts', ConceptController::class)->except(['show'])
->withoutMiddleware('htmlsanitize')

View file

@ -3,7 +3,6 @@
use App\Http\Controllers\Fo\ConceptController;
use App\Http\Controllers\Fo\ContactController;
use App\Http\Controllers\Fo\HomeController;
use App\Http\Controllers\Fo\NewsController;
use App\Http\Controllers\Fo\ProductController;
use App\Http\Controllers\Fo\RecipeController;
use App\Http\Controllers\Fo\StoreController;
@ -31,13 +30,6 @@ Route::namespace('\App\Http\Controllers\Fo')->middleware(['throttle:credentials'
[ProductController::class, 'show']
)->name('product.show');
// * NEWS
Route::get($trans->uri('news'), [NewsController::class, 'index'])->name('news.index');
Route::get(
$trans->uri('news/{publishedNews:slug}'),
[NewsController::class, 'show']
)->name('news.show');
Route::get($trans->uri('recipes'), [RecipeController::class, 'index'])->name('recipes.index');
Route::get(