diff --git a/app/Filament/Admin/Resources/Settings/Pages/CreateSetting.php b/app/Filament/Admin/Resources/Settings/Pages/CreateSetting.php new file mode 100644 index 0000000..a97330f --- /dev/null +++ b/app/Filament/Admin/Resources/Settings/Pages/CreateSetting.php @@ -0,0 +1,46 @@ +getResource()::getUrl('index'); + } + + protected function getCreatedNotificationTitle(): ?string + { + return __('settings.created_successfully'); + } + + protected function mutateFormDataBeforeSave(array $data): array + { + // Boolean değerleri string'e çevir + if (isset($data['type']) && $data['type'] === 'boolean') { + $data['value'] = $data['value'] ? '1' : '0'; + } + + // Integer değerleri string'e çevir + if (isset($data['type']) && $data['type'] === 'integer') { + $data['value'] = (string) $data['value']; + } + + // Float değerleri string'e çevir + if (isset($data['type']) && $data['type'] === 'float') { + $data['value'] = (string) $data['value']; + } + + return $data; + } +} diff --git a/app/Filament/Admin/Resources/Settings/Pages/EditSetting.php b/app/Filament/Admin/Resources/Settings/Pages/EditSetting.php new file mode 100644 index 0000000..01bea62 --- /dev/null +++ b/app/Filament/Admin/Resources/Settings/Pages/EditSetting.php @@ -0,0 +1,81 @@ +label(__('settings.delete')), + RestoreAction::make() + ->label(__('settings.restore')), + ForceDeleteAction::make() + ->label(__('settings.force_delete')), + ]; + } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('index'); + } + + protected function getSavedNotificationTitle(): ?string + { + return __('settings.updated_successfully'); + } + + protected function mutateFormDataBeforeFill(array $data): array + { + // Boolean tipindeki değerleri cast et + if (isset($data['type']) && $data['type'] === 'boolean') { + $data['value'] = filter_var($data['value'], FILTER_VALIDATE_BOOLEAN); + } + + // Integer tipindeki değerleri cast et + if (isset($data['type']) && $data['type'] === 'integer') { + $data['value'] = (int) $data['value']; + } + + // Float tipindeki değerleri cast et + if (isset($data['type']) && $data['type'] === 'float') { + $data['value'] = (float) $data['value']; + } + + return $data; + } + + protected function mutateFormDataBeforeSave(array $data): array + { + // Boolean değerleri string'e çevir + if (isset($data['type']) && $data['type'] === 'boolean') { + $data['value'] = $data['value'] ? '1' : '0'; + } + + // Integer değerleri string'e çevir + if (isset($data['type']) && $data['type'] === 'integer') { + $data['value'] = (string) $data['value']; + } + + // Float değerleri string'e çevir + if (isset($data['type']) && $data['type'] === 'float') { + $data['value'] = (string) $data['value']; + } + + return $data; + } +} diff --git a/app/Filament/Admin/Resources/Settings/Pages/ListSettings.php b/app/Filament/Admin/Resources/Settings/Pages/ListSettings.php new file mode 100644 index 0000000..c07e9c9 --- /dev/null +++ b/app/Filament/Admin/Resources/Settings/Pages/ListSettings.php @@ -0,0 +1,25 @@ +label(__('settings.create')), + ]; + } +} diff --git a/app/Filament/Admin/Resources/Settings/Schemas/SettingForm.php b/app/Filament/Admin/Resources/Settings/Schemas/SettingForm.php new file mode 100644 index 0000000..32e1861 --- /dev/null +++ b/app/Filament/Admin/Resources/Settings/Schemas/SettingForm.php @@ -0,0 +1,107 @@ +components([ + Section::make(__('settings.title')) + ->schema([ + TextInput::make('key') + ->label(__('settings.key')) + ->helperText(__('settings.key_helper')) + ->required() + ->unique(ignoreRecord: true) + ->regex('/^[a-z0-9_-]+$/') + ->maxLength(255) + ->disabled(fn ($record) => $record !== null) + ->columnSpanFull(), + + TextInput::make('label') + ->label(__('settings.label')) + ->helperText(__('settings.label_helper')) + ->required() + ->maxLength(255) + ->columnSpanFull(), + + Textarea::make('description') + ->label(__('settings.description')) + ->helperText(__('settings.description_helper')) + ->rows(3) + ->columnSpanFull(), + + Select::make('type') + ->label(__('settings.type')) + ->helperText(__('settings.type_helper')) + ->required() + ->options([ + 'string' => __('settings.type_string'), + 'text' => __('settings.type_text'), + 'boolean' => __('settings.type_boolean'), + 'integer' => __('settings.type_integer'), + 'float' => __('settings.type_float'), + 'array' => __('settings.type_array'), + 'json' => __('settings.type_json'), + ]) + ->live() + ->afterStateUpdated(fn (Set $set) => $set('value', null)), + + Select::make('group') + ->label(__('settings.group')) + ->helperText(__('settings.group_helper')) + ->required() + ->options([ + 'general' => __('settings.group_general'), + 'email' => __('settings.group_email'), + 'seo' => __('settings.group_seo'), + 'social' => __('settings.group_social'), + 'security' => __('settings.group_security'), + 'payment' => __('settings.group_payment'), + 'notification' => __('settings.group_notification'), + 'other' => __('settings.group_other'), + ]) + ->default('general'), + + Textarea::make('value') + ->label(__('settings.value')) + ->helperText(__('settings.value_helper')) + ->required() + ->visible(fn (Get $get) => !in_array($get('type'), ['boolean'])) + ->rows(fn (Get $get) => $get('type') === 'text' ? 5 : 3) + ->columnSpanFull(), + + Toggle::make('value') + ->label(__('settings.value')) + ->helperText(__('settings.value_helper')) + ->required() + ->visible(fn (Get $get) => $get('type') === 'boolean') + ->columnSpanFull(), + + Toggle::make('is_active') + ->label(__('settings.is_active')) + ->helperText(__('settings.is_active_helper')) + ->default(true) + ->columnSpan(1), + + Toggle::make('is_public') + ->label(__('settings.is_public')) + ->helperText(__('settings.is_public_helper')) + ->default(false) + ->columnSpan(1), + ]) + ->columns(2), + ]); + } +} diff --git a/app/Filament/Admin/Resources/Settings/SettingResource.php b/app/Filament/Admin/Resources/Settings/SettingResource.php new file mode 100644 index 0000000..65b93a7 --- /dev/null +++ b/app/Filament/Admin/Resources/Settings/SettingResource.php @@ -0,0 +1,78 @@ + ListSettings::route('/'), + 'create' => CreateSetting::route('/create'), + 'edit' => EditSetting::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Admin/Resources/Settings/Tables/SettingsTable.php b/app/Filament/Admin/Resources/Settings/Tables/SettingsTable.php new file mode 100644 index 0000000..48e44cb --- /dev/null +++ b/app/Filament/Admin/Resources/Settings/Tables/SettingsTable.php @@ -0,0 +1,136 @@ +columns([ + TextColumn::make('key') + ->label(__('settings.table_key')) + ->searchable() + ->sortable(), + + TextColumn::make('label') + ->label(__('settings.table_label')) + ->searchable() + ->sortable(), + + TextColumn::make('value') + ->label(__('settings.table_value')) + ->searchable() + ->limit(50) + ->wrap(), + + TextColumn::make('type') + ->label(__('settings.table_type')) + ->badge() + ->color(fn (string $state): string => match ($state) { + 'boolean' => 'success', + 'integer', 'float' => 'info', + 'array', 'json' => 'warning', + default => 'gray', + }) + ->formatStateUsing(fn (string $state): string => __("settings.type_{$state}")), + + TextColumn::make('group') + ->label(__('settings.table_group')) + ->badge() + ->color('primary') + ->formatStateUsing(fn (string $state): string => __("settings.group_{$state}")), + + IconColumn::make('is_active') + ->label(__('settings.table_active')) + ->boolean() + ->sortable(), + + IconColumn::make('is_public') + ->label(__('settings.table_public')) + ->boolean() + ->sortable(), + + TextColumn::make('created_at') + ->label(__('settings.table_created_at')) + ->dateTime('d.m.Y H:i') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + + TextColumn::make('updated_at') + ->label(__('settings.table_updated_at')) + ->dateTime('d.m.Y H:i') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + SelectFilter::make('group') + ->label(__('settings.filter_group')) + ->options([ + 'general' => __('settings.group_general'), + 'email' => __('settings.group_email'), + 'seo' => __('settings.group_seo'), + 'social' => __('settings.group_social'), + 'security' => __('settings.group_security'), + 'payment' => __('settings.group_payment'), + 'notification' => __('settings.group_notification'), + 'other' => __('settings.group_other'), + ]), + + SelectFilter::make('type') + ->label(__('settings.filter_type')) + ->options([ + 'string' => __('settings.type_string'), + 'text' => __('settings.type_text'), + 'boolean' => __('settings.type_boolean'), + 'integer' => __('settings.type_integer'), + 'float' => __('settings.type_float'), + 'array' => __('settings.type_array'), + 'json' => __('settings.type_json'), + ]), + + TernaryFilter::make('is_active') + ->label(__('settings.filter_active')) + ->boolean() + ->trueLabel(__('settings.is_active')) + ->falseLabel(__('settings.is_active') . ' (Hayır)'), + + TernaryFilter::make('is_public') + ->label(__('settings.filter_public')) + ->boolean() + ->trueLabel(__('settings.is_public')) + ->falseLabel(__('settings.is_public') . ' (Hayır)'), + + TrashedFilter::make(), + ]) + ->recordActions([ + EditAction::make(), + DeleteAction::make(), + RestoreAction::make(), + ForceDeleteAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + RestoreBulkAction::make(), + ForceDeleteBulkAction::make(), + ]), + ]) + ->defaultSort('created_at', 'desc'); + } +} diff --git a/app/Models/Setting.php b/app/Models/Setting.php new file mode 100644 index 0000000..a84672b --- /dev/null +++ b/app/Models/Setting.php @@ -0,0 +1,94 @@ + 'boolean', + 'is_active' => 'boolean', + ]; + + protected $dates = [ + 'created_at', + 'updated_at', + 'deleted_at', + ]; + + /** + * Get setting by key + */ + public static function get(string $key, $default = null) + { + $setting = static::where('key', $key) + ->where('is_active', true) + ->first(); + + if (!$setting) { + return $default; + } + + return static::castValue($setting->value, $setting->type); + } + + /** + * Set setting value + */ + public static function set(string $key, $value, string $type = 'string'): void + { + static::updateOrCreate( + ['key' => $key], + [ + 'value' => $value, + 'type' => $type, + 'is_active' => true, + ] + ); + } + + /** + * Cast value based on type + */ + protected static function castValue($value, string $type) + { + return match($type) { + 'boolean' => filter_var($value, FILTER_VALIDATE_BOOLEAN), + 'integer' => (int) $value, + 'float' => (float) $value, + 'array', 'json' => json_decode($value, true), + default => $value, + }; + } + + /** + * Get all settings by group + */ + public static function getGroup(string $group): array + { + return static::where('group', $group) + ->where('is_active', true) + ->pluck('value', 'key') + ->map(function ($value, $key) use ($group) { + $setting = static::where('key', $key)->first(); + return static::castValue($value, $setting->type); + }) + ->toArray(); + } +} diff --git a/database/factories/SettingFactory.php b/database/factories/SettingFactory.php new file mode 100644 index 0000000..eb11ef9 --- /dev/null +++ b/database/factories/SettingFactory.php @@ -0,0 +1,23 @@ + + */ +class SettingFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/seeders/SettingSeeder.php b/database/seeders/SettingSeeder.php new file mode 100644 index 0000000..e750004 --- /dev/null +++ b/database/seeders/SettingSeeder.php @@ -0,0 +1,237 @@ + 'site_name', + 'value' => 'Citrus Platform', + 'type' => 'string', + 'group' => 'general', + 'label' => 'Site Adı', + 'description' => 'Web sitesinin adı', + 'is_public' => true, + 'is_active' => true, + ], + [ + 'key' => 'site_description', + 'value' => 'Modern ve güvenilir web platformu', + 'type' => 'text', + 'group' => 'general', + 'label' => 'Site Açıklaması', + 'description' => 'Web sitesinin açıklaması', + 'is_public' => true, + 'is_active' => true, + ], + [ + 'key' => 'site_keywords', + 'value' => 'citrus, platform, web, modern', + 'type' => 'string', + 'group' => 'general', + 'label' => 'Site Anahtar Kelimeleri', + 'description' => 'SEO için anahtar kelimeler', + 'is_public' => true, + 'is_active' => true, + ], + [ + 'key' => 'maintenance_mode', + 'value' => '0', + 'type' => 'boolean', + 'group' => 'general', + 'label' => 'Bakım Modu', + 'description' => 'Site bakım modunda mı?', + 'is_public' => false, + 'is_active' => true, + ], + + // E-posta Ayarları + [ + 'key' => 'mail_from_address', + 'value' => 'noreply@citrus.truncgil.com', + 'type' => 'string', + 'group' => 'email', + 'label' => 'E-posta Gönderen Adres', + 'description' => 'Sistem e-postalarının gönderileceği adres', + 'is_public' => false, + 'is_active' => true, + ], + [ + 'key' => 'mail_from_name', + 'value' => 'Citrus Platform', + 'type' => 'string', + 'group' => 'email', + 'label' => 'E-posta Gönderen İsim', + 'description' => 'Sistem e-postalarının gönderen adı', + 'is_public' => false, + 'is_active' => true, + ], + [ + 'key' => 'mail_notifications_enabled', + 'value' => '1', + 'type' => 'boolean', + 'group' => 'email', + 'label' => 'E-posta Bildirimleri Aktif', + 'description' => 'E-posta bildirimleri gönderilsin mi?', + 'is_public' => false, + 'is_active' => true, + ], + + // SEO Ayarları + [ + 'key' => 'seo_meta_title', + 'value' => 'Citrus Platform - Modern Web Platformu', + 'type' => 'string', + 'group' => 'seo', + 'label' => 'SEO Meta Başlık', + 'description' => 'Varsayılan SEO meta başlığı', + 'is_public' => true, + 'is_active' => true, + ], + [ + 'key' => 'seo_meta_description', + 'value' => 'Modern ve güvenilir web platformu', + 'type' => 'text', + 'group' => 'seo', + 'label' => 'SEO Meta Açıklama', + 'description' => 'Varsayılan SEO meta açıklaması', + 'is_public' => true, + 'is_active' => true, + ], + [ + 'key' => 'seo_google_analytics', + 'value' => '', + 'type' => 'string', + 'group' => 'seo', + 'label' => 'Google Analytics ID', + 'description' => 'Google Analytics takip kodu', + 'is_public' => false, + 'is_active' => true, + ], + + // Sosyal Medya Ayarları + [ + 'key' => 'social_facebook', + 'value' => '', + 'type' => 'string', + 'group' => 'social', + 'label' => 'Facebook URL', + 'description' => 'Facebook sayfa URL\'si', + 'is_public' => true, + 'is_active' => true, + ], + [ + 'key' => 'social_twitter', + 'value' => '', + 'type' => 'string', + 'group' => 'social', + 'label' => 'Twitter URL', + 'description' => 'Twitter profil URL\'si', + 'is_public' => true, + 'is_active' => true, + ], + [ + 'key' => 'social_instagram', + 'value' => '', + 'type' => 'string', + 'group' => 'social', + 'label' => 'Instagram URL', + 'description' => 'Instagram profil URL\'si', + 'is_public' => true, + 'is_active' => true, + ], + [ + 'key' => 'social_linkedin', + 'value' => '', + 'type' => 'string', + 'group' => 'social', + 'label' => 'LinkedIn URL', + 'description' => 'LinkedIn profil URL\'si', + 'is_public' => true, + 'is_active' => true, + ], + + // Güvenlik Ayarları + [ + 'key' => 'security_max_login_attempts', + 'value' => '5', + 'type' => 'integer', + 'group' => 'security', + 'label' => 'Maksimum Giriş Denemesi', + 'description' => 'Kullanıcı hesabının kilitlenmesi için maksimum giriş denemesi', + 'is_public' => false, + 'is_active' => true, + ], + [ + 'key' => 'security_session_timeout', + 'value' => '120', + 'type' => 'integer', + 'group' => 'security', + 'label' => 'Oturum Zaman Aşımı (dakika)', + 'description' => 'Kullanıcı oturumunun zaman aşımına uğrayacağı süre', + 'is_public' => false, + 'is_active' => true, + ], + [ + 'key' => 'security_2fa_enabled', + 'value' => '0', + 'type' => 'boolean', + 'group' => 'security', + 'label' => 'İki Faktörlü Doğrulama', + 'description' => 'İki faktörlü doğrulama aktif mi?', + 'is_public' => false, + 'is_active' => true, + ], + + // Bildirim Ayarları + [ + 'key' => 'notification_email_enabled', + 'value' => '1', + 'type' => 'boolean', + 'group' => 'notification', + 'label' => 'E-posta Bildirimleri', + 'description' => 'E-posta bildirimleri gönderilsin mi?', + 'is_public' => false, + 'is_active' => true, + ], + [ + 'key' => 'notification_sms_enabled', + 'value' => '0', + 'type' => 'boolean', + 'group' => 'notification', + 'label' => 'SMS Bildirimleri', + 'description' => 'SMS bildirimleri gönderilsin mi?', + 'is_public' => false, + 'is_active' => true, + ], + [ + 'key' => 'notification_push_enabled', + 'value' => '0', + 'type' => 'boolean', + 'group' => 'notification', + 'label' => 'Push Bildirimleri', + 'description' => 'Push bildirimleri gönderilsin mi?', + 'is_public' => false, + 'is_active' => true, + ], + ]; + + foreach ($settings as $setting) { + Setting::updateOrCreate( + ['key' => $setting['key']], + $setting + ); + } + } +}