From 154b226a03e112dd51a82cc91983c99ee7392bfa Mon Sep 17 00:00:00 2001 From: fatihalp Date: Sat, 7 Mar 2026 02:30:09 +0300 Subject: [PATCH] Refactor Filament module UX --- .gemini/CUSTOM_INSTRUCTIONS.md | 2 +- AGENTS.md | 7 + .../Filament/Pages/ManageGeneralSettings.php | 78 ++- .../Filament/Resources/CategoryResource.php | 2 +- .../Admin/Filament/Resources/CityResource.php | 2 +- .../Filament/Resources/DistrictResource.php | 2 +- .../Resources/ListingCustomFieldResource.php | 2 +- .../Filament/Resources/ListingResource.php | 1 + .../Filament/Resources/LocationResource.php | 2 +- .../Admin/Filament/Resources/UserResource.php | 2 +- .../Http/Controllers/CategoryController.php | 19 - Modules/Category/Models/Category.php | 103 ++++ .../Category/resources/views/index.blade.php | 2 +- .../Category/resources/views/show.blade.php | 33 -- .../views/themes/default/index.blade.php | 2 +- .../views/themes/default/show.blade.php | 33 -- .../views/themes/otoplus/index.blade.php | 2 +- .../views/themes/otoplus/show.blade.php | 33 -- Modules/Category/routes/web.php | 1 - Modules/Conversation/routes/web.php | 2 +- .../Http/Controllers/ListingController.php | 30 +- Modules/Listing/Models/Listing.php | 23 +- .../Listing/resources/views/index.blade.php | 5 +- .../views/themes/default/index.blade.php | 5 +- .../views/themes/otoplus/index.blade.php | 5 +- .../views/themes/otoplus/show.blade.php | 2 +- .../Filament/Resources/ListingResource.php | 2 +- app/Livewire/PanelQuickListingForm.php | 42 +- resources/views/home.blade.php | 2 +- resources/views/layouts/app.blade.php | 52 +- resources/views/panel/create.blade.php | 2 +- .../views/panel/partials/sidebar.blade.php | 14 +- .../partials/quick-create/form.blade.php | 523 +++++++++++++++--- routes/web.php | 2 +- 34 files changed, 725 insertions(+), 314 deletions(-) create mode 100644 AGENTS.md delete mode 100644 Modules/Category/resources/views/show.blade.php delete mode 100644 Modules/Category/resources/views/themes/default/show.blade.php delete mode 100644 Modules/Category/resources/views/themes/otoplus/show.blade.php diff --git a/.gemini/CUSTOM_INSTRUCTIONS.md b/.gemini/CUSTOM_INSTRUCTIONS.md index 031b84595..a98c16fcd 100644 --- a/.gemini/CUSTOM_INSTRUCTIONS.md +++ b/.gemini/CUSTOM_INSTRUCTIONS.md @@ -4,4 +4,4 @@ Act as a Senior Laravel & FilamentPHP Architect. Refactor the attached code as a 3. Refactoring: Move all database logic into Models and extract repetitive Filament code into dedicated Helper classes. Identify and fix any existing logical errors. 4. Database: Consolidate migrations into a single file per table or topic (e.g., users, cache, jobs) to reduce the overall number of migration files. 5. Modularity: Use the `laravel-modules` package to encapsulate all features, routing, and Filament resources strictly inside their respective modules. -6. Frontend: Optimize and reduce the CSS footprint while maintaining the exact same visual output. +6. Frontend: Optimize and reduce the CSS footprint while maintaining the exact same visual output. \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..031b84595 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,7 @@ +Act as a Senior Laravel & FilamentPHP Architect. Refactor the attached code as a greenfield project adhering to the following strict constraints: +1. Architecture: Enforce strict SOLID principles, prioritize brevity, and completely ignore backward compatibility. +2. Cleanup: Remove all legacy code, comments, tests, and PHPDocs. +3. Refactoring: Move all database logic into Models and extract repetitive Filament code into dedicated Helper classes. Identify and fix any existing logical errors. +4. Database: Consolidate migrations into a single file per table or topic (e.g., users, cache, jobs) to reduce the overall number of migration files. +5. Modularity: Use the `laravel-modules` package to encapsulate all features, routing, and Filament resources strictly inside their respective modules. +6. Frontend: Optimize and reduce the CSS footprint while maintaining the exact same visual output. diff --git a/Modules/Admin/Filament/Pages/ManageGeneralSettings.php b/Modules/Admin/Filament/Pages/ManageGeneralSettings.php index eeb5ee084..0c10a91ad 100644 --- a/Modules/Admin/Filament/Pages/ManageGeneralSettings.php +++ b/Modules/Admin/Filament/Pages/ManageGeneralSettings.php @@ -33,16 +33,51 @@ class ManageGeneralSettings extends SettingsPage protected static ?int $navigationSort = 1; + protected function mutateFormDataBeforeFill(array $data): array + { + $defaults = $this->defaultFormData(); + + return [ + 'site_name' => filled($data['site_name'] ?? null) ? $data['site_name'] : $defaults['site_name'], + 'site_description' => filled($data['site_description'] ?? null) ? $data['site_description'] : $defaults['site_description'], + 'home_slides' => $this->normalizeHomeSlides($data['home_slides'] ?? $defaults['home_slides']), + 'site_logo' => $data['site_logo'] ?? null, + 'sender_name' => filled($data['sender_name'] ?? null) ? $data['sender_name'] : $defaults['sender_name'], + 'sender_email' => filled($data['sender_email'] ?? null) ? $data['sender_email'] : $defaults['sender_email'], + 'default_language' => filled($data['default_language'] ?? null) ? $data['default_language'] : $defaults['default_language'], + 'default_country_code' => filled($data['default_country_code'] ?? null) ? $data['default_country_code'] : $defaults['default_country_code'], + 'currencies' => $this->normalizeCurrencies($data['currencies'] ?? $defaults['currencies']), + 'linkedin_url' => filled($data['linkedin_url'] ?? null) ? $data['linkedin_url'] : $defaults['linkedin_url'], + 'instagram_url' => filled($data['instagram_url'] ?? null) ? $data['instagram_url'] : $defaults['instagram_url'], + 'whatsapp' => filled($data['whatsapp'] ?? null) ? $data['whatsapp'] : $defaults['whatsapp'], + 'enable_google_maps' => (bool) ($data['enable_google_maps'] ?? $defaults['enable_google_maps']), + 'google_maps_api_key' => $data['google_maps_api_key'] ?? null, + 'enable_google_login' => (bool) ($data['enable_google_login'] ?? $defaults['enable_google_login']), + 'google_client_id' => $data['google_client_id'] ?? null, + 'google_client_secret' => $data['google_client_secret'] ?? null, + 'enable_facebook_login' => (bool) ($data['enable_facebook_login'] ?? $defaults['enable_facebook_login']), + 'facebook_client_id' => $data['facebook_client_id'] ?? null, + 'facebook_client_secret' => $data['facebook_client_secret'] ?? null, + 'enable_apple_login' => (bool) ($data['enable_apple_login'] ?? $defaults['enable_apple_login']), + 'apple_client_id' => $data['apple_client_id'] ?? null, + 'apple_client_secret' => $data['apple_client_secret'] ?? null, + ]; + } + public function form(Schema $schema): Schema { + $defaults = $this->defaultFormData(); + return $schema ->components([ TextInput::make('site_name') ->label('Site Adı') + ->default($defaults['site_name']) ->required() ->maxLength(255), Textarea::make('site_description') ->label('Site Açıklaması') + ->default($defaults['site_description']) ->rows(3) ->maxLength(500), Repeater::make('home_slides') @@ -70,7 +105,7 @@ class ManageGeneralSettings extends SettingsPage ->required() ->maxLength(120), ]) - ->default($this->defaultHomeSlides()) + ->default($defaults['home_slides']) ->minItems(1) ->collapsible() ->reorderableWithButtons() @@ -86,26 +121,30 @@ class ManageGeneralSettings extends SettingsPage ->visibility('public'), TextInput::make('sender_name') ->label('Gönderici Adı') + ->default($defaults['sender_name']) ->required() ->maxLength(120), TextInput::make('sender_email') ->label('Gönderici E-postası') ->email() + ->default($defaults['sender_email']) ->required() ->maxLength(255), Select::make('default_language') ->label('Varsayılan Dil') ->options($this->localeOptions()) + ->default($defaults['default_language']) ->required() ->searchable(), CountryCodeSelect::make('default_country_code') ->label('Varsayılan Ülke') - ->default('+90') + ->default($defaults['default_country_code']) ->required() ->helperText('Panel formlarında varsayılan ülke olarak kullanılır.'), TagsInput::make('currencies') ->label('Para Birimleri') ->placeholder('TRY') + ->default($defaults['currencies']) ->helperText('TRY, USD, EUR gibi 3 harfli para birimi kodları ekleyin.') ->required() ->rules(['array', 'min:1']) @@ -114,22 +153,25 @@ class ManageGeneralSettings extends SettingsPage TextInput::make('linkedin_url') ->label('LinkedIn URL') ->url() + ->default($defaults['linkedin_url']) ->nullable() ->maxLength(255), TextInput::make('instagram_url') ->label('Instagram URL') ->url() + ->default($defaults['instagram_url']) ->nullable() ->maxLength(255), PhoneInput::make('whatsapp') ->label('WhatsApp') ->defaultCountry(CountryCodeManager::defaultCountryIso2()) + ->default($defaults['whatsapp']) ->nullable() ->formatAsYouType() ->helperText('Uluslararası format kullanın. Örnek: +905551112233'), Toggle::make('enable_google_maps') ->label('Google Maps Aktif') - ->default(false), + ->default($defaults['enable_google_maps']), TextInput::make('google_maps_api_key') ->label('Google Maps API Anahtarı') ->password() @@ -139,7 +181,7 @@ class ManageGeneralSettings extends SettingsPage ->helperText('İlan formlarındaki harita alanlarını açmak için gereklidir.'), Toggle::make('enable_google_login') ->label('Google ile Giriş Aktif') - ->default(false), + ->default($defaults['enable_google_login']), TextInput::make('google_client_id') ->label('Google Client ID') ->nullable() @@ -152,7 +194,7 @@ class ManageGeneralSettings extends SettingsPage ->maxLength(255), Toggle::make('enable_facebook_login') ->label('Facebook ile Giriş Aktif') - ->default(false), + ->default($defaults['enable_facebook_login']), TextInput::make('facebook_client_id') ->label('Facebook Client ID') ->nullable() @@ -165,7 +207,7 @@ class ManageGeneralSettings extends SettingsPage ->maxLength(255), Toggle::make('enable_apple_login') ->label('Apple ile Giriş Aktif') - ->default(false), + ->default($defaults['enable_apple_login']), TextInput::make('apple_client_id') ->label('Apple Client ID') ->nullable() @@ -179,6 +221,30 @@ class ManageGeneralSettings extends SettingsPage ]); } + private function defaultFormData(): array + { + $siteName = (string) config('app.name', 'OpenClassify'); + $siteHost = parse_url((string) config('app.url', 'https://oc2.test'), PHP_URL_HOST) ?: 'oc2.test'; + + return [ + 'site_name' => $siteName, + 'site_description' => 'Alim satim icin hizli ve guvenli ilan platformu.', + 'home_slides' => $this->defaultHomeSlides(), + 'sender_name' => $siteName, + 'sender_email' => (string) config('mail.from.address', 'info@' . $siteHost), + 'default_language' => in_array(config('app.locale'), array_keys($this->localeOptions()), true) ? (string) config('app.locale') : 'tr', + 'default_country_code' => CountryCodeManager::normalizeCountryCode(config('app.default_country_code', '+90')), + 'currencies' => $this->normalizeCurrencies(config('app.currencies', ['TRY'])), + 'linkedin_url' => 'https://www.linkedin.com/company/openclassify', + 'instagram_url' => 'https://www.instagram.com/openclassify', + 'whatsapp' => '+905551112233', + 'enable_google_maps' => false, + 'enable_google_login' => false, + 'enable_facebook_login' => false, + 'enable_apple_login' => false, + ]; + } + private function localeOptions(): array { $labels = [ diff --git a/Modules/Admin/Filament/Resources/CategoryResource.php b/Modules/Admin/Filament/Resources/CategoryResource.php index 1344c6241..e8114ba3b 100644 --- a/Modules/Admin/Filament/Resources/CategoryResource.php +++ b/Modules/Admin/Filament/Resources/CategoryResource.php @@ -45,7 +45,7 @@ class CategoryResource extends Resource TextColumn::make('listings_count')->counts('listings')->label('Listings'), IconColumn::make('is_active')->boolean(), TextColumn::make('sort_order')->sortable(), - ])->actions([ + ])->defaultSort('id', 'desc')->actions([ EditAction::make(), Action::make('activities') ->icon('heroicon-o-clock') diff --git a/Modules/Admin/Filament/Resources/CityResource.php b/Modules/Admin/Filament/Resources/CityResource.php index 877f3f15d..f60eb0517 100644 --- a/Modules/Admin/Filament/Resources/CityResource.php +++ b/Modules/Admin/Filament/Resources/CityResource.php @@ -46,7 +46,7 @@ class CityResource extends Resource TextColumn::make('districts_count')->counts('districts')->label('Districts')->sortable(), IconColumn::make('is_active')->boolean(), TextColumn::make('created_at')->dateTime()->sortable()->toggleable(isToggledHiddenByDefault: true), - ])->filters([ + ])->defaultSort('id', 'desc')->filters([ SelectFilter::make('country_id') ->label('Country') ->relationship('country', 'name') diff --git a/Modules/Admin/Filament/Resources/DistrictResource.php b/Modules/Admin/Filament/Resources/DistrictResource.php index 4577a12c4..f0ca451bb 100644 --- a/Modules/Admin/Filament/Resources/DistrictResource.php +++ b/Modules/Admin/Filament/Resources/DistrictResource.php @@ -48,7 +48,7 @@ class DistrictResource extends Resource TextColumn::make('city.country.name')->label('Country'), IconColumn::make('is_active')->boolean(), TextColumn::make('created_at')->dateTime()->sortable()->toggleable(isToggledHiddenByDefault: true), - ])->filters([ + ])->defaultSort('id', 'desc')->filters([ SelectFilter::make('country_id') ->label('Country') ->options(fn (): array => Country::query()->orderBy('name')->pluck('name', 'id')->all()) diff --git a/Modules/Admin/Filament/Resources/ListingCustomFieldResource.php b/Modules/Admin/Filament/Resources/ListingCustomFieldResource.php index 55742f18a..8e6514711 100644 --- a/Modules/Admin/Filament/Resources/ListingCustomFieldResource.php +++ b/Modules/Admin/Filament/Resources/ListingCustomFieldResource.php @@ -105,7 +105,7 @@ class ListingCustomFieldResource extends Resource IconColumn::make('is_active')->boolean()->label('Active'), TextColumn::make('sort_order')->sortable(), ]) - ->defaultSort('sort_order') + ->defaultSort('id', 'desc') ->actions([ EditAction::make(), DeleteAction::make(), diff --git a/Modules/Admin/Filament/Resources/ListingResource.php b/Modules/Admin/Filament/Resources/ListingResource.php index 6105ff6b2..4f2155627 100644 --- a/Modules/Admin/Filament/Resources/ListingResource.php +++ b/Modules/Admin/Filament/Resources/ListingResource.php @@ -194,6 +194,7 @@ class ListingResource extends Resource ->filtersFormColumns(3) ->filtersFormWidth('7xl') ->persistFiltersInSession() + ->defaultSort('id', 'desc') ->actions([ EditAction::make(), Action::make('activities') diff --git a/Modules/Admin/Filament/Resources/LocationResource.php b/Modules/Admin/Filament/Resources/LocationResource.php index de4cb56be..cdc679c20 100644 --- a/Modules/Admin/Filament/Resources/LocationResource.php +++ b/Modules/Admin/Filament/Resources/LocationResource.php @@ -46,7 +46,7 @@ class LocationResource extends Resource TextColumn::make('cities_count')->counts('cities')->label('Cities')->sortable(), IconColumn::make('is_active')->boolean(), TextColumn::make('created_at')->dateTime()->sortable()->toggleable(isToggledHiddenByDefault: true), - ])->filters([ + ])->defaultSort('id', 'desc')->filters([ TernaryFilter::make('is_active')->label('Active'), ])->actions([ EditAction::make(), diff --git a/Modules/Admin/Filament/Resources/UserResource.php b/Modules/Admin/Filament/Resources/UserResource.php index e47db0ad9..8c9295305 100644 --- a/Modules/Admin/Filament/Resources/UserResource.php +++ b/Modules/Admin/Filament/Resources/UserResource.php @@ -43,7 +43,7 @@ class UserResource extends Resource TextColumn::make('roles.name')->badge()->label('Roles'), StateFusionSelectColumn::make('status'), TextColumn::make('created_at')->dateTime()->sortable(), - ])->filters([ + ])->defaultSort('id', 'desc')->filters([ StateFusionSelectFilter::make('status'), ])->actions([ EditAction::make(), diff --git a/Modules/Category/Http/Controllers/CategoryController.php b/Modules/Category/Http/Controllers/CategoryController.php index 64212c121..60b51b6b6 100644 --- a/Modules/Category/Http/Controllers/CategoryController.php +++ b/Modules/Category/Http/Controllers/CategoryController.php @@ -3,7 +3,6 @@ namespace Modules\Category\Http\Controllers; use App\Http\Controllers\Controller; use Modules\Category\Models\Category; -use Modules\Listing\Models\Listing; use Modules\Theme\Support\ThemeManager; class CategoryController extends Controller @@ -18,22 +17,4 @@ class CategoryController extends Controller return view($this->themes->view('category', 'index'), compact('categories')); } - - public function show(Category $category) - { - $category->loadMissing([ - 'children' => fn ($query) => $query->active()->ordered(), - ]); - - $categoryIds = $category->descendantAndSelfIds()->all(); - - $listings = Listing::query() - ->where('status', 'active') - ->whereIn('category_id', $categoryIds) - ->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 1951abdb3..5f66ef1cf 100644 --- a/Modules/Category/Models/Category.php +++ b/Modules/Category/Models/Category.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Collection; +use Modules\Listing\Models\Listing; use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\LogsActivity; @@ -78,6 +79,58 @@ class Category extends Model ->get(); } + public static function listingDirectory(?int $selectedCategoryId): array + { + $categories = static::query() + ->active() + ->ordered() + ->get(['id', 'name', 'parent_id']); + + $activeListingCounts = Listing::query() + ->active() + ->whereNotNull('category_id') + ->selectRaw('category_id, count(*) as aggregate') + ->groupBy('category_id') + ->pluck('aggregate', 'category_id') + ->map(fn ($count): int => (int) $count); + + return [ + 'categories' => static::buildListingDirectoryTree($categories, $activeListingCounts), + 'selectedCategory' => $selectedCategoryId + ? $categories->firstWhere('id', $selectedCategoryId) + : null, + 'filterIds' => static::listingFilterIds($selectedCategoryId, $categories), + ]; + } + + public static function listingFilterIds(?int $selectedCategoryId, ?Collection $categories = null): ?array + { + if (! $selectedCategoryId) { + return null; + } + + if ($categories instanceof Collection) { + $selectedCategory = $categories->firstWhere('id', $selectedCategoryId); + + if (! $selectedCategory instanceof self) { + return []; + } + + return static::descendantAndSelfIdsFromCollection($selectedCategoryId, $categories); + } + + $selectedCategory = static::query() + ->active() + ->whereKey($selectedCategoryId) + ->first(['id']); + + if (! $selectedCategory) { + return []; + } + + return $selectedCategory->descendantAndSelfIds()->all(); + } + public function descendantAndSelfIds(): Collection { $ids = collect([(int) $this->getKey()]); @@ -127,4 +180,54 @@ class Category extends Model { return $this->hasMany(\Modules\Listing\Models\Listing::class)->where('status', 'active'); } + + private static function buildListingDirectoryTree(Collection $categories, Collection $activeListingCounts, ?int $parentId = null): Collection + { + return $categories + ->filter(fn (Category $category): bool => $parentId === null + ? $category->parent_id === null + : (int) $category->parent_id === $parentId) + ->values() + ->map(function (Category $category) use ($categories, $activeListingCounts): Category { + $children = static::buildListingDirectoryTree($categories, $activeListingCounts, (int) $category->getKey()); + $directActiveListingsCount = (int) $activeListingCounts->get((int) $category->getKey(), 0); + $activeListingTotal = $directActiveListingsCount + $children->sum( + fn (Category $child): int => (int) $child->getAttribute('active_listing_total') + ); + + $category->setRelation('children', $children); + $category->setAttribute('direct_active_listings_count', $directActiveListingsCount); + $category->setAttribute('active_listing_total', $activeListingTotal); + + return $category; + }) + ->values(); + } + + private static function descendantAndSelfIdsFromCollection(int $selectedCategoryId, Collection $categories): array + { + $ids = collect([$selectedCategoryId]); + $frontier = collect([$selectedCategoryId]); + + while ($frontier->isNotEmpty()) { + $children = $categories + ->filter(fn (Category $category): bool => $category->parent_id !== null && in_array((int) $category->parent_id, $frontier->all(), true)) + ->pluck('id') + ->map(fn ($id): int => (int) $id) + ->values(); + + if ($children->isEmpty()) { + break; + } + + $ids = $ids + ->merge($children) + ->unique() + ->values(); + + $frontier = $children; + } + + return $ids->all(); + } } diff --git a/Modules/Category/resources/views/index.blade.php b/Modules/Category/resources/views/index.blade.php index e7c719c45..ad08cf022 100644 --- a/Modules/Category/resources/views/index.blade.php +++ b/Modules/Category/resources/views/index.blade.php @@ -4,7 +4,7 @@

{{ __('messages.categories') }}

@foreach($categories as $category) - +
{{ $category->icon ?? '📦' }}

{{ $category->name }}

{{ $category->children->count() }} subcategories

diff --git a/Modules/Category/resources/views/show.blade.php b/Modules/Category/resources/views/show.blade.php deleted file mode 100644 index e827be93e..000000000 --- a/Modules/Category/resources/views/show.blade.php +++ /dev/null @@ -1,33 +0,0 @@ -@extends('app::layouts.app') -@section('content') -
-
-

{{ $category->icon ?? '' }} {{ $category->name }}

- @if($category->description)

{{ $category->description }}

@endif -
- @if($category->children->count()) -
- @endif -

Listings in {{ $category->name }}

-
- @forelse($listings as $listing) -
-
-

{{ $listing->title }}

-

{{ $listing->price ? number_format($listing->price, 0).' '.$listing->currency : 'Free' }}

- View → -
-
- @empty -

No listings in this category yet.

- @endforelse -
-
{{ $listings->links() }}
-
-@endsection diff --git a/Modules/Category/resources/views/themes/default/index.blade.php b/Modules/Category/resources/views/themes/default/index.blade.php index e7c719c45..ad08cf022 100644 --- a/Modules/Category/resources/views/themes/default/index.blade.php +++ b/Modules/Category/resources/views/themes/default/index.blade.php @@ -4,7 +4,7 @@

{{ __('messages.categories') }}

@foreach($categories as $category) - +
{{ $category->icon ?? '📦' }}

{{ $category->name }}

{{ $category->children->count() }} subcategories

diff --git a/Modules/Category/resources/views/themes/default/show.blade.php b/Modules/Category/resources/views/themes/default/show.blade.php deleted file mode 100644 index e827be93e..000000000 --- a/Modules/Category/resources/views/themes/default/show.blade.php +++ /dev/null @@ -1,33 +0,0 @@ -@extends('app::layouts.app') -@section('content') -
-
-

{{ $category->icon ?? '' }} {{ $category->name }}

- @if($category->description)

{{ $category->description }}

@endif -
- @if($category->children->count()) -
- @endif -

Listings in {{ $category->name }}

-
- @forelse($listings as $listing) -
-
-

{{ $listing->title }}

-

{{ $listing->price ? number_format($listing->price, 0).' '.$listing->currency : 'Free' }}

- View → -
-
- @empty -

No listings in this category yet.

- @endforelse -
-
{{ $listings->links() }}
-
-@endsection diff --git a/Modules/Category/resources/views/themes/otoplus/index.blade.php b/Modules/Category/resources/views/themes/otoplus/index.blade.php index e7c719c45..ad08cf022 100644 --- a/Modules/Category/resources/views/themes/otoplus/index.blade.php +++ b/Modules/Category/resources/views/themes/otoplus/index.blade.php @@ -4,7 +4,7 @@

{{ __('messages.categories') }}

@foreach($categories as $category) - +
{{ $category->icon ?? '📦' }}

{{ $category->name }}

{{ $category->children->count() }} subcategories

diff --git a/Modules/Category/resources/views/themes/otoplus/show.blade.php b/Modules/Category/resources/views/themes/otoplus/show.blade.php deleted file mode 100644 index e827be93e..000000000 --- a/Modules/Category/resources/views/themes/otoplus/show.blade.php +++ /dev/null @@ -1,33 +0,0 @@ -@extends('app::layouts.app') -@section('content') -
-
-

{{ $category->icon ?? '' }} {{ $category->name }}

- @if($category->description)

{{ $category->description }}

@endif -
- @if($category->children->count()) -
- @endif -

Listings in {{ $category->name }}

-
- @forelse($listings as $listing) -
-
-

{{ $listing->title }}

-

{{ $listing->price ? number_format($listing->price, 0).' '.$listing->currency : 'Free' }}

- View → -
-
- @empty -

No listings in this category yet.

- @endforelse -
-
{{ $listings->links() }}
-
-@endsection diff --git a/Modules/Category/routes/web.php b/Modules/Category/routes/web.php index 3174d4e08..760490fc6 100644 --- a/Modules/Category/routes/web.php +++ b/Modules/Category/routes/web.php @@ -4,5 +4,4 @@ use Modules\Category\Http\Controllers\CategoryController; Route::prefix('categories')->name('categories.')->group(function () { Route::get('/', [CategoryController::class, 'index'])->name('index'); - Route::get('/{category}', [CategoryController::class, 'show'])->name('show'); }); diff --git a/Modules/Conversation/routes/web.php b/Modules/Conversation/routes/web.php index 26114aedd..1ff3c9478 100644 --- a/Modules/Conversation/routes/web.php +++ b/Modules/Conversation/routes/web.php @@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Route; use Modules\Conversation\App\Http\Controllers\ConversationController; Route::middleware('auth')->prefix('panel')->name('panel.')->group(function () { - Route::get('/gelen-kutusu', [ConversationController::class, 'inbox'])->name('inbox.index'); + Route::get('/inbox', [ConversationController::class, 'inbox'])->name('inbox.index'); }); Route::middleware('auth')->name('conversations.')->group(function () { diff --git a/Modules/Listing/Http/Controllers/ListingController.php b/Modules/Listing/Http/Controllers/ListingController.php index 51e93acb8..7186c4751 100644 --- a/Modules/Listing/Http/Controllers/ListingController.php +++ b/Modules/Listing/Http/Controllers/ListingController.php @@ -65,11 +65,13 @@ class ListingController extends Controller $selectedCityName ); + $listingDirectory = Category::listingDirectory($categoryId); + $listingsQuery = Listing::query() - ->where('status', 'active') + ->active() ->with('category:id,name') ->searchTerm($search) - ->forCategory($categoryId) + ->forCategoryIds($listingDirectory['filterIds']) ->when($selectedCountryName, fn ($query) => $query->where('country', $selectedCountryName)) ->when($selectedCityName, fn ($query) => $query->where('city', $selectedCityName)) ->when(! is_null($minPrice), fn ($query) => $query->whereNotNull('price')->where('price', '>=', $minPrice)) @@ -82,28 +84,8 @@ class ListingController extends Controller ->paginate(16) ->withQueryString(); - $categories = Category::query() - ->where('is_active', true) - ->whereNull('parent_id') - ->withCount([ - 'listings as active_listings_count' => fn ($query) => $query->where('status', 'active'), - ]) - ->with([ - 'children' => fn ($query) => $query - ->where('is_active', true) - ->withCount([ - 'listings as active_listings_count' => fn ($childQuery) => $childQuery->where('status', 'active'), - ]) - ->orderBy('sort_order') - ->orderBy('name'), - ]) - ->orderBy('sort_order') - ->orderBy('name') - ->get(['id', 'name', 'parent_id']); - - $selectedCategory = $categoryId - ? Category::query()->whereKey($categoryId)->first(['id', 'name']) - : null; + $categories = $listingDirectory['categories']; + $selectedCategory = $listingDirectory['selectedCategory']; $favoriteListingIds = []; $isCurrentSearchSaved = false; diff --git a/Modules/Listing/Models/Listing.php b/Modules/Listing/Models/Listing.php index 1ac88d7d5..50cfab50d 100644 --- a/Modules/Listing/Models/Listing.php +++ b/Modules/Listing/Models/Listing.php @@ -2,11 +2,12 @@ namespace Modules\Listing\Models; use Illuminate\Database\Eloquent\Casts\Attribute; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Support\Collection; use Illuminate\Support\Str; +use Modules\Category\Models\Category; use Modules\Listing\States\ListingStatus; use Modules\Listing\Support\ListingPanelHelper; use Spatie\Activitylog\LogOptions; @@ -72,11 +73,16 @@ class Listing extends Model implements HasMedia public function scopePublicFeed(Builder $query): Builder { return $query - ->where('status', 'active') + ->active() ->orderByDesc('is_featured') ->orderByDesc('created_at'); } + public function scopeActive(Builder $query): Builder + { + return $query->where('status', 'active'); + } + public function scopeSearchTerm(Builder $query, string $search): Builder { $search = trim($search); @@ -96,11 +102,20 @@ class Listing extends Model implements HasMedia public function scopeForCategory(Builder $query, ?int $categoryId): Builder { - if (! $categoryId) { + return $query->forCategoryIds(Category::listingFilterIds($categoryId)); + } + + public function scopeForCategoryIds(Builder $query, ?array $categoryIds): Builder + { + if ($categoryIds === null) { return $query; } - return $query->where('category_id', $categoryId); + if ($categoryIds === []) { + return $query->whereRaw('1 = 0'); + } + + return $query->whereIn('category_id', $categoryIds); } public function themeGallery(): array diff --git a/Modules/Listing/resources/views/index.blade.php b/Modules/Listing/resources/views/index.blade.php index 03e7c0543..a5c4b361a 100644 --- a/Modules/Listing/resources/views/index.blade.php +++ b/Modules/Listing/resources/views/index.blade.php @@ -43,8 +43,7 @@ @foreach($categories as $category) @php - $childCount = (int) $category->children->sum('active_listings_count'); - $categoryCount = (int) $category->active_listings_count + $childCount; + $categoryCount = (int) $category->active_listing_total; $isSelectedParent = (int) $categoryId === (int) $category->id; $categoryUrl = route('listings.index', array_filter(array_merge($baseCategoryQuery, [ 'category' => $category->id, @@ -64,7 +63,7 @@ @endphp {{ $childCategory->name }} - {{ number_format((int) $childCategory->active_listings_count, 0, ',', '.') }} + {{ number_format((int) $childCategory->active_listing_total, 0, ',', '.') }} @endforeach @endforeach diff --git a/Modules/Listing/resources/views/themes/default/index.blade.php b/Modules/Listing/resources/views/themes/default/index.blade.php index 03e7c0543..a5c4b361a 100644 --- a/Modules/Listing/resources/views/themes/default/index.blade.php +++ b/Modules/Listing/resources/views/themes/default/index.blade.php @@ -43,8 +43,7 @@ @foreach($categories as $category) @php - $childCount = (int) $category->children->sum('active_listings_count'); - $categoryCount = (int) $category->active_listings_count + $childCount; + $categoryCount = (int) $category->active_listing_total; $isSelectedParent = (int) $categoryId === (int) $category->id; $categoryUrl = route('listings.index', array_filter(array_merge($baseCategoryQuery, [ 'category' => $category->id, @@ -64,7 +63,7 @@ @endphp {{ $childCategory->name }} - {{ number_format((int) $childCategory->active_listings_count, 0, ',', '.') }} + {{ number_format((int) $childCategory->active_listing_total, 0, ',', '.') }} @endforeach @endforeach diff --git a/Modules/Listing/resources/views/themes/otoplus/index.blade.php b/Modules/Listing/resources/views/themes/otoplus/index.blade.php index 03e7c0543..a5c4b361a 100644 --- a/Modules/Listing/resources/views/themes/otoplus/index.blade.php +++ b/Modules/Listing/resources/views/themes/otoplus/index.blade.php @@ -43,8 +43,7 @@ @foreach($categories as $category) @php - $childCount = (int) $category->children->sum('active_listings_count'); - $categoryCount = (int) $category->active_listings_count + $childCount; + $categoryCount = (int) $category->active_listing_total; $isSelectedParent = (int) $categoryId === (int) $category->id; $categoryUrl = route('listings.index', array_filter(array_merge($baseCategoryQuery, [ 'category' => $category->id, @@ -64,7 +63,7 @@ @endphp {{ $childCategory->name }} - {{ number_format((int) $childCategory->active_listings_count, 0, ',', '.') }} + {{ number_format((int) $childCategory->active_listing_total, 0, ',', '.') }} @endforeach @endforeach diff --git a/Modules/Listing/resources/views/themes/otoplus/show.blade.php b/Modules/Listing/resources/views/themes/otoplus/show.blade.php index 43ed45da2..208f3d19a 100644 --- a/Modules/Listing/resources/views/themes/otoplus/show.blade.php +++ b/Modules/Listing/resources/views/themes/otoplus/show.blade.php @@ -33,7 +33,7 @@ Anasayfa @foreach(($breadcrumbCategories ?? collect()) as $crumb) - {{ $crumb->name }} + {{ $crumb->name }} @endforeach {{ $displayTitle }} diff --git a/Modules/Partner/Filament/Resources/ListingResource.php b/Modules/Partner/Filament/Resources/ListingResource.php index e3d506970..1c7d346fe 100644 --- a/Modules/Partner/Filament/Resources/ListingResource.php +++ b/Modules/Partner/Filament/Resources/ListingResource.php @@ -174,7 +174,7 @@ class ListingResource extends Resource StateFusionSelectColumn::make('status'), TextColumn::make('city'), TextColumn::make('created_at')->dateTime()->sortable(), - ])->filters([ + ])->defaultSort('id', 'desc')->filters([ StateFusionSelectFilter::make('status'), SelectFilter::make('category_id') ->label('Category') diff --git a/app/Livewire/PanelQuickListingForm.php b/app/Livewire/PanelQuickListingForm.php index 497f094d7..d85cf4ead 100644 --- a/app/Livewire/PanelQuickListingForm.php +++ b/app/Livewire/PanelQuickListingForm.php @@ -188,13 +188,13 @@ class PanelQuickListingForm extends Component } catch (Throwable $exception) { report($exception); $this->isPublishing = false; - session()->flash('error', 'İlan oluşturulamadı. Lütfen tekrar deneyin.'); + session()->flash('error', 'The listing could not be created. Please try again.'); return; } $this->isPublishing = false; - session()->flash('success', 'İlan başarıyla oluşturuldu.'); + session()->flash('success', 'Your listing has been created successfully.'); $this->redirectRoute('panel.listings.index'); } @@ -243,23 +243,23 @@ class PanelQuickListingForm extends Component public function getCurrentParentNameProperty(): string { if (! $this->activeParentCategoryId) { - return 'Kategori Seçimi'; + return 'Category Selection'; } $category = collect($this->categories)->firstWhere('id', $this->activeParentCategoryId); - return (string) ($category['name'] ?? 'Kategori Seçimi'); + return (string) ($category['name'] ?? 'Category Selection'); } public function getCurrentStepTitleProperty(): string { return match ($this->currentStep) { - 1 => 'Fotoğraf', - 2 => 'Kategori Seçimi', - 3 => 'İlan Bilgileri', - 4 => 'İlan Özellikleri', - 5 => 'İlan Önizlemesi', - default => 'İlan Ver', + 1 => 'Photos', + 2 => 'Category Selection', + 3 => 'Listing Details', + 4 => 'Attributes', + 5 => 'Preview', + default => 'Create Listing', }; } @@ -352,7 +352,7 @@ class PanelQuickListingForm extends Component public function getCurrentUserNameProperty(): string { - return (string) (auth()->user()?->name ?: 'Kullanıcı'); + return (string) (auth()->user()?->name ?: 'User'); } public function getCurrentUserInitialProperty(): string @@ -402,8 +402,8 @@ class PanelQuickListingForm extends Component Rule::in(collect($this->categories)->pluck('id')->all()), ], ], [ - 'selectedCategoryId.required' => 'Lütfen bir kategori seçin.', - 'selectedCategoryId.in' => 'Geçerli bir kategori seçin.', + 'selectedCategoryId.required' => 'Please choose a category.', + 'selectedCategoryId.in' => 'Please choose a valid category.', ]); } @@ -426,18 +426,18 @@ class PanelQuickListingForm extends Component ->contains(fn (array $city): bool => $city['id'] === (int) $value); if (! $cityExists) { - $fail('Seçtiğiniz şehir, seçilen ülkeye ait değil.'); + $fail('The selected city does not belong to the chosen country.'); } }, ], ], [ - 'listingTitle.required' => 'İlan başlığı zorunludur.', - 'listingTitle.max' => 'İlan başlığı en fazla 70 karakter olabilir.', - 'price.required' => 'Fiyat zorunludur.', - 'price.numeric' => 'Fiyat sayısal olmalıdır.', - 'description.required' => 'Açıklama zorunludur.', - 'description.max' => 'Açıklama en fazla 1450 karakter olabilir.', - 'selectedCountryId.required' => 'Ülke seçimi zorunludur.', + 'listingTitle.required' => 'A title is required.', + 'listingTitle.max' => 'The title may not exceed 70 characters.', + 'price.required' => 'A price is required.', + 'price.numeric' => 'The price must be numeric.', + 'description.required' => 'A description is required.', + 'description.max' => 'The description may not exceed 1450 characters.', + 'selectedCountryId.required' => 'Please choose a country.', ]); } diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 496341cdd..616e53ace 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -188,7 +188,7 @@ $trendSkin = $trendSkins[$index % count($trendSkins)]; $trendIcon = $trendIcons[$index % count($trendIcons)]; @endphp - + @@ -178,10 +178,10 @@ - Tüm Kategoriler + All Categories @forelse($headerCategories as $headerCategory) - + {{ $headerCategory['name'] }} @empty @@ -211,22 +211,22 @@

{{ $siteDescription }}

-

Hızlı Linkler

+

Quick Links

-

Hesap

+

Account

-

Bağlantılar

+

Links

    @if($linkedinUrl)
  • LinkedIn
  • @@ -238,10 +238,10 @@
  • WhatsApp
  • @endif @if(!$linkedinUrl && !$instagramUrl && !$whatsappUrl) -
  • Henüz sosyal bağlantı eklenmedi.
  • +
  • No social links added yet.
  • @endif
-

Diller

+

Languages

@foreach($availableLocales as $locale) {{ strtoupper($locale) }} diff --git a/resources/views/panel/create.blade.php b/resources/views/panel/create.blade.php index 8a58cbc09..47674ba00 100644 --- a/resources/views/panel/create.blade.php +++ b/resources/views/panel/create.blade.php @@ -1,6 +1,6 @@ @extends('app::layouts.app') -@section('title', 'İlan Ver') +@section('title', 'Create Listing') @section('simple_page', '1') diff --git a/resources/views/panel/partials/sidebar.blade.php b/resources/views/panel/partials/sidebar.blade.php index 333b7530d..c03b16a6c 100644 --- a/resources/views/panel/partials/sidebar.blade.php +++ b/resources/views/panel/partials/sidebar.blade.php @@ -5,24 +5,24 @@ diff --git a/resources/views/partials/quick-create/form.blade.php b/resources/views/partials/quick-create/form.blade.php index 3044d989e..91ec10daa 100644 --- a/resources/views/partials/quick-create/form.blade.php +++ b/resources/views/partials/quick-create/form.blade.php @@ -1,4 +1,4 @@ -
+
-
-

{{ $this->currentStepTitle }}

-
-