🐛 KRİTİK BUG 11 Ocak 2026

Dual Listbox Arama Sorunu

Soldan sağa taşınan item'lar arama yapılınca kayboluyor

📝 Basit Anlatım (Herkes İçin)

Sorun nedir?
Admin panelde radyo, playlist, sektör gibi yerlerde "soldan sağa seçim yapma" sistemi var. Kullanıcı sol taraftan bir şey seçip sağ tarafa taşıyor. Sonra başka bir şey aramak için arama kutusuna yazıyor. O anda daha önce seçtiği şeyler kayboluyor!

Örnek senaryo:
Radyo oluştururken 5 tane playlist seçtin → Sağ tarafa taşıdın → Tamam güzel!
Sonra 6. playlist'i bulmak için arama yaptın → İlk 5 playlist kayboldu!

Neden önemli?
Kullanıcı radyo oluştururken 20 tane playlist seçmiş olabilir. Aramak istediğinde hepsi uçuyor ve baştan seçmek zorunda kalıyor. Bu kullanılamaz bir deneyim.

Etkilenen Sayfalar:

  • admin/muzibu/radio/manage - Radyo yönetimi (playlist seçimi)
  • admin/muzibu/playlist/manage - Playlist yönetimi (sektör, radyo, tür, kurum seçimi)
  • admin/muzibu/song/manage - Şarkı yönetimi (muhtemelen varsa)
  • admin/muzibu/sector/manage - Sektör yönetimi (muhtemelen varsa)

🔧 Teknik Detaylar (Geliştiriciler İçin)

🎯 Sorunun Kök Nedeni

Dual listbox sistemi hybrid bir yapı:

  • Livewire (Backend): Veritabanından data çekiyor, arama yapıyor
  • JavaScript (Frontend): DOM manipülasyonu yapıyor (soldan sağa taşıma)
  • SORUN: İki sistem senkronize değil!

📊 Hatalı Akış

1️⃣ Kullanıcı "Playlist A" seçti
   → JavaScript: DOM'da soldan sağa taşıdı ✅
   → JavaScript: @this.set('inputs.playlist_ids', [1]) gönderdi ✅
   → Livewire: $inputs['playlist_ids'] = [1] ✅

2️⃣ Kullanıcı "Playlist B"yi aramak için "search" yazdı
   → Livewire: $playlistSearch = "search" değişti
   → Livewire: activePlaylists() computed property tetiklendi
   → Livewire: Sadece "search" içeren playlist'leri getir
   → Livewire: RENDER! (Blade yeniden render edildi)

3️⃣ Blade Render Sırasında:
   @foreach($this->activePlaylists as $playlist)
       // activePlaylists() sadece arama sonuçlarını döndürüyor
       // "Playlist A" arama sonucunda YOK!
       // Blade "Playlist A"yı görmüyor
       // Sağ tarafta render edilemiyor
   @endforeach

4️⃣ SONUÇ:
   ❌ "Playlist A" kayboldu! (Arama sonucunda olmadığı için render edilmedi)
   ❌ Kullanıcı "Playlist A"yı görmüyor
   ❌ Tekrar aramak zorunda kalıyor

📄 Mevcut Kod (Hatalı)

Dosya: RadioManageComponent.php

#[Computed] public function activePlaylists() { $query = \Modules\Muzibu\App\Models\Playlist::where('is_active', true); // ❌ SORUN: Sadece arama sonuçlarını döndürüyor if (!empty($this->playlistSearch)) { $locale = app()->getLocale(); $search = strtolower($this->playlistSearch); $query->where(function($q) use ($search, $locale) { $q->whereRaw("LOWER(JSON_UNQUOTE(JSON_EXTRACT(title, ?))) LIKE ?", ["$.{$locale}", "%{$search}%"]); }); } return $query->orderBy('title->tr')->get(); // ❌ Seçili item'lar arama sonucunda yoksa render edilmiyor! }

Dosya: radio-manage-component.blade.php

<!-- Sol Taraf: Mevcut Listeler --> <div class="listbox" id="available-playlists"> @foreach($this->activePlaylists as $playlist) @if(!in_array($playlist->playlist_id, $inputs['playlist_ids'] ?? [])) <div class="listbox-item">{{ $playlist->title }}</div> @endif @endforeach </div> <!-- Sağ Taraf: Seçilen Listeler --> <div class="listbox" id="selected-playlists"> @foreach($this->activePlaylists as $playlist) @if(in_array($playlist->playlist_id, $inputs['playlist_ids'] ?? [])) <!-- ❌ SORUN: activePlaylists'te yoksa burası boş kalıyor! --> <div class="listbox-item">{{ $playlist->title }}</div> @endif @endforeach </div>

✨ Çözüm Stratejisi

🎯 Ana Çözüm

activePlaylists() computed property'sini iki ayrı property'ye böl:

  • availablePlaylists() → Arama sonuçları (seçilmemiş olanlar)
  • selectedPlaylists() → Seçili olanlar (her zaman render et)

📝 Yeni Kod (Düzeltilmiş)

Backend: RadioManageComponent.php

// ✅ ÇÖZÜM 1: Seçilmemiş olanlar (arama sonuçları) #[Computed] public function availablePlaylists() { $query = \Modules\Muzibu\App\Models\Playlist::where('is_active', true); // Seçilenleri hariç tut if (!empty($this->inputs['playlist_ids'])) { $query->whereNotIn('playlist_id', $this->inputs['playlist_ids']); } // Arama filtresi if (!empty($this->playlistSearch)) { $locale = app()->getLocale(); $search = strtolower($this->playlistSearch); $query->where(function($q) use ($search, $locale) { $q->whereRaw("LOWER(JSON_UNQUOTE(JSON_EXTRACT(title, ?))) LIKE ?", ["$.{$locale}", "%{$search}%"]); }); } return $query->orderBy('title->tr')->get(); } // ✅ ÇÖZÜM 2: Seçili olanlar (her zaman göster) #[Computed] public function selectedPlaylists() { if (empty($this->inputs['playlist_ids'])) { return collect([]); } // Seçili playlist'leri ID'ye göre getir return \Modules\Muzibu\App\Models\Playlist::whereIn('playlist_id', $this->inputs['playlist_ids']) ->orderBy('title->tr') ->get(); }

Frontend: radio-manage-component.blade.php

<!-- Sol Taraf: Mevcut Listeler (Seçilmemiş + Arama Sonuçları) --> <div class="listbox" id="available-playlists"> @foreach($this->availablePlaylists as $playlist) <div class="listbox-item" data-value="{{ $playlist->playlist_id }}"> {{ $playlist->getTranslated('title', app()->getLocale()) }} </div> @endforeach </div> <!-- Sağ Taraf: Seçilen Listeler (Her Zaman Göster) --> <div class="listbox" id="selected-playlists"> @foreach($this->selectedPlaylists as $playlist) <!-- ✅ ÇÖZÜM: Seçililer her zaman render ediliyor --> <div class="listbox-item" data-value="{{ $playlist->playlist_id }}"> {{ $playlist->getTranslated('title', app()->getLocale()) }} </div> @endforeach </div>

🎁 Çözümün Avantajları

  • ✅ Seçililer kaybolmaz: Her zaman sağ tarafta render edilir
  • ✅ Arama çalışır: Sol taraf sadece arama sonuçlarını gösterir
  • ✅ Performans: Seçilmemişler filtrelenir, gereksiz data yok
  • ✅ UX: Kullanıcı istediği kadar arama yapabilir, seçimleri korunur

📁 Düzeltilmesi Gereken Dosyalar

🎵 Radio Modülü

  • Modules/Muzibu/App/Http/Livewire/Admin/RadioManageComponent.php
    • activePlaylists()availablePlaylists() + selectedPlaylists()
  • Modules/Muzibu/resources/views/admin/livewire/radio-manage-component.blade.php
    • $this->activePlaylists$this->availablePlaylists ve $this->selectedPlaylists

📋 Playlist Modülü

  • Modules/Muzibu/App/Http/Livewire/Admin/PlaylistManageComponent.php
    • activeSectors()availableSectors() + selectedSectors()
    • activeRadios()availableRadios() + selectedRadios()
    • activeGenres()availableGenres() + selectedGenres()
    • activeCorporates()availableCorporates() + selectedCorporates()
  • Modules/Muzibu/resources/views/admin/livewire/playlist-manage-component.blade.php
    • • Tüm $this->active* referanslarını $this->available* ve $this->selected* olarak ayır

🔍 Kontrol Edilmesi Gereken Diğer Modüller

  • SongManageComponent.php (varsa playlist/album/artist seçimi)
  • SectorManageComponent.php (varsa playlist seçimi)
  • AlbumManageComponent.php (varsa song seçimi)

💡 Tüm dual listbox kullanan component'lerde aynı pattern uygulanmalı!

🧪 Test Senaryosu

1️⃣ Test Adımları

  1. Admin panel → Radyo oluştur sayfasına git
  2. Sol taraftan 5 playlist seç → Sağ tarafa taşı
  3. Arama kutusuna "test" yaz
  4. ✅ BEKLENEN: Sağ tarafta 5 playlist hala görünüyor
  5. ❌ MEVCUT: Sağ taraf boşaldı (5 playlist kayboldu)
  6. Aramayı temizle
  7. ✅ BEKLENEN: Sol tarafta tüm playlist'ler, sağ tarafta 5 seçili

2️⃣ Test Sayfaları

  • muzibu.com/admin/muzibu/radio/manage
  • muzibu.com/admin/muzibu/playlist/manage
  • muzibu.com/admin/muzibu/song/manage (varsa)

3️⃣ Başarı Kriterleri

  • Arama yapılınca seçili item'lar kaybolmamalı
  • Sol taraf sadece seçilmemiş item'ları göstermeli
  • Sağ taraf her zaman seçili item'ları göstermeli
  • Kaydet butonuna basınca seçili item'lar database'e yazılmalı