273 lines
14 KiB
PHP
273 lines
14 KiB
PHP
@extends('layouts.shop')
|
||
|
||
@php
|
||
$translatedCategoryName = __($category->name);
|
||
$translatedCategoryDescription = $category->description ? __($category->description) : __('Категория товаров :category', ['category' => $translatedCategoryName]);
|
||
$hasSeoFilters = request()->filled('q')
|
||
|| request()->filled('sort')
|
||
|| request()->filled('page')
|
||
|| request()->filled('price_from')
|
||
|| request()->filled('price_to')
|
||
|| collect((array) request('filters', []))
|
||
->contains(fn ($value) => is_scalar($value) && trim((string) $value) !== '')
|
||
|| collect(request()->query())
|
||
->keys()
|
||
->contains(fn ($key) => is_string($key) && (str_ends_with($key, '_from') || str_ends_with($key, '_to')));
|
||
|
||
$categoryItemList = collect($products->items())
|
||
->values()
|
||
->map(fn ($product, $index) => [
|
||
'@type' => 'ListItem',
|
||
'position' => $index + 1,
|
||
'url' => route('products.show', $product),
|
||
'name' => $product->name,
|
||
])
|
||
->all();
|
||
$categorySchema = [
|
||
'@context' => 'https://schema.org',
|
||
'@type' => 'CollectionPage',
|
||
'name' => $translatedCategoryName,
|
||
'url' => route('catalog.category', $category),
|
||
'description' => $translatedCategoryDescription,
|
||
'mainEntity' => [
|
||
'@type' => 'ItemList',
|
||
'numberOfItems' => $products->total(),
|
||
'itemListElement' => $categoryItemList,
|
||
],
|
||
];
|
||
@endphp
|
||
|
||
@section('meta_title', $translatedCategoryName)
|
||
@section('meta_description', __('Товары категории :category. Фильтры и сортировка для быстрого подбора.', ['category' => $translatedCategoryName]))
|
||
@section('meta_keywords', __(':category, комплектующие, купить, фильтры товаров', ['category' => $translatedCategoryName]))
|
||
@section('meta_canonical', route('catalog.category', $category))
|
||
@section('meta_robots', $hasSeoFilters ? 'noindex,follow' : 'index,follow')
|
||
|
||
@push('structured_data')
|
||
<script type="application/ld+json">
|
||
@json($categorySchema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
|
||
</script>
|
||
@endpush
|
||
|
||
@section('content')
|
||
@include('partials.breadcrumbs', [
|
||
'items' => [
|
||
['label' => __('Главная'), 'url' => route('home')],
|
||
['label' => __('Каталог'), 'url' => route('catalog.index')],
|
||
['label' => $translatedCategoryName, 'url' => null],
|
||
],
|
||
])
|
||
|
||
<section class="pc-section pc-category-page">
|
||
<div class="pc-section-title">
|
||
<h2>{{ $translatedCategoryName }}</h2>
|
||
</div>
|
||
|
||
@php
|
||
$activeFilters = collect((array) ($appliedFilters ?? []))
|
||
->filter(fn ($value) => is_scalar($value) && trim((string) $value) !== '')
|
||
->values();
|
||
|
||
if (request()->filled('price_from') || request()->filled('price_to')) {
|
||
$priceFromLabel = trim((string) request('price_from', ''));
|
||
$priceToLabel = trim((string) request('price_to', ''));
|
||
$activeFilters->push(__('Цена: :from - :to', ['from' => $priceFromLabel, 'to' => $priceToLabel]));
|
||
}
|
||
|
||
foreach ((array) ($filters ?? []) as $filter) {
|
||
if ((string) ($filter['filter'] ?? 'select') !== 'range') {
|
||
continue;
|
||
}
|
||
|
||
$rangeKey = (string) ($filter['key'] ?? '');
|
||
if ($rangeKey === '') {
|
||
continue;
|
||
}
|
||
|
||
$fromParam = $rangeKey . '_from';
|
||
$toParam = $rangeKey . '_to';
|
||
if (!request()->filled($fromParam) && !request()->filled($toParam)) {
|
||
continue;
|
||
}
|
||
|
||
$fromLabel = trim((string) request($fromParam, ''));
|
||
$toLabel = trim((string) request($toParam, ''));
|
||
$activeFilters->push(__(':label: :from - :to', [
|
||
'label' => __($filter['label'] ?? $rangeKey),
|
||
'from' => $fromLabel,
|
||
'to' => $toLabel,
|
||
]));
|
||
}
|
||
@endphp
|
||
|
||
<div class="pc-category-toolbar">
|
||
<p class="pc-muted">{{ __('Найдено товаров:') }} <strong>{{ $products->total() }}</strong></p>
|
||
<div class="pc-category-toolbar-controls">
|
||
<form class="pc-sort-form" method="get">
|
||
@foreach ((array) ($appliedFilters ?? []) as $key => $value)
|
||
@if (is_scalar($value) && trim((string) $value) !== '')
|
||
<input type="hidden" name="filters[{{ $key }}]" value="{{ $value }}">
|
||
@endif
|
||
@endforeach
|
||
@if (request()->filled('price_from'))
|
||
<input type="hidden" name="price_from" value="{{ request('price_from') }}">
|
||
@endif
|
||
@if (request()->filled('price_to'))
|
||
<input type="hidden" name="price_to" value="{{ request('price_to') }}">
|
||
@endif
|
||
@foreach ((array) ($filters ?? []) as $filter)
|
||
@php
|
||
$rangeKey = (string) ($filter['key'] ?? '');
|
||
@endphp
|
||
@continue($rangeKey === '' || (string) ($filter['filter'] ?? 'select') !== 'range')
|
||
@if (request()->filled($rangeKey . '_from'))
|
||
<input type="hidden" name="{{ $rangeKey }}_from" value="{{ request($rangeKey . '_from') }}">
|
||
@endif
|
||
@if (request()->filled($rangeKey . '_to'))
|
||
<input type="hidden" name="{{ $rangeKey }}_to" value="{{ request($rangeKey . '_to') }}">
|
||
@endif
|
||
@endforeach
|
||
@if (request()->filled('q'))
|
||
<input type="hidden" name="q" value="{{ request('q') }}">
|
||
@endif
|
||
<label for="sort">{{ __('Сортировка:') }}</label>
|
||
<select id="sort" name="sort" onchange="this.form.submit()">
|
||
<option value="newest" @selected($sort === 'newest')>{{ __('Сначала новые') }}</option>
|
||
<option value="price_asc" @selected($sort === 'price_asc')>{{ __('Сначала дешевле') }}</option>
|
||
<option value="price_desc" @selected($sort === 'price_desc')>{{ __('Сначала дороже') }}</option>
|
||
<option value="name_asc" @selected($sort === 'name_asc')>{{ __('По названию') }}</option>
|
||
</select>
|
||
</form>
|
||
<button
|
||
class="pc-filter-inline-toggle"
|
||
type="button"
|
||
data-filter-toggle
|
||
aria-controls="pc-category-filters"
|
||
aria-expanded="{{ $activeFilters->isNotEmpty() ? 'true' : 'false' }}"
|
||
>
|
||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||
<path d="M4 6h16v2H4V6zm3 5h10v2H7v-2zm3 5h4v2h-4v-2z"></path>
|
||
</svg>
|
||
<span>{{ __('Фильтр') }}</span>
|
||
@if ($activeFilters->isNotEmpty())
|
||
<span class="pc-filter-toggle-count">{{ $activeFilters->count() }}</span>
|
||
@endif
|
||
</button>
|
||
</div>
|
||
</div>
|
||
@if ($activeFilters->isNotEmpty())
|
||
<div class="pc-filter-tags">
|
||
@foreach ($activeFilters as $key => $value)
|
||
<span class="pc-filter-chip">{{ $value }}</span>
|
||
@endforeach
|
||
</div>
|
||
@endif
|
||
|
||
<div class="pc-catalog-layout">
|
||
<aside class="pc-filters">
|
||
<div id="pc-category-filters" class="pc-filter-details {{ $activeFilters->isNotEmpty() ? 'is-open' : '' }}">
|
||
<form method="get">
|
||
<div class="pc-filter-title">{{ __('Фильтры') }}</div>
|
||
@if ($sort !== 'newest')
|
||
<input type="hidden" name="sort" value="{{ $sort }}">
|
||
@endif
|
||
@if (request()->filled('q'))
|
||
<input type="hidden" name="q" value="{{ request('q') }}">
|
||
@endif
|
||
<label class="pc-filter-block">
|
||
<span>{{ __('Цена') }}</span>
|
||
<div class="pc-range-fields">
|
||
<input
|
||
type="number"
|
||
name="price_from"
|
||
step="1"
|
||
value="{{ $priceFilter['from'] ?? '' }}"
|
||
@if (!empty($priceFilter['min'])) min="{{ $priceFilter['min'] }}" @endif
|
||
@if (!empty($priceFilter['max'])) max="{{ $priceFilter['max'] }}" @endif
|
||
placeholder="{{ __('От') }}"
|
||
>
|
||
<input
|
||
type="number"
|
||
name="price_to"
|
||
step="1"
|
||
value="{{ $priceFilter['to'] ?? '' }}"
|
||
@if (!empty($priceFilter['min'])) min="{{ $priceFilter['min'] }}" @endif
|
||
@if (!empty($priceFilter['max'])) max="{{ $priceFilter['max'] }}" @endif
|
||
placeholder="{{ __('До') }}"
|
||
>
|
||
</div>
|
||
</label>
|
||
@forelse ($filters as $filter)
|
||
@php
|
||
$filterKey = (string) ($filter['key'] ?? '');
|
||
$isRangeFilter = (string) ($filter['filter'] ?? 'select') === 'range';
|
||
$rangeData = (array) ($rangeFilters[$filterKey] ?? []);
|
||
@endphp
|
||
@continue($filterKey === '')
|
||
@if ($isRangeFilter)
|
||
<label class="pc-filter-block">
|
||
<span>{{ __($filter['label']) }}</span>
|
||
<div class="pc-range-fields">
|
||
<input
|
||
type="number"
|
||
name="{{ $filterKey }}_from"
|
||
step="1"
|
||
value="{{ $rangeData['from'] ?? '' }}"
|
||
@if (!empty($rangeData['min'])) min="{{ $rangeData['min'] }}" @endif
|
||
@if (!empty($rangeData['max'])) max="{{ $rangeData['max'] }}" @endif
|
||
placeholder="{{ __('От') }}"
|
||
>
|
||
<input
|
||
type="number"
|
||
name="{{ $filterKey }}_to"
|
||
step="1"
|
||
value="{{ $rangeData['to'] ?? '' }}"
|
||
@if (!empty($rangeData['min'])) min="{{ $rangeData['min'] }}" @endif
|
||
@if (!empty($rangeData['max'])) max="{{ $rangeData['max'] }}" @endif
|
||
placeholder="{{ __('До') }}"
|
||
>
|
||
</div>
|
||
</label>
|
||
@else
|
||
<label class="pc-filter-block">
|
||
<span>{{ __($filter['label']) }}</span>
|
||
<select name="filters[{{ $filterKey }}]">
|
||
<option value="">{{ __('Все') }}</option>
|
||
@foreach ($filterOptions[$filterKey] ?? [] as $option)
|
||
@php
|
||
$optionValue = trim((string) $option);
|
||
@endphp
|
||
@continue($optionValue === '')
|
||
<option value="{{ $optionValue }}" @selected(($appliedFilters[$filterKey] ?? '') === $optionValue)>
|
||
{{ __($optionValue) }}
|
||
</option>
|
||
@endforeach
|
||
</select>
|
||
</label>
|
||
@endif
|
||
@empty
|
||
<p class="pc-muted">{{ __('Для этой категории фильтры пока не заданы.') }}</p>
|
||
@endforelse
|
||
<div class="pc-filter-actions">
|
||
<button type="submit" class="pc-btn primary">{{ __('Показать') }}</button>
|
||
<a class="pc-btn ghost" href="{{ route('catalog.category', $category) }}">{{ __('Сбросить') }}</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</aside>
|
||
|
||
<div class="pc-products-grid">
|
||
@forelse ($products as $product)
|
||
@include('partials.product-card', ['product' => $product])
|
||
@empty
|
||
<div class="pc-card">{{ __('Пока нет товаров в этой категории.') }}</div>
|
||
@endforelse
|
||
</div>
|
||
</div>
|
||
|
||
<div class="pc-pagination">
|
||
{{ $products->links('partials.pagination') }}
|
||
</div>
|
||
</section>
|
||
@endsection
|