diff --git a/Modules/Admin/Filament/Pages/ManageGeneralSettings.php b/Modules/Admin/Filament/Pages/ManageGeneralSettings.php index 56992c914..53328ef87 100644 --- a/Modules/Admin/Filament/Pages/ManageGeneralSettings.php +++ b/Modules/Admin/Filament/Pages/ManageGeneralSettings.php @@ -2,23 +2,23 @@ namespace Modules\Admin\Filament\Pages; -use App\Settings\GeneralSettings; -use App\Support\CountryCodeManager; -use App\Support\HomeSlideDefaults; use BackedEnum; use Filament\Forms\Components\FileUpload; use Filament\Forms\Components\Hidden; use Filament\Forms\Components\Select; use Filament\Forms\Components\TagsInput; -use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Textarea; +use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Toggle; use Filament\Pages\SettingsPage; use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Components\Utilities\Set; use Filament\Schemas\Schema; use Modules\Admin\Support\HomeSlideFormSchema; +use Modules\Location\Support\CountryCodeManager; use Modules\S3\Support\MediaStorage; +use Modules\Site\App\Settings\GeneralSettings; +use Modules\Site\App\Support\HomeSlideDefaults; use Tapp\FilamentCountryCodeField\Forms\Components\CountryCodeSelect; use UnitEnum; use Ysfkaya\FilamentPhoneInput\Forms\PhoneInput; @@ -31,9 +31,9 @@ class ManageGeneralSettings extends SettingsPage protected static ?string $navigationLabel = 'Genel Ayarlar'; - protected static string | BackedEnum | null $navigationIcon = 'heroicon-o-cog-6-tooth'; + protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-cog-6-tooth'; - protected static string | UnitEnum | null $navigationGroup = 'Ayarlar'; + protected static string|UnitEnum|null $navigationGroup = 'Ayarlar'; protected static ?int $navigationSort = 1; @@ -246,7 +246,7 @@ class ManageGeneralSettings extends SettingsPage 'home_slides' => $this->defaultHomeSlides(), 'site_logo_disk' => null, 'sender_name' => $siteName, - 'sender_email' => (string) config('mail.from.address', 'info@' . $siteHost), + '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') : 'en', 'default_country_code' => CountryCodeManager::normalizeCountryCode(config('app.default_country_code', '+90')), 'currencies' => $this->normalizeCurrencies(config('app.currencies', ['TRY'])), @@ -272,7 +272,7 @@ class ManageGeneralSettings extends SettingsPage ->all(); } - private function normalizeCurrencies(null | array | string $state): array + private function normalizeCurrencies(null|array|string $state): array { $source = is_array($state) ? $state : (filled($state) ? [$state] : []); diff --git a/Modules/Admin/Filament/Pages/ManageHomeSlides.php b/Modules/Admin/Filament/Pages/ManageHomeSlides.php deleted file mode 100644 index c311e6728..000000000 --- a/Modules/Admin/Filament/Pages/ManageHomeSlides.php +++ /dev/null @@ -1,68 +0,0 @@ - $this->normalizeHomeSlides( - $data['home_slides'] ?? $this->defaultHomeSlides(), - MediaStorage::storedDisk('public'), - ), - ]; - } - - protected function mutateFormDataBeforeSave(array $data): array - { - $data['home_slides'] = $this->normalizeHomeSlides($data['home_slides'] ?? [], MediaStorage::activeDisk()); - - return $data; - } - - public function form(Schema $schema): Schema - { - return $schema - ->components([ - HomeSlideFormSchema::make( - $this->defaultHomeSlides(), - fn ($state): array => $this->normalizeHomeSlides($state, MediaStorage::activeDisk()), - ), - ]); - } - - private function defaultHomeSlides(): array - { - return HomeSlideDefaults::defaults(); - } - - private function normalizeHomeSlides(mixed $state, ?string $defaultDisk = null): array - { - return HomeSlideDefaults::normalize($state, $defaultDisk); - } -} diff --git a/Modules/Admin/Filament/Resources/ListingResource.php b/Modules/Admin/Filament/Resources/ListingResource.php index 2d38d2b18..64044882d 100644 --- a/Modules/Admin/Filament/Resources/ListingResource.php +++ b/Modules/Admin/Filament/Resources/ListingResource.php @@ -2,42 +2,14 @@ namespace Modules\Admin\Filament\Resources; -use A909M\FilamentStateFusion\Forms\Components\StateFusionSelect; -use A909M\FilamentStateFusion\Tables\Columns\StateFusionSelectColumn; -use A909M\FilamentStateFusion\Tables\Filters\StateFusionSelectFilter; -use App\Support\CountryCodeManager; use BackedEnum; -use Cheesegrits\FilamentGoogleMaps\Fields\Map; -use Filament\Forms\Components\DatePicker; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\SpatieMediaLibraryFileUpload; -use Filament\Forms\Components\Textarea; -use Filament\Forms\Components\TextInput; -use Filament\Forms\Components\Toggle; use Filament\Resources\Resource; -use Filament\Schemas\Components\Section; -use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Schema; -use Filament\Tables\Columns\IconColumn; -use Filament\Tables\Columns\SpatieMediaLibraryImageColumn; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Enums\FiltersLayout; -use Filament\Tables\Filters\Filter; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TernaryFilter; use Filament\Tables\Table; -use Illuminate\Database\Eloquent\Builder; use Modules\Admin\Filament\Resources\ListingResource\Pages; -use Modules\Admin\Support\Filament\ResourceTableActions; -use Modules\Category\Models\Category; use Modules\Listing\Models\Listing; -use Modules\Listing\Support\ListingCustomFieldSchemaBuilder; -use Modules\Listing\Support\ListingPanelHelper; -use Modules\Location\Models\City; -use Modules\Location\Models\Country; -use Modules\Video\Support\Filament\VideoFormSchema; +use Modules\Listing\Support\Filament\AdminListingResourceSchema; use UnitEnum; -use Ysfkaya\FilamentPhoneInput\Forms\PhoneInput; class ListingResource extends Resource { @@ -49,138 +21,12 @@ class ListingResource extends Resource public static function form(Schema $schema): Schema { - return $schema->schema([ - TextInput::make('title')->required()->maxLength(255)->live(onBlur: true)->afterStateUpdated(fn ($state, $set) => $set('slug', \Illuminate\Support\Str::slug($state).'-'.\Illuminate\Support\Str::random(4))), - TextInput::make('slug')->required()->maxLength(255)->unique(ignoreRecord: true), - Textarea::make('description')->rows(4), - TextInput::make('price') - ->numeric() - ->currencyMask(thousandSeparator: ',', decimalSeparator: '.', precision: 2), - Select::make('currency') - ->options(fn () => ListingPanelHelper::currencyOptions()) - ->default(fn () => ListingPanelHelper::defaultCurrency()) - ->required(), - Select::make('category_id') - ->label('Category') - ->options(fn (): array => Category::activeIdNameOptions()) - ->searchable() - ->live() - ->afterStateUpdated(fn ($state, $set) => $set('custom_fields', [])) - ->nullable(), - Select::make('user_id')->relationship('user', 'email')->label('Owner')->searchable()->preload()->nullable(), - Section::make('Custom Fields') - ->description('Category specific listing attributes.') - ->schema(fn (Get $get): array => ListingCustomFieldSchemaBuilder::formComponents( - ($categoryId = $get('category_id')) ? (int) $categoryId : null - )) - ->columns(2) - ->columnSpanFull() - ->visible(fn (Get $get): bool => ListingCustomFieldSchemaBuilder::hasFields( - ($categoryId = $get('category_id')) ? (int) $categoryId : null - )), - StateFusionSelect::make('status')->required(), - PhoneInput::make('contact_phone')->defaultCountry(CountryCodeManager::defaultCountryIso2())->nullable(), - TextInput::make('contact_email')->email()->maxLength(255), - Toggle::make('is_featured')->default(false), - Select::make('country') - ->label('Country') - ->options(fn (): array => Country::nameOptions()) - ->searchable() - ->preload() - ->live() - ->afterStateUpdated(fn ($state, $set) => $set('city', null)) - ->nullable(), - Select::make('city') - ->label('City') - ->options(fn (Get $get): array => City::nameOptions($get('country'))) - ->searchable() - ->preload() - ->nullable(), - Map::make('location') - ->label('Location') - ->visible(fn (): bool => ListingPanelHelper::googleMapsEnabled()) - ->draggable() - ->clickable() - ->autocomplete('city') - ->autocompleteReverse(true) - ->reverseGeocode([ - 'city' => '%L', - ]) - ->defaultLocation([41.0082, 28.9784]) - ->defaultZoom(10) - ->height('320px') - ->columnSpanFull(), - SpatieMediaLibraryFileUpload::make('images') - ->collection('listing-images') - ->multiple() - ->image() - ->reorderable(), - VideoFormSchema::listingSection(), - ]); + return $schema->schema(AdminListingResourceSchema::form()); } public static function table(Table $table): Table { - return $table->columns([ - SpatieMediaLibraryImageColumn::make('images') - ->collection('listing-images') - ->circular(), - TextColumn::make('id')->sortable(), - TextColumn::make('title')->searchable()->sortable()->limit(40), - TextColumn::make('category.name')->label('Category')->sortable(), - TextColumn::make('user.email')->label('Owner')->searchable()->toggleable()->sortable(), - TextColumn::make('price') - ->currency(fn (Listing $record): string => $record->currency ?: ListingPanelHelper::defaultCurrency()) - ->sortable(), - StateFusionSelectColumn::make('status')->sortable(), - IconColumn::make('is_featured')->boolean()->label('Featured')->sortable(), - TextColumn::make('city')->sortable(), - TextColumn::make('country')->sortable(), - TextColumn::make('created_at')->dateTime()->sortable(), - ])->filters([ - StateFusionSelectFilter::make('status'), - SelectFilter::make('category_id') - ->label('Category') - ->relationship('category', 'name') - ->searchable() - ->preload(), - SelectFilter::make('user_id') - ->label('Owner') - ->relationship('user', 'email') - ->searchable() - ->preload(), - SelectFilter::make('country') - ->options(fn (): array => Country::nameOptions()) - ->searchable(), - SelectFilter::make('city') - ->options(fn (): array => City::nameOptions(null, false)) - ->searchable(), - TernaryFilter::make('is_featured')->label('Featured'), - Filter::make('created_at') - ->label('Created Date') - ->schema([ - DatePicker::make('from')->label('From'), - DatePicker::make('until')->label('Until'), - ]) - ->query(fn (Builder $query, array $data): Builder => $query - ->when($data['from'] ?? null, fn (Builder $query, string $date): Builder => $query->whereDate('created_at', '>=', $date)) - ->when($data['until'] ?? null, fn (Builder $query, string $date): Builder => $query->whereDate('created_at', '<=', $date))), - Filter::make('price') - ->label('Price Range') - ->schema([ - TextInput::make('min')->numeric()->label('Min'), - TextInput::make('max')->numeric()->label('Max'), - ]) - ->query(fn (Builder $query, array $data): Builder => $query - ->when($data['min'] ?? null, fn (Builder $query, string $amount): Builder => $query->where('price', '>=', (float) $amount)) - ->when($data['max'] ?? null, fn (Builder $query, string $amount): Builder => $query->where('price', '<=', (float) $amount))), - ]) - ->filtersLayout(FiltersLayout::AboveContent) - ->filtersFormColumns(3) - ->filtersFormWidth('7xl') - ->persistFiltersInSession() - ->defaultSort('id', 'desc') - ->actions(ResourceTableActions::editActivityDelete(static::class)); + return AdminListingResourceSchema::configureTable($table, static::class); } public static function getPages(): array diff --git a/Modules/Admin/Filament/Widgets/ListingsTrendChart.php b/Modules/Admin/Filament/Widgets/ListingsTrendChart.php index 416f32710..f1011b330 100644 --- a/Modules/Admin/Filament/Widgets/ListingsTrendChart.php +++ b/Modules/Admin/Filament/Widgets/ListingsTrendChart.php @@ -1,4 +1,5 @@ all(); } + public static function activeCount(): int + { + return (int) static::query() + ->active() + ->count(); + } + + public static function homeParentCategories(int $limit = 8): Collection + { + return static::query() + ->active() + ->whereNull('parent_id') + ->ordered() + ->limit($limit) + ->get(); + } + + public static function headerNavigationItems(int $limit = 8): array + { + return static::query() + ->active() + ->whereNull('parent_id') + ->ordered() + ->limit($limit) + ->get(['id', 'name', 'icon']) + ->map(fn (self $category): array => [ + 'id' => (int) $category->id, + 'name' => (string) $category->name, + 'icon_url' => $category->iconUrl(), + ]) + ->all(); + } + + public static function activeAiCatalog(): Collection + { + return static::query() + ->active() + ->ordered() + ->get(['id', 'name', 'parent_id']); + } + + public static function panelQuickCatalog(): array + { + $all = static::query() + ->active() + ->ordered() + ->get(['id', 'name', 'parent_id', 'icon']); + + $childrenCount = static::query() + ->active() + ->selectRaw('parent_id, count(*) as aggregate') + ->whereNotNull('parent_id') + ->groupBy('parent_id') + ->pluck('aggregate', 'parent_id'); + + return $all + ->map(fn (self $category): array => [ + 'id' => (int) $category->id, + 'name' => (string) $category->name, + 'parent_id' => $category->parent_id ? (int) $category->parent_id : null, + 'icon' => $category->icon, + 'has_children' => ((int) ($childrenCount[$category->id] ?? 0)) > 0, + ]) + ->all(); + } + public static function rootIdNameOptions(): array { return static::query() diff --git a/Modules/Conversation/database/seeders/ConversationDemoSeeder.php b/Modules/Conversation/Database/Seeders/ConversationDemoSeeder.php similarity index 99% rename from Modules/Conversation/database/seeders/ConversationDemoSeeder.php rename to Modules/Conversation/Database/Seeders/ConversationDemoSeeder.php index 4018ee4a9..e866e7d9e 100644 --- a/Modules/Conversation/database/seeders/ConversationDemoSeeder.php +++ b/Modules/Conversation/Database/Seeders/ConversationDemoSeeder.php @@ -107,7 +107,7 @@ class ConversationDemoSeeder extends Seeder $readAfterMinutes = $payload['read_after_minutes']; $readAt = is_numeric($readAfterMinutes) ? $createdAt->copy()->addMinutes((int) $readAfterMinutes) : null; - $message = new ConversationMessage(); + $message = new ConversationMessage; $message->forceFill([ 'conversation_id' => $conversation->getKey(), 'sender_id' => $sender->getKey(), diff --git a/Modules/Conversation/database/migrations/2026_03_04_000000_create_conversation_tables.php b/Modules/Conversation/Database/migrations/2026_03_04_000000_create_conversation_tables.php similarity index 100% rename from Modules/Conversation/database/migrations/2026_03_04_000000_create_conversation_tables.php rename to Modules/Conversation/Database/migrations/2026_03_04_000000_create_conversation_tables.php diff --git a/Modules/Conversation/resources/views/inbox.blade.php b/Modules/Conversation/resources/views/inbox.blade.php index 053ec63fb..2d7b73056 100644 --- a/Modules/Conversation/resources/views/inbox.blade.php +++ b/Modules/Conversation/resources/views/inbox.blade.php @@ -5,10 +5,10 @@ @section('content')