+ @if(!is_null($priceValue) && $priceValue > 0) + {{ number_format($priceValue, 0, ',', '.') }} {{ $listing->currency }} + @else + Ücretsiz + @endif +
++ {{ $listing->title }} +
+ + ++ {{ $listing->category?->name ?: 'Kategori yok' }} +
+ +diff --git a/.env.example b/.env.example index 5f8ec325a..b44d2eec0 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,9 @@ APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost:8000 +OC_THEME=otoplus +OC_THEME_LISTING=otoplus +OC_THEME_CATEGORY=otoplus APP_LOCALE=en APP_FALLBACK_LOCALE=en diff --git a/Modules/Category/Http/Controllers/CategoryController.php b/Modules/Category/Http/Controllers/CategoryController.php index a4ded2e7e..7f016191b 100644 --- a/Modules/Category/Http/Controllers/CategoryController.php +++ b/Modules/Category/Http/Controllers/CategoryController.php @@ -1,21 +1,34 @@ with('children')->where('is_active', true)->get(); - return view('category::index', compact('categories')); + $categories = Category::rootTreeWithActiveChildren(); + + return view($this->themes->view('category', 'index'), compact('categories')); } public function show(Category $category) { - $listings = $category->listings()->where('status', 'active')->paginate(12); - return view('category::show', compact('category', 'listings')); + $category->loadMissing([ + 'children' => fn ($query) => $query->active()->ordered(), + ]); + + $listings = $category->activeListings() + ->with('category:id,name') + ->latest('id') + ->paginate(12); + + return view($this->themes->view('category', 'show'), compact('category', 'listings')); } } diff --git a/Modules/Category/Models/Category.php b/Modules/Category/Models/Category.php index 0ef96af8c..6b2b2870f 100644 --- a/Modules/Category/Models/Category.php +++ b/Modules/Category/Models/Category.php @@ -1,9 +1,11 @@ hasMany(\Modules\Listing\Models\Listing::class); } + public function scopeActive(Builder $query): Builder + { + return $query->where('is_active', true); + } + + public function scopeOrdered(Builder $query): Builder + { + return $query->orderBy('sort_order')->orderBy('name'); + } + + public static function filterOptions(): Collection + { + return static::query() + ->active() + ->ordered() + ->get(['id', 'name']); + } + + public static function themePills(int $limit = 8): Collection + { + return static::query() + ->active() + ->ordered() + ->limit($limit) + ->get(['id', 'name', 'slug']); + } + + public static function rootTreeWithActiveChildren(): Collection + { + return static::query() + ->active() + ->whereNull('parent_id') + ->with([ + 'children' => fn (Builder $query) => $query->active()->ordered(), + ]) + ->ordered() + ->get(); + } + + public function breadcrumbTrail(): Collection + { + $trail = collect(); + $current = $this; + + while ($current) { + $trail->prepend($current); + $current = $current->parent; + } + + return $trail; + } + public function listingCustomFields(): HasMany { return $this->hasMany(\Modules\Listing\Models\ListingCustomField::class); } + + public function activeListings(): HasMany + { + return $this->hasMany(\Modules\Listing\Models\Listing::class)->where('status', 'active'); + } } diff --git a/Modules/Category/resources/views/themes/README.md b/Modules/Category/resources/views/themes/README.md new file mode 100644 index 000000000..835cb8a57 --- /dev/null +++ b/Modules/Category/resources/views/themes/README.md @@ -0,0 +1,10 @@ +# Category Theme Contract + +Active category template is resolved from `config('theme.modules.category')`. + +Create: + +- `themes/{theme}/index.blade.php` +- `themes/{theme}/show.blade.php` + +Then set `OC_THEME_CATEGORY={theme}`. diff --git a/Modules/Category/resources/views/themes/default/index.blade.php b/Modules/Category/resources/views/themes/default/index.blade.php new file mode 100644 index 000000000..e7c719c45 --- /dev/null +++ b/Modules/Category/resources/views/themes/default/index.blade.php @@ -0,0 +1,15 @@ +@extends('app::layouts.app') +@section('content') +
{{ $category->children->count() }} subcategories
+ + @endforeach +{{ $category->description }}
@endif +{{ $listing->price ? number_format($listing->price, 0).' '.$listing->currency : 'Free' }}
+ View → +No listings in this category yet.
+ @endforelse +{{ $category->children->count() }} subcategories
+ + @endforeach +{{ $category->description }}
@endif +{{ $listing->price ? number_format($listing->price, 0).' '.$listing->currency : 'Free' }}
+ View → +No listings in this category yet.
+ @endforelse +- @if($listing->price) {{ number_format($listing->price, 0) }} {{ $listing->currency }} @else Free @endif -
-{{ $listing->category?->name ?: 'Kategori yok' }}
-{{ $listing->city }}, {{ $listing->country }}
-+ {{ $activeCategoryName !== '' ? 'İkinci El '.$activeCategoryName.' kategorisinde' : 'İkinci El Araba kategorisinde' }} + {{ number_format($totalListings, 0, ',', '.') }} + ilan bulundu +
++ @if(!is_null($priceValue) && $priceValue > 0) + {{ number_format($priceValue, 0, ',', '.') }} {{ $listing->currency }} + @else + Ücretsiz + @endif +
++ {{ $listing->category?->name ?: 'Kategori yok' }} +
+ ++ {{ $activeCategoryName !== '' ? 'İkinci El '.$activeCategoryName.' kategorisinde' : 'İkinci El Araba kategorisinde' }} + {{ number_format($totalListings, 0, ',', '.') }} + ilan bulundu +
++ @if(!is_null($priceValue) && $priceValue > 0) + {{ number_format($priceValue, 0, ',', '.') }} {{ $listing->currency }} + @else + Ücretsiz + @endif +
++ {{ $listing->category?->name ?: 'Kategori yok' }} +
+ +{{ $location !== '' ? $location : 'Location not specified' }}
+Posted {{ $listing->created_at?->diffForHumans() ?? 'recently' }}
+{{ $displayDescription }}
+{{ $field['label'] }}
+{{ $field['value'] }}
+Name: {{ $listing->user->name }}
+ @endif + @if($listing->contact_phone) +Phone: {{ $listing->contact_phone }}
+ @endif + @if($listing->contact_email) +Email: {{ $listing->contact_email }}
+ @endif + @if(!$listing->contact_phone && !$listing->contact_email) +No contact details provided.
+ @endif ++ {{ $activeCategoryName !== '' ? 'İkinci El '.$activeCategoryName.' kategorisinde' : 'İkinci El Araba kategorisinde' }} + {{ number_format($totalListings, 0, ',', '.') }} + ilan bulundu +
++ @if(!is_null($priceValue) && $priceValue > 0) + {{ number_format($priceValue, 0, ',', '.') }} {{ $listing->currency }} + @else + Ücretsiz + @endif +
++ {{ $listing->category?->name ?: 'Kategori yok' }} +
+ +Kredi fırsatlarını hemen incele.
+
- İpucu: En az 1 fotoğraf, en çok {{ (int) config('quick-listing.max_photo_count', 20) }} fotoğraf yükleyebilirsin.
- Desteklenen formatlar: .jpg, .jpeg ve .png
-
- Hızlı ilan vermek için en az 1 fotoğraf yükleyin.
- Laravel AI sizin için otomatik kategori önerileri sunar.
-
{{ $this->selectedCategoryPath ?: '-' }}
-Ürünün temel özelliklerinden bahset (ör. marka, model, yaş, tip)
-Lütfen unutma; doğru fiyat daha hızlı satmanıza yardımcı olacaktır
- @error('price')Durum, özellik ve satma nedeni gibi bilgileri ekle
-{{ $this->selectedCategoryPath ?: '-' }}
-{{ $field['help_text'] }}
- @endif - - @error('customFieldValues.'.$field['name']) -
- İpucu: En az 1 fotoğraf, en çok {{ (int) config('quick-listing.max_photo_count', 20) }} fotoğraf yükleyebilirsin.
- Desteklenen formatlar: .jpg, .jpeg ve .png
-
- Hızlı ilan vermek için en az 1 fotoğraf yükleyin.
- Laravel AI sizin için otomatik kategori önerileri sunar.
-
{{ $this->selectedCategoryPath ?: '-' }}
-Ürünün temel özelliklerinden bahset (ör. marka, model, yaş, tip)
-Lütfen unutma; doğru fiyat daha hızlı satmanıza yardımcı olacaktır
- @error('price')Durum, özellik ve satma nedeni gibi bilgileri ekle
-{{ $this->selectedCategoryPath ?: '-' }}
-{{ $field['help_text'] }}
- @endif - - @error('customFieldValues.'.$field['name']) -
+ İpucu: En az 1 fotoğraf, en çok {{ (int) config('quick-listing.max_photo_count', 20) }} fotoğraf yükleyebilirsin.
+ Desteklenen formatlar: .jpg, .jpeg ve .png
+
+ Hızlı ilan vermek için en az 1 fotoğraf yükleyin.
+ Laravel AI sizin için otomatik kategori önerileri sunar.
+
{{ $this->selectedCategoryPath ?: '-' }}
+Ürünün temel özelliklerinden bahset (ör. marka, model, yaş, tip)
+Lütfen unutma; doğru fiyat daha hızlı satmanıza yardımcı olacaktır
+ @error('price')Durum, özellik ve satma nedeni gibi bilgileri ekle
+{{ $this->selectedCategoryPath ?: '-' }}
+{{ $field['help_text'] }}
+ @endif + + @error('customFieldValues.'.$field['name']) +