mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 01:01:24 +00:00
Update sorting
This commit is contained in:
@@ -252,6 +252,7 @@ class ReleaseBrowseService
|
||||
'size' => 'size',
|
||||
'files' => 'totalpart',
|
||||
'stats' => 'grabs',
|
||||
'added' => 'adddate',
|
||||
default => 'postdate',
|
||||
};
|
||||
|
||||
@@ -272,6 +273,8 @@ class ReleaseBrowseService
|
||||
'cat_desc',
|
||||
'posted_asc',
|
||||
'posted_desc',
|
||||
'added_asc',
|
||||
'added_desc',
|
||||
'size_asc',
|
||||
'size_desc',
|
||||
'files_asc',
|
||||
|
||||
@@ -1167,6 +1167,7 @@ class ReleaseSearchService
|
||||
'size' => 'size',
|
||||
'files' => 'totalpart',
|
||||
'stats' => 'grabs',
|
||||
'added' => 'adddate',
|
||||
default => 'postdate',
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class SortDropdown extends Component
|
||||
{
|
||||
/**
|
||||
* The current sort value.
|
||||
*/
|
||||
public string $currentSort;
|
||||
|
||||
/**
|
||||
* Available sort options.
|
||||
*/
|
||||
public array $sortOptions;
|
||||
|
||||
/**
|
||||
* The base URL for sorting links.
|
||||
*/
|
||||
public string $baseUrl;
|
||||
|
||||
/**
|
||||
* Query parameters to preserve.
|
||||
*/
|
||||
public array $queryParams;
|
||||
|
||||
/**
|
||||
* Current sort label.
|
||||
*/
|
||||
public string $currentLabel;
|
||||
|
||||
/**
|
||||
* Current sort icon.
|
||||
*/
|
||||
public string $currentIcon;
|
||||
|
||||
/**
|
||||
* Pre-computed sort URLs.
|
||||
*/
|
||||
public array $sortUrls;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct(
|
||||
?string $currentSort = null,
|
||||
?array $options = null,
|
||||
?string $baseUrl = null,
|
||||
?array $queryParams = null
|
||||
) {
|
||||
$this->currentSort = $currentSort ?? request('ob', 'posted_desc');
|
||||
$this->baseUrl = $baseUrl ?? request()->url();
|
||||
$this->queryParams = $queryParams ?? request()->except(['ob', 'page']);
|
||||
|
||||
// Default sort options for releases
|
||||
$this->sortOptions = $options ?? [
|
||||
'posted_desc' => ['label' => 'Posted (Newest)', 'icon' => 'fa-calendar-alt'],
|
||||
'posted_asc' => ['label' => 'Posted (Oldest)', 'icon' => 'fa-calendar-alt'],
|
||||
'added_desc' => ['label' => 'Added (Newest)', 'icon' => 'fa-clock'],
|
||||
'added_asc' => ['label' => 'Added (Oldest)', 'icon' => 'fa-clock'],
|
||||
'name_asc' => ['label' => 'Name (A-Z)', 'icon' => 'fa-font'],
|
||||
'name_desc' => ['label' => 'Name (Z-A)', 'icon' => 'fa-font'],
|
||||
'size_desc' => ['label' => 'Size (Largest)', 'icon' => 'fa-hdd'],
|
||||
'size_asc' => ['label' => 'Size (Smallest)', 'icon' => 'fa-hdd'],
|
||||
'files_desc' => ['label' => 'Files (Most)', 'icon' => 'fa-file'],
|
||||
'files_asc' => ['label' => 'Files (Least)', 'icon' => 'fa-file'],
|
||||
'stats_desc' => ['label' => 'Grabs (Most)', 'icon' => 'fa-download'],
|
||||
'stats_asc' => ['label' => 'Grabs (Least)', 'icon' => 'fa-download'],
|
||||
];
|
||||
|
||||
// Pre-compute current label and icon
|
||||
$this->currentLabel = $this->sortOptions[$this->currentSort]['label'] ?? 'Posted (Newest)';
|
||||
$this->currentIcon = $this->sortOptions[$this->currentSort]['icon'] ?? 'fa-calendar-alt';
|
||||
|
||||
// Pre-compute all sort URLs
|
||||
$this->sortUrls = [];
|
||||
foreach ($this->sortOptions as $sortKey => $sortData) {
|
||||
$params = array_merge($this->queryParams, ['ob' => $sortKey]);
|
||||
$this->sortUrls[$sortKey] = $this->baseUrl.'?'.http_build_query($params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.sort-dropdown');
|
||||
}
|
||||
}
|
||||
@@ -5667,3 +5667,63 @@ function initAutocompleteInput(inputId, dropdownId, formId, itemClass) {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Sort Dropdown Component
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Initialize sort dropdown functionality
|
||||
* Handles the custom dropdown for sorting releases on browse/search pages
|
||||
*/
|
||||
function initSortDropdowns() {
|
||||
document.querySelectorAll('.sort-dropdown').forEach(function(dropdown) {
|
||||
var toggle = dropdown.querySelector('.sort-dropdown-toggle');
|
||||
var menu = dropdown.querySelector('.sort-dropdown-menu');
|
||||
var chevron = dropdown.querySelector('.sort-dropdown-chevron');
|
||||
|
||||
if (!toggle || !menu) return;
|
||||
|
||||
// Skip if already initialized
|
||||
if (toggle.hasAttribute('data-sort-initialized')) return;
|
||||
toggle.setAttribute('data-sort-initialized', 'true');
|
||||
|
||||
toggle.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
var isOpen = !menu.classList.contains('hidden');
|
||||
|
||||
// Close all other dropdowns first
|
||||
document.querySelectorAll('.sort-dropdown-menu').forEach(function(m) {
|
||||
m.classList.add('hidden');
|
||||
});
|
||||
document.querySelectorAll('.sort-dropdown-chevron').forEach(function(c) {
|
||||
c.classList.remove('rotate-180');
|
||||
});
|
||||
|
||||
if (!isOpen) {
|
||||
menu.classList.remove('hidden');
|
||||
if (chevron) chevron.classList.add('rotate-180');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Close dropdowns when clicking outside (single global listener)
|
||||
if (!window._sortDropdownOutsideListenerAdded) {
|
||||
window._sortDropdownOutsideListenerAdded = true;
|
||||
document.addEventListener('click', function(e) {
|
||||
var isInsideDropdown = e.target.closest('.sort-dropdown');
|
||||
if (!isInsideDropdown) {
|
||||
document.querySelectorAll('.sort-dropdown-menu').forEach(function(m) {
|
||||
m.classList.add('hidden');
|
||||
});
|
||||
document.querySelectorAll('.sort-dropdown-chevron').forEach(function(c) {
|
||||
c.classList.remove('rotate-180');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize sort dropdowns on DOMContentLoaded
|
||||
document.addEventListener('DOMContentLoaded', initSortDropdowns);
|
||||
|
||||
|
||||
@@ -93,16 +93,7 @@
|
||||
|
||||
<!-- Right Section - Sort Options -->
|
||||
<div class="flex items-center justify-end">
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-sm text-gray-600 dark:text-gray-400 dark:text-gray-400">Sort by:</label>
|
||||
<select class="border border-gray-300 dark:border-gray-600 dark:border-gray-600 bg-white dark:bg-gray-800 dark:bg-gray-800 text-gray-900 dark:text-gray-100 dark:text-gray-100 rounded px-3 py-1 text-sm focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400" data-redirect-on-change>
|
||||
<option value="{{ $orderbyposted ?? '#' }}" {{ request('ob') == 'posted_desc' ? 'selected' : '' }}>Posted</option>
|
||||
<option value="{{ $orderbyname ?? '#' }}" {{ request('ob') == 'name_asc' ? 'selected' : '' }}>Name</option>
|
||||
<option value="{{ $orderbysize ?? '#' }}" {{ request('ob') == 'size_desc' ? 'selected' : '' }}>Size</option>
|
||||
<option value="{{ $orderbyfiles ?? '#' }}" {{ request('ob') == 'files_desc' ? 'selected' : '' }}>Files</option>
|
||||
<option value="{{ $orderbystats ?? '#' }}" {{ request('ob') == 'stats_desc' ? 'selected' : '' }}>Stats</option>
|
||||
</select>
|
||||
</div>
|
||||
<x-sort-dropdown />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<div class="relative inline-block sort-dropdown">
|
||||
<button
|
||||
type="button"
|
||||
class="sort-dropdown-toggle inline-flex items-center gap-2 px-3 py-2 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 transition shadow-sm"
|
||||
>
|
||||
<i class="fas {{ $currentIcon }} text-gray-500 dark:text-gray-300"></i>
|
||||
<span>Sort: {{ $currentLabel }}</span>
|
||||
<i class="fas fa-chevron-down text-xs text-gray-400 dark:text-gray-300 transition-transform sort-dropdown-chevron"></i>
|
||||
</button>
|
||||
|
||||
<div class="sort-dropdown-menu hidden absolute right-0 z-50 mt-2 w-56 origin-top-right rounded-lg bg-white dark:bg-gray-700 shadow-lg border border-gray-200 dark:border-gray-600 focus:outline-none">
|
||||
<div class="py-1 max-h-80 overflow-y-auto">
|
||||
@foreach($sortOptions as $sortKey => $sortData)
|
||||
<a
|
||||
href="{{ $sortUrls[$sortKey] }}"
|
||||
class="flex items-center gap-3 px-4 py-2 text-sm transition {{ $currentSort === $sortKey ? 'bg-blue-100 dark:bg-blue-600 text-blue-800 dark:text-white font-medium' : 'text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600' }}"
|
||||
>
|
||||
<i class="fas {{ $sortData['icon'] }} w-4 text-center {{ $currentSort === $sortKey ? 'text-blue-600 dark:text-blue-200' : 'text-gray-400 dark:text-gray-400' }}"></i>
|
||||
<span class="flex-1">{{ $sortData['label'] }}</span>
|
||||
@if($currentSort === $sortKey)
|
||||
<i class="fas fa-check text-blue-600 dark:text-blue-200"></i>
|
||||
@endif
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -157,6 +157,11 @@
|
||||
<button type="button" class="nzb_multi_operations_cart px-3 py-1 bg-blue-600 dark:bg-blue-700 text-white rounded hover:bg-blue-700 dark:hover:bg-blue-800 transition text-sm" title="Send to Download Basket">
|
||||
<i class="fa fa-shopping-basket"></i>
|
||||
</button>
|
||||
@if(auth()->check() && auth()->user()->hasRole('Admin'))
|
||||
<button type="button" class="nzb_multi_operations_delete px-3 py-1 bg-red-600 dark:bg-red-700 text-white rounded hover:bg-red-700 dark:hover:bg-red-800 transition text-sm" title="Delete">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
@@ -173,28 +178,9 @@
|
||||
</div>
|
||||
|
||||
<!-- Sort Options -->
|
||||
<div class="mb-4 bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<span class="block sm:inline font-medium text-gray-700 dark:text-gray-300 mb-2 sm:mb-0 sm:mr-2 text-sm">Sort by:</span>
|
||||
<div class="flex flex-wrap items-center gap-2 text-sm">
|
||||
@php
|
||||
$currentSort = request('ob', 'posted_desc');
|
||||
$sortOptions = [
|
||||
'posted_desc' => ['label' => 'Posted Date ↓', 'icon' => 'fa-calendar'],
|
||||
'posted_asc' => ['label' => 'Posted Date ↑', 'icon' => 'fa-calendar'],
|
||||
'size_desc' => ['label' => 'Size ↓', 'icon' => 'fa-hdd'],
|
||||
'size_asc' => ['label' => 'Size ↑', 'icon' => 'fa-hdd'],
|
||||
'name_asc' => ['label' => 'Name ↑', 'icon' => 'fa-font'],
|
||||
'name_desc' => ['label' => 'Name ↓', 'icon' => 'fa-font'],
|
||||
];
|
||||
$queryParams = request()->except('ob');
|
||||
@endphp
|
||||
@foreach($sortOptions as $sortKey => $sortData)
|
||||
<a href="{{ route('search', array_merge($queryParams, ['ob' => $sortKey])) }}"
|
||||
class="px-3 py-1.5 rounded {{ $currentSort === $sortKey ? 'bg-blue-600 text-white' : 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700' }} transition border border-gray-300 dark:border-gray-600">
|
||||
<i class="fas {{ $sortData['icon'] }} mr-1"></i>{{ $sortData['label'] }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="mb-4 bg-gray-50 dark:bg-gray-900 rounded-lg p-4 flex items-center justify-between">
|
||||
<span class="font-medium text-gray-700 dark:text-gray-300 text-sm">Sort results:</span>
|
||||
<x-sort-dropdown />
|
||||
</div>
|
||||
|
||||
<!-- Desktop Table View (hidden on mobile) -->
|
||||
@@ -224,6 +210,12 @@
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<a href="{{ url('/details/' . $result->guid) }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 font-medium break-words break-all">{{ $result->searchname }}</a>
|
||||
@if(!empty($result->failed) && $result->failed > 0)
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200"
|
||||
title="{{ $result->failed }} user(s) reported download failure">
|
||||
<i class="fas fa-exclamation-triangle mr-1"></i> Failed ({{ $result->failed }})
|
||||
</span>
|
||||
@endif
|
||||
@if(isset($result->haspreview) && $result->haspreview == 1)
|
||||
<button type="button"
|
||||
class="preview-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 hover:bg-purple-200 dark:hover:bg-purple-800 transition cursor-pointer"
|
||||
@@ -240,6 +232,14 @@
|
||||
<i class="fas fa-images mr-1"></i> Sample
|
||||
</button>
|
||||
@endif
|
||||
@if(isset($result->reid) && $result->reid != null)
|
||||
<button type="button"
|
||||
class="mediainfo-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 hover:bg-blue-200 dark:hover:bg-blue-800 transition cursor-pointer"
|
||||
data-release-id="{{ $result->id }}"
|
||||
title="View media info">
|
||||
<i class="fas fa-info-circle mr-1"></i> Media Info
|
||||
</button>
|
||||
@endif
|
||||
@if(isset($result->nfostatus) && $result->nfostatus == 1)
|
||||
<button type="button"
|
||||
class="nfo-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 hover:bg-yellow-200 dark:hover:bg-yellow-800 transition cursor-pointer"
|
||||
@@ -287,7 +287,16 @@
|
||||
{{ number_format($result->size / 1073741824, 2) }} GB
|
||||
</td>
|
||||
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $result->totalpart ?? 0 }}
|
||||
@if($result->totalpart > 0)
|
||||
<button type="button"
|
||||
class="filelist-badge text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 font-medium cursor-pointer hover:underline"
|
||||
data-guid="{{ $result->guid }}"
|
||||
title="View file list">
|
||||
{{ $result->totalpart ?? 0 }}
|
||||
</button>
|
||||
@else
|
||||
{{ $result->totalpart ?? 0 }}
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-3 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
|
||||
<div class="flex items-center gap-2">
|
||||
@@ -306,6 +315,13 @@
|
||||
<a href="#" class="add-to-cart px-2 py-1 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded hover:bg-gray-300 dark:hover:bg-gray-600 transition text-sm" data-guid="{{ $result->guid }}" title="Add to Cart">
|
||||
<i class="icon_cart fa fa-shopping-basket"></i>
|
||||
</a>
|
||||
@if(!empty($result->imdbid) && $result->imdbid != '0' && $result->imdbid != 0 && $result->imdbid != '0000000')
|
||||
<a href="{{ url('/mymovies?id=add&imdb=' . $result->imdbid) }}"
|
||||
class="px-2 py-1 bg-purple-600 dark:bg-purple-700 text-white rounded hover:bg-purple-700 dark:hover:bg-purple-800 transition text-sm"
|
||||
title="Add to My Movies">
|
||||
<i class="fa fa-film"></i>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -325,6 +341,12 @@
|
||||
<a href="{{ url('/details/' . $result->guid) }}" class="text-lg font-medium text-blue-600 dark:text-blue-400 hover:text-blue-800 break-words break-all">
|
||||
{{ $result->searchname }}
|
||||
</a>
|
||||
@if(!empty($result->failed) && $result->failed > 0)
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200"
|
||||
title="{{ $result->failed }} user(s) reported download failure">
|
||||
<i class="fas fa-exclamation-triangle mr-1"></i> Failed ({{ $result->failed }})
|
||||
</span>
|
||||
@endif
|
||||
@if(isset($result->haspreview) && $result->haspreview == 1)
|
||||
<button type="button"
|
||||
class="preview-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 hover:bg-purple-200 dark:hover:bg-purple-800 transition cursor-pointer"
|
||||
@@ -341,6 +363,14 @@
|
||||
<i class="fas fa-images mr-1"></i> Sample
|
||||
</button>
|
||||
@endif
|
||||
@if(isset($result->reid) && $result->reid != null)
|
||||
<button type="button"
|
||||
class="mediainfo-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 hover:bg-blue-200 dark:hover:bg-blue-800 transition cursor-pointer"
|
||||
data-release-id="{{ $result->id }}"
|
||||
title="View media info">
|
||||
<i class="fas fa-info-circle mr-1"></i> Media Info
|
||||
</button>
|
||||
@endif
|
||||
@if(isset($result->nfostatus) && $result->nfostatus == 1)
|
||||
<button type="button"
|
||||
class="nfo-badge inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200 hover:bg-yellow-200 dark:hover:bg-yellow-800 transition cursor-pointer"
|
||||
@@ -392,6 +422,13 @@
|
||||
title="Add to Cart">
|
||||
<i class="icon_cart fa fa-shopping-basket"></i>
|
||||
</a>
|
||||
@if(!empty($result->imdbid) && $result->imdbid != '0' && $result->imdbid != 0 && $result->imdbid != '0000000')
|
||||
<a href="{{ url('/mymovies?id=add&imdb=' . $result->imdbid) }}"
|
||||
class="px-2 py-1 bg-purple-600 dark:bg-purple-700 text-white rounded hover:bg-purple-700 dark:hover:bg-purple-800 transition text-sm"
|
||||
title="Add to My Movies">
|
||||
<i class="fa fa-film"></i>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -436,6 +473,46 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MediaInfo Modal -->
|
||||
<div id="mediainfoModal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center p-4 modal-hidden modal-z-index">
|
||||
<div class="relative max-w-4xl w-full bg-white dark:bg-gray-800 rounded-lg shadow-2xl max-h-[90vh] overflow-hidden">
|
||||
<div class="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 flex items-center">
|
||||
<i class="fas fa-info-circle mr-2 text-blue-600 dark:text-blue-400"></i> Media Information
|
||||
</h3>
|
||||
<button type="button" data-close-mediainfo-modal class="text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 text-2xl font-bold">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="mediainfoContent" class="p-6 overflow-y-auto modal-content-scroll">
|
||||
<div class="text-center py-8">
|
||||
<i class="fas fa-spinner fa-spin text-3xl text-blue-600 dark:text-blue-400"></i>
|
||||
<p class="text-gray-600 dark:text-gray-400 mt-2">Loading media information...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File List Modal -->
|
||||
<div id="filelistModal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center p-4 modal-hidden modal-z-index">
|
||||
<div class="relative max-w-4xl w-full bg-white dark:bg-gray-800 rounded-lg shadow-2xl max-h-[90vh] overflow-hidden">
|
||||
<div class="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 flex items-center">
|
||||
<i class="fas fa-file-archive mr-2 text-green-600 dark:text-green-400"></i> File List
|
||||
</h3>
|
||||
<button type="button" data-close-filelist-modal class="text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 text-2xl font-bold">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="filelistContent" class="p-6 overflow-y-auto modal-content-scroll">
|
||||
<div class="text-center py-8">
|
||||
<i class="fas fa-spinner fa-spin text-3xl text-green-600 dark:text-green-400"></i>
|
||||
<p class="text-gray-600 dark:text-gray-400 mt-2">Loading file list...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- NFO Modal -->
|
||||
@include('partials.nfo-modal')
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Services\Releases\ReleaseBrowseService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ReleaseBrowseServiceTest extends TestCase
|
||||
{
|
||||
private ReleaseBrowseService $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->service = new ReleaseBrowseService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrdering returns all expected ordering options.
|
||||
*/
|
||||
public function test_get_browse_ordering_returns_all_options(): void
|
||||
{
|
||||
$ordering = $this->service->getBrowseOrdering();
|
||||
|
||||
$this->assertIsArray($ordering);
|
||||
$this->assertContains('name_asc', $ordering);
|
||||
$this->assertContains('name_desc', $ordering);
|
||||
$this->assertContains('cat_asc', $ordering);
|
||||
$this->assertContains('cat_desc', $ordering);
|
||||
$this->assertContains('posted_asc', $ordering);
|
||||
$this->assertContains('posted_desc', $ordering);
|
||||
$this->assertContains('added_asc', $ordering);
|
||||
$this->assertContains('added_desc', $ordering);
|
||||
$this->assertContains('size_asc', $ordering);
|
||||
$this->assertContains('size_desc', $ordering);
|
||||
$this->assertContains('files_asc', $ordering);
|
||||
$this->assertContains('files_desc', $ordering);
|
||||
$this->assertContains('stats_asc', $ordering);
|
||||
$this->assertContains('stats_desc', $ordering);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'added' to 'adddate' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_added_to_adddate(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('added_desc');
|
||||
|
||||
$this->assertEquals(['adddate', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly handles added ascending.
|
||||
*/
|
||||
public function test_get_browse_order_handles_added_ascending(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('added_asc');
|
||||
|
||||
$this->assertEquals(['adddate', 'asc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'posted' to 'postdate' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_posted_to_postdate(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('posted_desc');
|
||||
|
||||
$this->assertEquals(['postdate', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder defaults to postdate desc for empty string.
|
||||
*/
|
||||
public function test_get_browse_order_defaults_to_postdate_desc(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('');
|
||||
|
||||
$this->assertEquals(['postdate', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'name' to 'searchname' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_name_to_searchname(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('name_asc');
|
||||
|
||||
$this->assertEquals(['searchname', 'asc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'size' to 'size' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_size_correctly(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('size_desc');
|
||||
|
||||
$this->assertEquals(['size', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'files' to 'totalpart' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_files_to_totalpart(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('files_asc');
|
||||
|
||||
$this->assertEquals(['totalpart', 'asc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'stats' to 'grabs' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_stats_to_grabs(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('stats_desc');
|
||||
|
||||
$this->assertEquals(['grabs', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'cat' to 'categories_id' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_cat_to_categories_id(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('cat_asc');
|
||||
|
||||
$this->assertEquals(['categories_id', 'asc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder handles unknown order type by defaulting to postdate.
|
||||
*/
|
||||
public function test_get_browse_order_defaults_unknown_to_postdate(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('unknown_desc');
|
||||
|
||||
$this->assertEquals(['postdate', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder handles invalid direction by defaulting to desc.
|
||||
*/
|
||||
public function test_get_browse_order_defaults_invalid_direction_to_desc(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('name_invalid');
|
||||
|
||||
$this->assertEquals(['searchname', 'desc'], $result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Services\Releases\ReleaseSearchService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionClass;
|
||||
|
||||
class ReleaseSearchServiceOrderingTest extends TestCase
|
||||
{
|
||||
private ReleaseSearchService $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
// Use reflection to instantiate without constructor dependencies
|
||||
$reflection = new ReflectionClass(ReleaseSearchService::class);
|
||||
$this->service = $reflection->newInstanceWithoutConstructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'added' to 'adddate' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_added_to_adddate(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('added_desc');
|
||||
|
||||
$this->assertEquals(['adddate', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly handles added ascending.
|
||||
*/
|
||||
public function test_get_browse_order_handles_added_ascending(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('added_asc');
|
||||
|
||||
$this->assertEquals(['adddate', 'asc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'posted' to 'postdate' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_posted_to_postdate(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('posted_desc');
|
||||
|
||||
$this->assertEquals(['postdate', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder defaults to postdate desc for empty string.
|
||||
*/
|
||||
public function test_get_browse_order_defaults_to_postdate_desc(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('');
|
||||
|
||||
$this->assertEquals(['postdate', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'name' to 'searchname' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_name_to_searchname(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('name_asc');
|
||||
|
||||
$this->assertEquals(['searchname', 'asc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'size' to 'size' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_size_correctly(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('size_desc');
|
||||
|
||||
$this->assertEquals(['size', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'files' to 'totalpart' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_files_to_totalpart(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('files_asc');
|
||||
|
||||
$this->assertEquals(['totalpart', 'asc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'stats' to 'grabs' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_stats_to_grabs(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('stats_desc');
|
||||
|
||||
$this->assertEquals(['grabs', 'desc'], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that getBrowseOrder correctly maps 'cat' to 'categories_id' column.
|
||||
*/
|
||||
public function test_get_browse_order_maps_cat_to_categories_id(): void
|
||||
{
|
||||
$result = $this->service->getBrowseOrder('cat_asc');
|
||||
|
||||
$this->assertEquals(['categories_id', 'asc'], $result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user