mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 17:01:16 +00:00
Fix views inconsistencies
This commit is contained in:
@@ -95,7 +95,8 @@ if (! function_exists('makeFieldLinks')) {
|
||||
if ($i > 7) {
|
||||
break;
|
||||
}
|
||||
$newArr[] = '<a href="'.url('/'.ucfirst($type).'?'.$field.'='.urlencode($ta)).'" title="'.$ta.'">'.$ta.'</a>';
|
||||
$escaped = e($ta);
|
||||
$newArr[] = '<a href="'.url('/'.ucfirst($type).'?'.$field.'='.urlencode($ta)).'" title="'.$escaped.'">'.$escaped.'</a>';
|
||||
$i++;
|
||||
}
|
||||
|
||||
@@ -693,6 +694,54 @@ if (! function_exists('formatBytes')) {
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('formatDate')) {
|
||||
/**
|
||||
* Format a date as Y-m-d (house date style).
|
||||
*/
|
||||
function formatDate(Carbon|DateTimeInterface|string|null $date): string
|
||||
{
|
||||
if ($date === null || $date === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$date = $date instanceof DateTimeInterface ? $date : Carbon::parse($date);
|
||||
|
||||
return $date->format('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('formatDateTime')) {
|
||||
/**
|
||||
* Format a date as Y-m-d H:i (house datetime style).
|
||||
*/
|
||||
function formatDateTime(Carbon|DateTimeInterface|string|null $date): string
|
||||
{
|
||||
if ($date === null || $date === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$date = $date instanceof DateTimeInterface ? $date : Carbon::parse($date);
|
||||
|
||||
return $date->format('Y-m-d H:i');
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('formatDateTimeSeconds')) {
|
||||
/**
|
||||
* Format a date as Y-m-d H:i:s (house datetime style with seconds).
|
||||
*/
|
||||
function formatDateTimeSeconds(Carbon|DateTimeInterface|string|null $date): string
|
||||
{
|
||||
if ($date === null || $date === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$date = $date instanceof DateTimeInterface ? $date : Carbon::parse($date);
|
||||
|
||||
return $date->format('Y-m-d H:i:s');
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('csp_nonce')) {
|
||||
/**
|
||||
* Generate a CSP nonce for inline scripts
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Country;
|
||||
use App\Models\DnzbFailure;
|
||||
use App\Models\Predb;
|
||||
use App\Models\Release;
|
||||
@@ -109,7 +110,7 @@ class DetailsController extends BasePageController
|
||||
if (Settings::settingValue('trailers_display')) {
|
||||
$trailer = empty($mov['trailer']) ? $this->movieService->getTrailer($data['imdbid']) : $mov['trailer'];
|
||||
if ($trailer) {
|
||||
$mov['trailer'] = sprintf('<iframe width="%d" height="%d" src="%s"></iframe>', Settings::settingValue('trailers_size_x'), Settings::settingValue('trailers_size_y'), $trailer);
|
||||
$mov['trailer'] = sprintf('<iframe width="%d" height="%d" src="%s"></iframe>', Settings::settingValue('trailers_size_x'), Settings::settingValue('trailers_size_y'), e($trailer));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,6 +159,20 @@ class DetailsController extends BasePageController
|
||||
|
||||
$pre = Predb::getForRelease($data['predb_id']);
|
||||
|
||||
// Resolve AniDB country code to a Country model/name here so the view stays query-free
|
||||
$anidbCountryCode = null;
|
||||
if (is_object($AniDBAPIArray)) {
|
||||
$anidbCountryCode = $AniDBAPIArray->country ?? null;
|
||||
} elseif (is_array($AniDBAPIArray)) {
|
||||
$anidbCountryCode = $AniDBAPIArray['country'] ?? null;
|
||||
}
|
||||
$anidbCountryModel = null;
|
||||
$anidbCountryName = null;
|
||||
if (! empty($anidbCountryCode)) {
|
||||
$anidbCountryModel = Country::query()->find($anidbCountryCode);
|
||||
$anidbCountryName = $anidbCountryModel->name ?? $anidbCountryCode;
|
||||
}
|
||||
|
||||
$this->viewData = array_merge($this->viewData, [
|
||||
'release' => $data,
|
||||
'reVideo' => $reVideo,
|
||||
@@ -166,6 +181,8 @@ class DetailsController extends BasePageController
|
||||
'show' => $showInfo,
|
||||
'movie' => $mov,
|
||||
'anidb' => $AniDBAPIArray,
|
||||
'anidbCountryModel' => $anidbCountryModel,
|
||||
'anidbCountryName' => $anidbCountryName,
|
||||
'music' => $mus,
|
||||
'con' => $con,
|
||||
'game' => $game,
|
||||
|
||||
@@ -181,6 +181,11 @@ class ProfileController extends BasePageController
|
||||
}
|
||||
}
|
||||
|
||||
// Keep the composer's cached per-user data in sync with theme changes
|
||||
if ($request->hasAny(['theme_preference', 'color_scheme'])) {
|
||||
Cache::forget('composer_user_'.$userid);
|
||||
}
|
||||
|
||||
// Update timezone preference
|
||||
if ($request->has('timezone')) {
|
||||
$timezoneValue = $request->input('timezone');
|
||||
@@ -305,6 +310,9 @@ class ProfileController extends BasePageController
|
||||
}
|
||||
$user->save();
|
||||
|
||||
// Keep the composer's cached per-user data in sync with theme changes
|
||||
Cache::forget('composer_user_'.$user->id);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'theme_preference' => $user->theme_preference,
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace App\Models;
|
||||
|
||||
use App\Enums\UserRole;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
@@ -201,4 +202,56 @@ class Content extends Model
|
||||
{
|
||||
return $this->contenttype === self::TYPE_INDEX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the content URL: external URLs are used as-is (domains without
|
||||
* protocol get https://), internal paths are prefixed with the site URL.
|
||||
*
|
||||
* @return Attribute<string|null, never>
|
||||
*/
|
||||
protected function resolvedUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function (): ?string {
|
||||
$url = $this->url;
|
||||
|
||||
if (empty($url)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (str_starts_with($url, 'http://') || str_starts_with($url, 'https://')) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
// Domain pattern without protocol (e.g. google.com, chatgpt.ai)
|
||||
if (! str_starts_with($url, '/') && preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z]{2,}/', $url)) {
|
||||
return 'https://'.$url;
|
||||
}
|
||||
|
||||
return url($url);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the content URL points to an external site.
|
||||
*
|
||||
* @return Attribute<bool, never>
|
||||
*/
|
||||
protected function isExternalUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function (): bool {
|
||||
$url = $this->url;
|
||||
|
||||
if (empty($url)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return str_starts_with($url, 'http://')
|
||||
|| str_starts_with($url, 'https://')
|
||||
|| (! str_starts_with($url, '/') && preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z]{2,}/', $url));
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ class AdminDataComposer
|
||||
'loggedin' => $user !== null,
|
||||
'isadmin' => $isNntmuxUser && $user->hasRole('Admin'),
|
||||
'ismod' => $isNntmuxUser && $user->hasRole('Moderator'),
|
||||
'userTheme' => $isNntmuxUser ? ($user->theme_preference ?? 'light') : 'light',
|
||||
'userColorScheme' => $isNntmuxUser ? ($user->color_scheme ?? 'blue') : 'blue',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,12 +97,16 @@ class GlobalDataComposer
|
||||
'ismod' => $userdata->hasRole('Moderator'),
|
||||
'parentcatlist' => $parentcatlist,
|
||||
'header_menu_cat' => request()->input('t', ''),
|
||||
'userTheme' => $userdata->theme_preference ?? 'light',
|
||||
'userColorScheme' => $userdata->color_scheme ?? 'blue',
|
||||
]);
|
||||
} else {
|
||||
$viewData = array_merge($viewData, [
|
||||
'isadmin' => false,
|
||||
'ismod' => false,
|
||||
'loggedin' => false,
|
||||
'userTheme' => 'light',
|
||||
'userColorScheme' => 'blue',
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -130,8 +130,7 @@
|
||||
@if($hasCover)
|
||||
<img src="{{ getImageAssetUrl('anime', $anime['anidbid'] . '-cover', url('/covers/anime/no-cover.jpg'), [(string) $anime['anidbid']]) }}"
|
||||
alt="{{ $anime['title'] }}"
|
||||
class="max-w-full h-auto mx-auto rounded shadow-lg"
|
||||
style="max-height: 400px;">
|
||||
class="max-w-full h-auto mx-auto rounded shadow-lg max-h-[400px]">
|
||||
@else
|
||||
<div class="flex flex-col items-center justify-center py-12 text-gray-400 dark:text-gray-500">
|
||||
<i class="fas fa-dragon text-6xl mb-3"></i>
|
||||
@@ -257,6 +256,6 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -43,20 +43,17 @@
|
||||
</div>
|
||||
|
||||
<!-- AniDB Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cover</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Title</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Type</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Start Date</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">End Date</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Rating</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>Cover</x-admin.th>
|
||||
<x-admin.th>Title</x-admin.th>
|
||||
<x-admin.th>Type</x-admin.th>
|
||||
<x-admin.th>Start Date</x-admin.th>
|
||||
<x-admin.th>End Date</x-admin.th>
|
||||
<x-admin.th>Rating</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@forelse($anidblist as $anime)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@@ -123,28 +120,22 @@
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
@php
|
||||
$anidbEmptyTitle = $animetitle ? 'No anime found for "'.$animetitle.'"' : 'No anime available';
|
||||
@endphp
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">
|
||||
@if($animetitle)
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-search text-4xl mb-3"></i>
|
||||
<p class="text-lg">No anime found for "{{ $animetitle }}"</p>
|
||||
<a href="{{ url('admin/anidb-list') }}" class="mt-3 text-blue-600 dark:text-blue-400 hover:underline">
|
||||
<td colspan="7" class="p-0">
|
||||
<x-admin.empty-state :icon="$animetitle ? 'fas fa-search' : 'fas fa-dragon'" :title="$anidbEmptyTitle">
|
||||
@if($animetitle)
|
||||
<a href="{{ url('admin/anidb-list') }}" class="text-blue-600 dark:text-blue-400 hover:underline">
|
||||
Clear search and view all
|
||||
</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-dragon text-4xl mb-3"></i>
|
||||
<p class="text-lg">No anime available</p>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</x-admin.empty-state>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
|
||||
@@ -157,7 +148,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
{{-- Styles moved to resources/css/csp-safe.css --}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -111,7 +111,7 @@
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -177,7 +177,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
{{-- Scripts moved to resources/js/csp-safe.js --}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -185,7 +185,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
{{-- Scripts moved to resources/js/csp-safe.js --}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -16,20 +16,17 @@
|
||||
</div>
|
||||
|
||||
<!-- Books Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cover</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Title</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Author</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Publisher</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Published</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Created</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>Cover</x-admin.th>
|
||||
<x-admin.th>Title</x-admin.th>
|
||||
<x-admin.th>Author</x-admin.th>
|
||||
<x-admin.th>Publisher</x-admin.th>
|
||||
<x-admin.th>Published</x-admin.th>
|
||||
<x-admin.th>Created</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@forelse($bookList as $book)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@@ -37,6 +34,7 @@
|
||||
<img src="{{ asset('storage/covers/book/' . $book->id . (file_exists(storage_path('covers/book/' . $book->id . '.webp')) ? '.webp' : '.jpg')) }}"
|
||||
alt="{{ $book->title }}"
|
||||
class="h-16 w-12 object-cover rounded shadow"
|
||||
loading="lazy"
|
||||
data-fallback-src="{{ asset('images/no-cover.png') }}">
|
||||
@else
|
||||
<div class="h-16 w-12 bg-gray-200 dark:bg-gray-700 rounded flex items-center justify-center">
|
||||
@@ -68,7 +66,7 @@
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $book->created_at ? $book->created_at->format('Y-m-d') : '—' }}
|
||||
{{ $book->created_at ? formatDate($book->created_at) : '—' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
<a href="{{ url('admin/book-edit?id=' . $book->id) }}"
|
||||
@@ -93,14 +91,12 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
|
||||
{{ $bookList->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
<div>{{ $comment->created_at ? $comment->created_at->format('Y-m-d') : '—' }}</div>
|
||||
<div>{{ $comment->created_at ? formatDate($comment->created_at) : '—' }}</div>
|
||||
<div class="text-xs">{{ $comment->created_at ? $comment->created_at->format('H:i:s') : '' }}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -236,7 +236,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
{{-- Scripts moved to resources/js/csp-safe.js --}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -16,20 +16,17 @@
|
||||
</div>
|
||||
|
||||
<!-- Console Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cover</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Title</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Platform</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Publisher</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Release Date</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">ESRB</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>Cover</x-admin.th>
|
||||
<x-admin.th>Title</x-admin.th>
|
||||
<x-admin.th>Platform</x-admin.th>
|
||||
<x-admin.th>Publisher</x-admin.th>
|
||||
<x-admin.th>Release Date</x-admin.th>
|
||||
<x-admin.th>ESRB</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@forelse($consoleList as $console)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@@ -120,14 +117,12 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
|
||||
{{ $consoleList->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
$isEditing = filled($contentId);
|
||||
@endphp
|
||||
<div class="space-y-6" x-data="tinyMceEditor">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800">
|
||||
@@ -182,7 +182,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
data-refresh-interval="{{ 60 * 1000 }}">
|
||||
<div class="space-y-6" data-dashboard-content>
|
||||
<!-- Welcome Section -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-gray-800 dark:text-gray-200 mb-2">Admin Dashboard</h2>
|
||||
@@ -31,7 +31,7 @@
|
||||
<p class="mt-1 text-xs text-green-600 dark:text-green-400">Auto-refreshes every minute</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<!-- Stats Grid -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6 gap-6">
|
||||
@@ -230,8 +230,8 @@
|
||||
<div class="mt-3">
|
||||
<p class="text-base font-semibold text-gray-900 dark:text-gray-100">{{ $registrationStatus['active_period']->name }}</p>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $registrationStatus['active_period']->starts_at->format('Y-m-d H:i') }} to
|
||||
{{ $registrationStatus['active_period']->ends_at->format('Y-m-d H:i') }}
|
||||
{{ formatDateTime($registrationStatus['active_period']->starts_at) }} to
|
||||
{{ formatDateTime($registrationStatus['active_period']->ends_at) }}
|
||||
</p>
|
||||
</div>
|
||||
<p class="mt-3 text-sm text-gray-600 dark:text-gray-400">This window is active on the public registration form right now.</p>
|
||||
@@ -240,8 +240,8 @@
|
||||
<div class="mt-3">
|
||||
<p class="text-base font-semibold text-gray-900 dark:text-gray-100">{{ $nextRegistrationPeriod->name }}</p>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $nextRegistrationPeriod->starts_at->format('Y-m-d H:i') }} to
|
||||
{{ $nextRegistrationPeriod->ends_at->format('Y-m-d H:i') }}
|
||||
{{ formatDateTime($nextRegistrationPeriod->starts_at) }} to
|
||||
{{ formatDateTime($nextRegistrationPeriod->ends_at) }}
|
||||
</p>
|
||||
</div>
|
||||
<p class="mt-3 text-sm text-gray-600 dark:text-gray-400">The next temporary public signup window is already scheduled.</p>
|
||||
@@ -510,7 +510,7 @@
|
||||
@endif
|
||||
|
||||
<!-- Quick Links -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Quick Links</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<a href="{{ url('/admin/user-list') }}" class="flex flex-col items-center p-4 bg-gray-50 dark:bg-gray-900 hover:bg-gray-100 dark:bg-gray-800 rounded-lg transition">
|
||||
@@ -530,7 +530,7 @@
|
||||
<span class="text-sm font-medium text-gray-700">Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
@@ -209,6 +209,6 @@
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -65,21 +65,18 @@
|
||||
|
||||
<!-- Game List Table -->
|
||||
@if(!empty($gamelist) && count($gamelist) > 0)
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Title</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Publisher</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Genre</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">ESRB</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Release Date</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cover</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>ID</x-admin.th>
|
||||
<x-admin.th>Title</x-admin.th>
|
||||
<x-admin.th>Publisher</x-admin.th>
|
||||
<x-admin.th>Genre</x-admin.th>
|
||||
<x-admin.th>ESRB</x-admin.th>
|
||||
<x-admin.th>Release Date</x-admin.th>
|
||||
<x-admin.th>Cover</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@foreach($gamelist as $game)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-200">
|
||||
@@ -124,22 +121,14 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
@else
|
||||
<div class="px-6 py-12 text-center">
|
||||
<i class="fas fa-gamepad text-gray-400 text-5xl mb-4"></i>
|
||||
<p class="text-gray-500 dark:text-gray-400 text-lg">
|
||||
@if(!empty($lastSearch))
|
||||
No games found matching "{{ $lastSearch }}".
|
||||
@else
|
||||
No games found in the database.
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
@php
|
||||
$gamesEmptyMessage = ! empty($lastSearch) ? 'No games found matching "'.$lastSearch.'".' : 'No games found in the database.';
|
||||
@endphp
|
||||
<x-admin.empty-state icon="fas fa-gamepad" title="No games found" :message="$gamesEmptyMessage" />
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-100">
|
||||
@@ -51,24 +51,21 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Requested</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">User</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Type</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Status</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Completed</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>ID</x-admin.th>
|
||||
<x-admin.th>Requested</x-admin.th>
|
||||
<x-admin.th>User</x-admin.th>
|
||||
<x-admin.th>Type</x-admin.th>
|
||||
<x-admin.th>Status</x-admin.th>
|
||||
<x-admin.th>Completed</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@forelse($requests as $gdprRequest)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">#{{ $gdprRequest->id }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ $gdprRequest->created_at?->format('Y-m-d H:i') }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ formatDateTime($gdprRequest->created_at) }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
<div>{{ $gdprRequest->requester_username ?? 'N/A' }}</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">{{ $gdprRequest->requester_email ?? 'N/A' }}</div>
|
||||
@@ -77,7 +74,7 @@
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200">{{ ucfirst($gdprRequest->status) }}</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ $gdprRequest->completed_at?->format('Y-m-d H:i') ?? '—' }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ formatDateTime($gdprRequest->completed_at) ?: '—' }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm">
|
||||
<a href="{{ route('admin.gdpr-requests.show', $gdprRequest) }}" class="text-blue-600 dark:text-blue-400 hover:underline">
|
||||
<i class="fas fa-eye mr-1"></i>View
|
||||
@@ -89,23 +86,21 @@
|
||||
<td colspan="7" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">No GDPR requests found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
|
||||
<div class="px-6 py-4">
|
||||
{{ $requests->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-100 mb-3">Retention Summary</h2>
|
||||
<ul class="list-disc pl-6 text-sm text-gray-700 dark:text-gray-300 space-y-1">
|
||||
@foreach($retentionPolicy['retained_records'] as $record)
|
||||
<li><strong>{{ $record['table'] }}:</strong> {{ $record['reason'] }} <span class="text-gray-500">({{ str_replace('_', ' ', $record['erasure_action']) }})</span></li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-100">
|
||||
@@ -59,11 +59,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-gray-500 dark:text-gray-400">Created</dt>
|
||||
<dd class="text-gray-900 dark:text-gray-100 font-medium">{{ $gdprRequest->created_at?->format('Y-m-d H:i') }}</dd>
|
||||
<dd class="text-gray-900 dark:text-gray-100 font-medium">{{ formatDateTime($gdprRequest->created_at) }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-gray-500 dark:text-gray-400">Completed</dt>
|
||||
<dd class="text-gray-900 dark:text-gray-100 font-medium">{{ $gdprRequest->completed_at?->format('Y-m-d H:i') ?? '—' }}</dd>
|
||||
<dd class="text-gray-900 dark:text-gray-100 font-medium">{{ formatDateTime($gdprRequest->completed_at) ?: '—' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
<div class="border-l-4 border-blue-500 pl-3">
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ $auditLog->event }}</div>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400">{{ $auditLog->description }}</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-500">{{ $auditLog->created_at?->format('Y-m-d H:i:s') }}</div>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-500">{{ formatDateTimeSeconds($auditLog->created_at) }}</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">No GDPR audit events recorded for this request yet.</p>
|
||||
@@ -137,12 +137,12 @@
|
||||
@if($gdprRequest->isDownloadableExport())
|
||||
<div class="border border-green-200 dark:border-green-800 rounded-lg p-5 bg-green-50 dark:bg-green-900/10">
|
||||
<h2 class="text-lg font-semibold text-green-800 dark:text-green-200 mb-2">Export Ready</h2>
|
||||
<p class="text-sm text-green-700 dark:text-green-300">The user can download this export until {{ $gdprRequest->export_expires_at?->format('Y-m-d H:i') }}.</p>
|
||||
<p class="text-sm text-green-700 dark:text-green-300">The user can download this export until {{ formatDateTime($gdprRequest->export_expires_at) }}.</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -175,7 +175,7 @@
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -248,7 +248,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@@ -2,55 +2,50 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-envelope mr-2"></i>{{ $title }}
|
||||
</h1>
|
||||
<div class="space-x-2">
|
||||
<form method="POST" action="{{ url('admin/invitations/cleanup') }}" class="inline">
|
||||
@csrf
|
||||
<button type="submit" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700">
|
||||
<i class="fas fa-trash mr-2"></i>Cleanup Expired
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<x-admin.page-header :title="$title" icon="fas fa-envelope">
|
||||
<x-slot:actions>
|
||||
<form method="POST" action="{{ url('admin/invitations/cleanup') }}" class="inline">
|
||||
@csrf
|
||||
<button type="submit" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700">
|
||||
<i class="fas fa-trash mr-2"></i>Cleanup Expired
|
||||
</button>
|
||||
</form>
|
||||
</x-slot:actions>
|
||||
</x-admin.page-header>
|
||||
|
||||
<!-- Statistics Cards -->
|
||||
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-4 gap-4">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Total Invitations</div>
|
||||
<div class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ $stats['total'] }}</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Pending</div>
|
||||
<div class="text-2xl font-bold text-blue-600 dark:text-blue-400">{{ $stats['pending'] }}</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Used</div>
|
||||
<div class="text-2xl font-bold text-green-600 dark:text-green-400">{{ $stats['used'] }}</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Expired</div>
|
||||
<div class="text-2xl font-bold text-red-600 dark:text-red-400">{{ $stats['expired'] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4 mt-4">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">Today</div>
|
||||
<div class="text-xl font-semibold text-gray-800 dark:text-gray-200">{{ $stats['today'] }}</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">This Week</div>
|
||||
<div class="text-xl font-semibold text-gray-800 dark:text-gray-200">{{ $stats['this_week'] }}</div>
|
||||
</div>
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow">
|
||||
<div class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow border border-gray-200 dark:border-gray-700">
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">This Month</div>
|
||||
<div class="text-xl font-semibold text-gray-800 dark:text-gray-200">{{ $stats['this_month'] }}</div>
|
||||
</div>
|
||||
@@ -114,21 +109,19 @@
|
||||
<div class="overflow-x-auto">
|
||||
<form method="POST" action="{{ url('admin/invitations/bulk') }}" id="bulkForm">
|
||||
@csrf
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left">
|
||||
<input type="checkbox" id="select_all" class="rounded">
|
||||
</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Email</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Invited By</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Status</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Created</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Expires</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>
|
||||
<input type="checkbox" id="select_all" class="rounded">
|
||||
</x-admin.th>
|
||||
<x-admin.th>Email</x-admin.th>
|
||||
<x-admin.th>Invited By</x-admin.th>
|
||||
<x-admin.th>Status</x-admin.th>
|
||||
<x-admin.th>Created</x-admin.th>
|
||||
<x-admin.th>Expires</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@forelse($invitations as $invitation)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-center">
|
||||
@@ -160,10 +153,10 @@
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $invitation->created_at->format('Y-m-d H:i') }}
|
||||
{{ formatDateTime($invitation->created_at) }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $invitation->expires_at ? $invitation->expires_at->format('Y-m-d H:i') : 'Never' }}
|
||||
{{ $invitation->expires_at ? formatDateTime($invitation->expires_at) : 'Never' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
|
||||
<a href="{{ url('admin/invitations/' . $invitation->id) }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300">
|
||||
@@ -176,9 +169,15 @@
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form method="POST" action="{{ url('admin/invitations/' . $invitation->id . '/cancel') }}" class="inline">
|
||||
<form method="POST" action="{{ url('admin/invitations/' . $invitation->id . '/cancel') }}" class="inline"
|
||||
x-data="confirmForm"
|
||||
data-message="Are you sure you want to cancel this invitation?"
|
||||
data-title="Cancel Invitation"
|
||||
data-type="danger"
|
||||
data-confirm-text="Cancel Invitation"
|
||||
@submit.prevent="submit()">
|
||||
@csrf
|
||||
<button type="submit" class="text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-300" title="Cancel" data-confirm="Are you sure you want to cancel this invitation?">
|
||||
<button type="submit" class="text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-300" title="Cancel">
|
||||
<i class="fas fa-ban"></i>
|
||||
</button>
|
||||
</form>
|
||||
@@ -192,8 +191,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</x-admin.data-table>
|
||||
|
||||
<!-- Bulk Actions -->
|
||||
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
|
||||
@@ -216,30 +214,27 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<!-- Top Inviters Section -->
|
||||
@if(count($topInviters) > 0)
|
||||
<div class="mt-8 bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card class="mt-8">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-trophy mr-2"></i>Top Inviters
|
||||
</h2>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Rank</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Username</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Email</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Total Invitations</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Successful</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Success Rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>Rank</x-admin.th>
|
||||
<x-admin.th>Username</x-admin.th>
|
||||
<x-admin.th>Email</x-admin.th>
|
||||
<x-admin.th>Total Invitations</x-admin.th>
|
||||
<x-admin.th>Successful</x-admin.th>
|
||||
<x-admin.th>Success Rate</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@foreach($topInviters as $index => $user)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-200">
|
||||
@@ -272,11 +267,9 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -131,14 +131,14 @@
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Created At</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-200">
|
||||
{{ $invitation->created_at->format('Y-m-d H:i:s') }}
|
||||
{{ formatDateTimeSeconds($invitation->created_at) }}
|
||||
<span class="text-gray-500 dark:text-gray-400">({{ $invitation->created_at->diffForHumans() }})</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Updated At</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-200">
|
||||
{{ $invitation->updated_at->format('Y-m-d H:i:s') }}
|
||||
{{ formatDateTimeSeconds($invitation->updated_at) }}
|
||||
<span class="text-gray-500 dark:text-gray-400">({{ $invitation->updated_at->diffForHumans() }})</span>
|
||||
</dd>
|
||||
</div>
|
||||
@@ -146,7 +146,7 @@
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Expires At</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-200">
|
||||
@if($invitation->expires_at)
|
||||
{{ $invitation->expires_at->format('Y-m-d H:i:s') }}
|
||||
{{ formatDateTimeSeconds($invitation->expires_at) }}
|
||||
<span class="text-gray-500 dark:text-gray-400">({{ $invitation->expires_at->diffForHumans() }})</span>
|
||||
@if($invitation->expires_at->isPast())
|
||||
<span class="ml-2 text-red-600 dark:text-red-400"><i class="fas fa-exclamation-triangle"></i> Expired</span>
|
||||
@@ -160,7 +160,7 @@
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Used At</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-200">
|
||||
@if($invitation->used_at)
|
||||
{{ $invitation->used_at->format('Y-m-d H:i:s') }}
|
||||
{{ formatDateTimeSeconds($invitation->used_at) }}
|
||||
<span class="text-gray-500 dark:text-gray-400">({{ $invitation->used_at->diffForHumans() }})</span>
|
||||
@else
|
||||
<span class="text-gray-500 dark:text-gray-400">Not used yet</span>
|
||||
@@ -200,7 +200,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
{{-- Scripts moved to resources/js/csp-safe.js --}}
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-gray-500 dark:text-gray-400 uppercase tracking-wide text-xs font-semibold">Last Modified</p>
|
||||
<p class="text-gray-900 dark:text-gray-100 font-medium">{{ $selectedLog['modified_at']->format('Y-m-d H:i:s') }}</p>
|
||||
<p class="text-gray-900 dark:text-gray-100 font-medium">{{ formatDateTimeSeconds($selectedLog['modified_at']) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
@@ -98,7 +98,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
@@ -256,14 +256,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
@extends('layouts.admin')
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -388,6 +388,6 @@
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
@@ -230,6 +230,6 @@
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -65,22 +65,19 @@
|
||||
|
||||
<!-- Music List Table -->
|
||||
@if(!empty($musicList) && count($musicList) > 0)
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Title</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Artist</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Publisher</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Year</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Genre</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Tracks</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cover</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>ID</x-admin.th>
|
||||
<x-admin.th>Title</x-admin.th>
|
||||
<x-admin.th>Artist</x-admin.th>
|
||||
<x-admin.th>Publisher</x-admin.th>
|
||||
<x-admin.th>Year</x-admin.th>
|
||||
<x-admin.th>Genre</x-admin.th>
|
||||
<x-admin.th>Tracks</x-admin.th>
|
||||
<x-admin.th>Cover</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@foreach($musicList as $music)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-200">
|
||||
@@ -124,9 +121,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
@else
|
||||
<div class="px-6 py-12 text-center">
|
||||
<i class="fas fa-music text-gray-400 text-5xl mb-4"></i>
|
||||
@@ -139,7 +134,7 @@
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -43,21 +43,18 @@
|
||||
</div>
|
||||
|
||||
<!-- PreDB Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Title</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Category</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Size</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Files</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Pre Date</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Source</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Status</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Release</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>Title</x-admin.th>
|
||||
<x-admin.th>Category</x-admin.th>
|
||||
<x-admin.th>Size</x-admin.th>
|
||||
<x-admin.th>Files</x-admin.th>
|
||||
<x-admin.th>Pre Date</x-admin.th>
|
||||
<x-admin.th>Source</x-admin.th>
|
||||
<x-admin.th>Status</x-admin.th>
|
||||
<x-admin.th>Release</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@forelse($results as $pre)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4">
|
||||
@@ -127,28 +124,22 @@
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
@php
|
||||
$predbEmptyTitle = $lastSearch ? 'No PreDB entries found for "'.$lastSearch.'"' : 'No PreDB entries available';
|
||||
@endphp
|
||||
<tr>
|
||||
<td colspan="8" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">
|
||||
@if($lastSearch)
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-search text-4xl mb-3"></i>
|
||||
<p class="text-lg">No PreDB entries found for "{{ $lastSearch }}"</p>
|
||||
<a href="{{ url('admin/predb') }}" class="mt-3 text-blue-600 dark:text-blue-400 hover:underline">
|
||||
<td colspan="8" class="p-0">
|
||||
<x-admin.empty-state :icon="$lastSearch ? 'fas fa-search' : 'fas fa-database'" :title="$predbEmptyTitle">
|
||||
@if($lastSearch)
|
||||
<a href="{{ url('admin/predb') }}" class="text-blue-600 dark:text-blue-400 hover:underline">
|
||||
Clear search and view all
|
||||
</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-database text-4xl mb-3"></i>
|
||||
<p class="text-lg">No PreDB entries available</p>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</x-admin.empty-state>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
|
||||
@@ -161,7 +152,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<!-- Info Box -->
|
||||
@if($lastSearch)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm max-w-3xl mx-auto">
|
||||
<x-admin.card class="max-w-3xl mx-auto">
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
@@ -126,7 +126,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm max-w-3xl mx-auto">
|
||||
<x-admin.card class="max-w-3xl mx-auto">
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
@@ -88,7 +88,7 @@
|
||||
<label for="start_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Start Date
|
||||
</label>
|
||||
<input type="date" name="start_date" id="start_date" value="{{ old('start_date', $promotion->start_date ? $promotion->start_date->format('Y-m-d') : '') }}"
|
||||
<input type="date" name="start_date" id="start_date" value="{{ old('start_date', $promotion->start_date ? formatDate($promotion->start_date) : '') }}"
|
||||
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-100">
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Leave blank for no start restriction</p>
|
||||
@error('start_date')
|
||||
@@ -100,7 +100,7 @@
|
||||
<label for="end_date" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
End Date
|
||||
</label>
|
||||
<input type="date" name="end_date" id="end_date" value="{{ old('end_date', $promotion->end_date ? $promotion->end_date->format('Y-m-d') : '') }}"
|
||||
<input type="date" name="end_date" id="end_date" value="{{ old('end_date', $promotion->end_date ? formatDate($promotion->end_date) : '') }}"
|
||||
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-100">
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Leave blank for no end restriction</p>
|
||||
@error('end_date')
|
||||
@@ -132,7 +132,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -23,20 +23,17 @@
|
||||
|
||||
<!-- Promotions Table -->
|
||||
@if(count($promotions) > 0)
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Name</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Applicable Roles</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Additional Days</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Start Date</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">End Date</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Status</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>Name</x-admin.th>
|
||||
<x-admin.th>Applicable Roles</x-admin.th>
|
||||
<x-admin.th>Additional Days</x-admin.th>
|
||||
<x-admin.th>Start Date</x-admin.th>
|
||||
<x-admin.th>End Date</x-admin.th>
|
||||
<x-admin.th>Status</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@foreach($promotions as $promotion)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@@ -65,10 +62,10 @@
|
||||
<span class="font-semibold">{{ $promotion->additional_days }}</span> days
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $promotion->start_date ? $promotion->start_date->format('Y-m-d') : 'No limit' }}
|
||||
{{ $promotion->start_date ? formatDate($promotion->start_date) : 'No limit' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $promotion->end_date ? $promotion->end_date->format('Y-m-d') : 'No limit' }}
|
||||
{{ $promotion->end_date ? formatDate($promotion->end_date) : 'No limit' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@php
|
||||
@@ -127,9 +124,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
@else
|
||||
<div class="px-6 py-12 text-center">
|
||||
<i class="fas fa-gift text-gray-400 text-5xl mb-4"></i>
|
||||
@@ -137,7 +132,7 @@
|
||||
<p class="text-gray-500 dark:text-gray-400">Create your first promotion to get started.</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Promotion Info Card -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 mb-6">
|
||||
<x-admin.card class="p-6 mb-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Status</p>
|
||||
@@ -43,11 +43,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Start Date</p>
|
||||
<p class="text-xl font-bold text-gray-800 dark:text-gray-200">{{ $promotion->start_date ? $promotion->start_date->format('Y-m-d') : 'No limit' }}</p>
|
||||
<p class="text-xl font-bold text-gray-800 dark:text-gray-200">{{ $promotion->start_date ? formatDate($promotion->start_date) : 'No limit' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">End Date</p>
|
||||
<p class="text-xl font-bold text-gray-800 dark:text-gray-200">{{ $promotion->end_date ? $promotion->end_date->format('Y-m-d') : 'No limit' }}</p>
|
||||
<p class="text-xl font-bold text-gray-800 dark:text-gray-200">{{ $promotion->end_date ? formatDate($promotion->end_date) : 'No limit' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@if($promotion->description)
|
||||
@@ -56,10 +56,10 @@
|
||||
<p class="text-gray-800 dark:text-gray-200 mt-1">{{ $promotion->description }}</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<!-- Date Range Filter -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 mb-6">
|
||||
<x-admin.card class="p-6 mb-6">
|
||||
<form method="GET" action="{{ route('admin.promotions.show-statistics', $promotion->id) }}" class="flex flex-wrap gap-4 items-end" x-data="periodFilter({{ $selectedPeriod === 'custom' ? 'true' : 'false' }})" x-ref="periodForm">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Time Period</label>
|
||||
@@ -75,11 +75,11 @@
|
||||
<div x-show="showCustom" x-cloak class="flex gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Start Date</label>
|
||||
<input type="date" name="start_date" value="{{ $startDate ? $startDate->format('Y-m-d') : '' }}" class="form-input rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
||||
<input type="date" name="start_date" value="{{ $startDate ? formatDate($startDate) : '' }}" class="form-input rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">End Date</label>
|
||||
<input type="date" name="end_date" value="{{ $endDate->format('Y-m-d') }}" class="form-input rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
||||
<input type="date" name="end_date" value="{{ formatDate($endDate) }}" class="form-input rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||
@@ -88,11 +88,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<!-- Statistics Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 bg-blue-100 dark:bg-blue-900 rounded-full p-3">
|
||||
<i class="fas fa-arrow-up text-2xl text-blue-600 dark:text-blue-400"></i>
|
||||
@@ -102,9 +102,9 @@
|
||||
<p class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ number_format($stats['total_upgrades']) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 bg-green-100 dark:bg-green-900 rounded-full p-3">
|
||||
<i class="fas fa-users text-2xl text-green-600 dark:text-green-400"></i>
|
||||
@@ -114,9 +114,9 @@
|
||||
<p class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ number_format($stats['unique_users']) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 bg-purple-100 dark:bg-purple-900 rounded-full p-3">
|
||||
<i class="fas fa-calendar-plus text-2xl text-purple-600 dark:text-purple-400"></i>
|
||||
@@ -126,9 +126,9 @@
|
||||
<p class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ number_format($stats['total_days_added']) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 bg-yellow-100 dark:bg-yellow-900 rounded-full p-3">
|
||||
<i class="fas fa-user-tag text-2xl text-yellow-600 dark:text-yellow-400"></i>
|
||||
@@ -138,12 +138,12 @@
|
||||
<p class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ number_format($stats['roles_affected']) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
<!-- Statistics by Role -->
|
||||
@if(count($statsByRole) > 0)
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm mb-6">
|
||||
<x-admin.card class="mb-6">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-user-tag mr-2"></i>Statistics by Role
|
||||
@@ -181,12 +181,12 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
@endif
|
||||
|
||||
<!-- Daily Activity Chart -->
|
||||
@if($dailyStats->isNotEmpty())
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm mb-6">
|
||||
<x-admin.card class="mb-6">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-chart-area mr-2"></i>Daily Activity
|
||||
@@ -221,11 +221,11 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
@endif
|
||||
|
||||
<!-- Recent Applications -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-list mr-2"></i>Recent Applications
|
||||
@@ -266,13 +266,13 @@
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">days</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $application->previous_expiry_date ? $application->previous_expiry_date->format('Y-m-d H:i') : 'N/A' }}
|
||||
{{ $application->previous_expiry_date ? formatDateTime($application->previous_expiry_date) : 'N/A' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $application->new_expiry_date ? $application->new_expiry_date->format('Y-m-d H:i') : 'N/A' }}
|
||||
{{ $application->new_expiry_date ? formatDateTime($application->new_expiry_date) : 'N/A' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
<div>{{ $application->applied_at->format('Y-m-d H:i') }}</div>
|
||||
<div>{{ formatDateTime($application->applied_at) }}</div>
|
||||
<div class="text-xs">{{ $application->applied_at->diffForHumans() }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -291,7 +291,7 @@
|
||||
{{ $applications->appends(request()->query())->links() }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Date Range Filter -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 mb-6">
|
||||
<x-admin.card class="p-6 mb-6">
|
||||
<form method="GET" action="{{ route('admin.promotions.statistics') }}" class="flex flex-wrap gap-4 items-end" x-data="periodFilter({{ $selectedPeriod === 'custom' ? 'true' : 'false' }})" x-ref="periodForm">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Quick Select</label>
|
||||
@@ -34,11 +34,11 @@
|
||||
<div x-show="showCustom" x-cloak class="flex gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Start Date</label>
|
||||
<input type="date" name="start_date" value="{{ $startDate ? $startDate->format('Y-m-d') : '' }}" class="form-input rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
||||
<input type="date" name="start_date" value="{{ $startDate ? formatDate($startDate) : '' }}" class="form-input rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">End Date</label>
|
||||
<input type="date" name="end_date" value="{{ $endDate->format('Y-m-d') }}" class="form-input rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
||||
<input type="date" name="end_date" value="{{ formatDate($endDate) }}" class="form-input rounded-md border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200">
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||
@@ -47,11 +47,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<!-- Overall Statistics Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6 mb-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 bg-blue-100 dark:bg-blue-900 rounded-full p-3">
|
||||
<i class="fas fa-gift text-2xl text-blue-600 dark:text-blue-400"></i>
|
||||
@@ -61,9 +61,9 @@
|
||||
<p class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ $overallStats['total_promotions'] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 bg-green-100 dark:bg-green-900 rounded-full p-3">
|
||||
<i class="fas fa-check-circle text-2xl text-green-600 dark:text-green-400"></i>
|
||||
@@ -73,9 +73,9 @@
|
||||
<p class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ $overallStats['active_promotions'] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 bg-purple-100 dark:bg-purple-900 rounded-full p-3">
|
||||
<i class="fas fa-arrow-up text-2xl text-purple-600 dark:text-purple-400"></i>
|
||||
@@ -85,9 +85,9 @@
|
||||
<p class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ number_format($overallStats['total_applications']) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 bg-yellow-100 dark:bg-yellow-900 rounded-full p-3">
|
||||
<i class="fas fa-users text-2xl text-yellow-600 dark:text-yellow-400"></i>
|
||||
@@ -97,9 +97,9 @@
|
||||
<p class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ number_format($overallStats['unique_users']) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
|
||||
<x-admin.card class="p-6">
|
||||
<div class="flex items-center">
|
||||
<div class="shrink-0 bg-red-100 dark:bg-red-900 rounded-full p-3">
|
||||
<i class="fas fa-calendar-plus text-2xl text-red-600 dark:text-red-400"></i>
|
||||
@@ -109,12 +109,12 @@
|
||||
<p class="text-2xl font-bold text-gray-800 dark:text-gray-200">{{ number_format($overallStats['total_days_added']) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||
<!-- Top Promotions -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-trophy mr-2 text-yellow-500"></i>Top Promotions by Usage
|
||||
@@ -142,10 +142,10 @@
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<!-- Statistics by Role -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-user-tag mr-2"></i>Statistics by Role
|
||||
@@ -180,11 +180,11 @@
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
<!-- All Promotions with Statistics -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-list mr-2"></i>All Promotions Statistics
|
||||
@@ -245,10 +245,10 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm mt-6">
|
||||
<x-admin.card class="mt-6">
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-clock mr-2"></i>Recent Promotion Activity
|
||||
@@ -296,7 +296,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -179,7 +179,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
{{-- Scripts moved to resources/js/csp-safe.js --}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -154,7 +154,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
{{-- Scripts moved to resources/js/csp-safe.js --}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -165,6 +165,6 @@
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -154,7 +154,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
{{-- Scripts moved to resources/js/csp-safe.js --}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -187,6 +187,6 @@
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -77,8 +77,8 @@
|
||||
<div class="mt-3">
|
||||
<p class="text-base font-semibold text-gray-900 dark:text-gray-100">{{ $registrationStatus['active_period']->name }}</p>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $registrationStatus['active_period']->starts_at->format('Y-m-d H:i') }} to
|
||||
{{ $registrationStatus['active_period']->ends_at->format('Y-m-d H:i') }}
|
||||
{{ formatDateTime($registrationStatus['active_period']->starts_at) }} to
|
||||
{{ formatDateTime($registrationStatus['active_period']->ends_at) }}
|
||||
</p>
|
||||
</div>
|
||||
@else
|
||||
@@ -207,19 +207,16 @@
|
||||
<p>No current or upcoming open-registration periods are scheduled.</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Name</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Status</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Window</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Notes</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Updated By</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-800">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>Name</x-admin.th>
|
||||
<x-admin.th>Status</x-admin.th>
|
||||
<x-admin.th>Window</x-admin.th>
|
||||
<x-admin.th>Notes</x-admin.th>
|
||||
<x-admin.th>Updated By</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@foreach($currentPeriods as $period)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/40">
|
||||
<td class="px-6 py-4 align-top">
|
||||
@@ -236,8 +233,8 @@
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 align-top text-sm text-gray-700 dark:text-gray-300">
|
||||
<p>{{ $period->starts_at->format('Y-m-d H:i') }}</p>
|
||||
<p class="mt-1 text-gray-500 dark:text-gray-400">to {{ $period->ends_at->format('Y-m-d H:i') }}</p>
|
||||
<p>{{ formatDateTime($period->starts_at) }}</p>
|
||||
<p class="mt-1 text-gray-500 dark:text-gray-400">to {{ formatDateTime($period->ends_at) }}</p>
|
||||
</td>
|
||||
<td class="px-6 py-4 align-top text-sm text-gray-700 dark:text-gray-300">
|
||||
{{ $period->notes ? Str::limit($period->notes, 100) : '—' }}
|
||||
@@ -288,9 +285,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
@endif
|
||||
</x-admin.card>
|
||||
|
||||
@@ -306,19 +301,16 @@
|
||||
<p>No past open-registration periods have been recorded yet.</p>
|
||||
</div>
|
||||
@else
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Name</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Status</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Window</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Notes</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Updated By</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-800">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>Name</x-admin.th>
|
||||
<x-admin.th>Status</x-admin.th>
|
||||
<x-admin.th>Window</x-admin.th>
|
||||
<x-admin.th>Notes</x-admin.th>
|
||||
<x-admin.th>Updated By</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@foreach($pastPeriods as $period)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700/40">
|
||||
<td class="px-6 py-4 align-top">
|
||||
@@ -330,8 +322,8 @@
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 align-top text-sm text-gray-700 dark:text-gray-300">
|
||||
<p>{{ $period->starts_at->format('Y-m-d H:i') }}</p>
|
||||
<p class="mt-1 text-gray-500 dark:text-gray-400">to {{ $period->ends_at->format('Y-m-d H:i') }}</p>
|
||||
<p>{{ formatDateTime($period->starts_at) }}</p>
|
||||
<p class="mt-1 text-gray-500 dark:text-gray-400">to {{ formatDateTime($period->ends_at) }}</p>
|
||||
</td>
|
||||
<td class="px-6 py-4 align-top text-sm text-gray-700 dark:text-gray-300">
|
||||
{{ $period->notes ? Str::limit($period->notes, 100) : '—' }}
|
||||
@@ -365,9 +357,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
@endif
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@@ -458,7 +448,7 @@
|
||||
@if(!empty($failure['registration_status_label']))
|
||||
<span>Effective: {{ $failure['registration_status_label'] }}</span>
|
||||
@endif
|
||||
<span>{{ $failure['timestamp']->format('Y-m-d H:i:s') }}</span>
|
||||
<span>{{ formatDateTimeSeconds($failure['timestamp']) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div x-data="adminReleaseList" class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -173,18 +173,12 @@
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="px-6 py-12 text-center">
|
||||
<i class="fas fa-check-circle text-gray-400 text-5xl mb-4"></i>
|
||||
<p class="text-gray-500 dark:text-gray-400 text-lg">
|
||||
@if(request('failrelsearch'))
|
||||
No failed releases found matching "{{ request('failrelsearch') }}".
|
||||
@else
|
||||
No failed releases found. Great job!
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
@php
|
||||
$failedEmptyMessage = request('failrelsearch') ? 'No failed releases found matching "'.request('failrelsearch').'".' : 'No failed releases found. Great job!';
|
||||
@endphp
|
||||
<x-admin.empty-state icon="fas fa-check-circle" title="No failed releases found" :message="$failedEmptyMessage" />
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-100">
|
||||
@@ -211,7 +211,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
@@ -251,7 +251,7 @@
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6" x-data="showAddForm" data-config="{{ $configJson }}">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center">
|
||||
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
@@ -123,7 +123,7 @@
|
||||
class="mt-6 p-4 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg">
|
||||
<div class="flex gap-4">
|
||||
<template x-if="hasPoster">
|
||||
<img x-bind:src="posterUrl" alt="" class="w-32 h-auto rounded shadow" />
|
||||
<img x-bind:src="posterUrl" x-bind:alt="title ? title + ' poster' : 'Show poster'" class="w-32 h-auto rounded shadow" />
|
||||
</template>
|
||||
<div class="flex-1">
|
||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100" x-text="title"></h3>
|
||||
@@ -159,7 +159,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -142,8 +142,7 @@
|
||||
@if(!empty($show['image']))
|
||||
<img src="{{ $show['image'] }}"
|
||||
alt="{{ $show['title'] }}"
|
||||
class="max-w-full h-auto mx-auto rounded shadow-lg"
|
||||
style="max-height: 400px;"
|
||||
class="max-w-full h-auto mx-auto rounded shadow-lg max-h-[400px]"
|
||||
data-fallback-src="{{ asset('images/no-cover.png') }}">
|
||||
@else
|
||||
<div class="flex flex-col items-center justify-center py-12 text-gray-400 dark:text-gray-500">
|
||||
@@ -223,7 +222,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -49,20 +49,17 @@
|
||||
</div>
|
||||
|
||||
<!-- TV Shows Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Cover</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Title</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Type</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Started</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">IDs</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Publisher</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th>Cover</x-admin.th>
|
||||
<x-admin.th>Title</x-admin.th>
|
||||
<x-admin.th>Type</x-admin.th>
|
||||
<x-admin.th>Started</x-admin.th>
|
||||
<x-admin.th>IDs</x-admin.th>
|
||||
<x-admin.th>Publisher</x-admin.th>
|
||||
<x-admin.th>Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@forelse($tvshowlist as $show)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@@ -161,28 +158,22 @@
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
@php
|
||||
$showsEmptyTitle = $showname ? 'No TV shows found for "'.$showname.'"' : 'No TV shows available';
|
||||
@endphp
|
||||
<tr>
|
||||
<td colspan="7" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">
|
||||
@if($showname)
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-search text-4xl mb-3"></i>
|
||||
<p class="text-lg">No TV shows found for "{{ $showname }}"</p>
|
||||
<a href="{{ url('admin/show-list') }}" class="mt-3 text-blue-600 dark:text-blue-400 hover:underline">
|
||||
<td colspan="7" class="p-0">
|
||||
<x-admin.empty-state :icon="$showname ? 'fas fa-search' : 'fas fa-tv'" :title="$showsEmptyTitle">
|
||||
@if($showname)
|
||||
<a href="{{ url('admin/show-list') }}" class="text-blue-600 dark:text-blue-400 hover:underline">
|
||||
Clear search and view all
|
||||
</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex flex-col items-center">
|
||||
<i class="fas fa-tv text-4xl mb-3"></i>
|
||||
<p class="text-lg">No TV shows available</p>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</x-admin.empty-state>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
|
||||
@@ -195,7 +186,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
{{-- Styles moved to resources/css/csp-safe.css --}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -111,7 +111,7 @@
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
<!-- Additional Usenet Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Additional Usenet Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="maxsizetopostprocess" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-file-archive mr-1"></i>Maximum Release Size to Post Process
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="maxsizetopostprocess" name="maxsizetopostprocess" value="{{ $site['maxsizetopostprocess'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">GB</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum size in gigabytes to postprocess a release. If set to 0, then ignored.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="minsizetopostprocess" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-file-archive mr-1"></i>Minimum Release Size to Post Process
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="minsizetopostprocess" name="minsizetopostprocess" value="{{ $site['minsizetopostprocess'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">MB</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">The minimum size in megabytes to post process (additional) a release. If set to 0, then ignored.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,145 @@
|
||||
<!-- Advanced Settings - For advanced users -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Advanced Settings - For Advanced Users</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="maxnzbsprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-file-code mr-1"></i>Maximum NZBs Stage5
|
||||
</label>
|
||||
<input type="text" id="maxnzbsprocessed" name="maxnzbsprocessed" value="{{ $site['maxnzbsprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum amount of NZB files to create on stage 5 at a time in update_releases. If more are to be created it will loop stage 5 until none remain.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="partrepair" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-toolbox mr-1"></i>Part Repair
|
||||
</label>
|
||||
<select id="partrepair" name="partrepair" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['partrepair'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to repair parts or not, increases backfill/binaries updating time.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="safepartrepair" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-shield-alt mr-1"></i>Part Repair for Backfill Scripts
|
||||
</label>
|
||||
<select id="safepartrepair" name="safepartrepair" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['safepartrepair'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to put unreceived parts into missed_parts table when running binaries(safe) or backfill scripts.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxpartrepair" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tools mr-1"></i>Maximum Repair Per Run
|
||||
</label>
|
||||
<input type="text" id="maxpartrepair" name="maxpartrepair" value="{{ $site['maxpartrepair'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum amount of articles to attempt to repair at a time. If you notice that you are getting a lot of parts into the missed_parts table, it is possible that you USP is not keeping up with the requests. Try to reduce the threads to safe scripts or stop using safe scripts until improves.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="partrepairmaxtries" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-redo mr-1"></i>Maximum Repair Tries
|
||||
</label>
|
||||
<input type="text" id="partrepairmaxtries" name="partrepairmaxtries" value="{{ $site['partrepairmaxtries'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">Maximum amount of times to try part repair.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="processjpg" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-image mr-1"></i>Process JPG
|
||||
</label>
|
||||
<select id="processjpg" name="processjpg" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['processjpg'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to retrieve a JPG file while additional post processing, these are usually on XXX releases.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="processthumbnails" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-file-image mr-1"></i>Process Video Thumbnails
|
||||
</label>
|
||||
<select id="processthumbnails" name="processthumbnails" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['processthumbnails'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to process a video thumbnail image. You must have ffmpeg for this.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="processvideos" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-film mr-1"></i>Process Video Samples
|
||||
</label>
|
||||
<select id="processvideos" name="processvideos" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['processvideos'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to process a video sample, these videos are very short 1-3 seconds, 100KB on average, in ogg video format. You must have ffmpeg for this.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="segmentstodownload" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-download mr-1"></i>Number of Segments to Download
|
||||
</label>
|
||||
<input type="text" id="segmentstodownload" name="segmentstodownload" value="{{ $site['segmentstodownload'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum number of segments to download to generate the sample video file or jpg sample image. (Default 2)</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="ffmpeg_duration" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-film mr-1"></i>Video Sample File Duration
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="ffmpeg_duration" name="ffmpeg_duration" value="{{ $site['ffmpeg_duration'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">seconds</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum duration (in seconds) for ffmpeg to generate the sample for. (Default 5)</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxnestedlevels" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-layer-group mr-1"></i>Nested Archive Depth
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="maxnestedlevels" name="maxnestedlevels" value="{{ $site['maxnestedlevels'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">levels</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">If a rar/zip has rar/zip inside of it, how many times should we go in those inner rar/zip files.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="innerfileblacklist" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-ban mr-1"></i>Inner File Black List Regex
|
||||
</label>
|
||||
<textarea id="innerfileblacklist" name="innerfileblacklist" rows="3" placeholder="Example: /setup\.exe|password\.url/i"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">{{ $site['innerfileblacklist'] ?? '' }}</textarea>
|
||||
<p class="mt-1 text-sm text-gray-500">You can add a regex here to set releases to potentially passworded when a file name inside a rar/zip matches this regex. <strong>You must ensure this regex is valid, a non valid regex will cause errors during processing!</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!-- Connection Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Connection Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="nntpretries" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-refresh mr-1"></i>NNTP Retry Attempts
|
||||
</label>
|
||||
<input type="text" id="nntpretries" name="nntpretries" value="{{ $site['nntpretries'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum number of retry attempts to connect to nntp provider. On error, each retry takes approximately 5 seconds nntp returns reply. (Default 10)</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="delaytime" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-clock-o mr-1"></i>Delay Time Check
|
||||
</label>
|
||||
<input type="text" id="delaytime" name="delaytime" value="{{ $site['delaytime'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The time in hours to wait, since last activity, before releases without parts counts in the subject are are created.<br>Setting this below 2 hours could create incomplete releases.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="collection_timeout" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-hourglass-end mr-1"></i>Collection Timeout Check
|
||||
</label>
|
||||
<input type="text" id="collection_timeout" name="collection_timeout" value="{{ $site['collection_timeout'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">How many hours to wait before converting a collection into a release that is considered "stuck".<br>Default value is 48 hours.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
<!-- Developer Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Developer Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="showdroppedyencparts" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-bug mr-1"></i>Log Dropped Headers
|
||||
</label>
|
||||
<select id="showdroppedyencparts" name="showdroppedyencparts" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['showdroppedyencparts'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">For developers. Whether to log all headers that have 'yEnc' and are dropped. Logged to not_yenc/groupname.dropped.txt.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
<!-- Language/Categorization Options -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Language/Categorization Options</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="categorizeforeign" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-globe mr-1"></i>Categorize Foreign
|
||||
</label>
|
||||
<select id="categorizeforeign" name="categorizeforeign" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['categorizeforeign'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to send foreign movies/tv to foreign sections or not. If set to true they will go in foreign categories.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="catwebdl" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-cloud-download-alt mr-1"></i>Categorize WEB-DL
|
||||
</label>
|
||||
<select id="catwebdl" name="catwebdl" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['catwebdl'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to send WEB-DL to the WEB-DL section or not. If set to true they will go in WEB-DL category, false will send them in HD TV. This will also make them inaccessible to Sickbeard and possibly Couchpotato.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,118 @@
|
||||
<!-- Lookup Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Lookup Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="lookuptv" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tv mr-1"></i>Lookup TV
|
||||
</label>
|
||||
<select id="lookuptv" name="lookuptv" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($lookuptv['ids'] as $index => $lookuptvId)
|
||||
<option value="{{ $lookuptvId }}" {{ ($site['lookuptv'] ?? '') == $lookuptvId ? 'selected' : '' }}>
|
||||
{{ $lookuptv['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to lookup TV related ids on the web.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="lookupbooks" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-book mr-1"></i>Lookup Books
|
||||
</label>
|
||||
<select id="lookupbooks" name="lookupbooks" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($lookupbooks['ids'] as $index => $lookupbooksId)
|
||||
<option value="{{ $lookupbooksId }}" {{ ($site['lookupbooks'] ?? '') == $lookupbooksId ? 'selected' : '' }}>
|
||||
{{ $lookupbooks['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to lookup book information from ISBNdb, with iTunes fallback.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="lookupimdb" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-film mr-1"></i>Lookup Movies
|
||||
</label>
|
||||
<select id="lookupimdb" name="lookupimdb" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($lookupmovies['ids'] as $index => $lookupmoviesId)
|
||||
<option value="{{ $lookupmoviesId }}" {{ ($site['lookupimdb'] ?? '') == $lookupmoviesId ? 'selected' : '' }}>
|
||||
{{ $lookupmovies['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to lookup film information from IMDB or TheMovieDB.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="lookuplanguage" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-language mr-1"></i>Movie Lookup Language
|
||||
</label>
|
||||
<select id="lookuplanguage" name="lookuplanguage" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($lookuplanguage['iso'] as $index => $languageIso)
|
||||
<option value="{{ $languageIso }}" {{ ($site['lookuplanguage'] ?? '') == $languageIso ? 'selected' : '' }}>
|
||||
{{ $lookuplanguage['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Preferred language for scraping external sources.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="lookupanidb" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-dragon mr-1"></i>Lookup AniDB
|
||||
</label>
|
||||
<select id="lookupanidb" name="lookupanidb" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['lookupanidb'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to lookup anime information from AniDB when processing binaries.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="lookupmusic" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-music mr-1"></i>Lookup Music
|
||||
</label>
|
||||
<select id="lookupmusic" name="lookupmusic" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($lookupmusic['ids'] as $index => $lookupmusicId)
|
||||
<option value="{{ $lookupmusicId }}" {{ ($site['lookupmusic'] ?? '') == $lookupmusicId ? 'selected' : '' }}>
|
||||
{{ $lookupmusic['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to lookup music information from metadata providers.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="saveaudiopreview" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-music mr-1"></i>Save Audio Preview
|
||||
</label>
|
||||
<select id="saveaudiopreview" name="saveaudiopreview" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['saveaudiopreview'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to save a preview of an audio release (requires deep rar inspection enabled).<br>It is advisable to specify a path to the lame binary to reduce the size of audio previews.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="lookupgames" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-gamepad mr-1"></i>Lookup Games
|
||||
</label>
|
||||
<select id="lookupgames" name="lookupgames" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($lookupgames['ids'] as $index => $lookupgamesId)
|
||||
<option value="{{ $lookupgamesId }}" {{ ($site['lookupgames'] ?? '') == $lookupgamesId ? 'selected' : '' }}>
|
||||
{{ $lookupgames['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to lookup game information from metadata providers.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,79 @@
|
||||
<!-- Main Site Settings, HTML Layout, Tags -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Main Site Settings, HTML Layout, Tags</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="strapline" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-quote-right mr-1"></i>Strapline
|
||||
</label>
|
||||
<input type="text" id="strapline" name="strapline" value="{{ $site['strapline'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">Displayed in the header on every public page.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="metatitle" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-heading mr-1"></i>Meta Title
|
||||
</label>
|
||||
<input type="text" id="metatitle" name="metatitle" value="{{ $site['metatitle'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">Stem meta-tag appended to all page title tags.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="metadescription" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-comment mr-1"></i>Meta Description
|
||||
</label>
|
||||
<textarea id="metadescription" name="metadescription" rows="3"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">{{ $site['metadescription'] ?? '' }}</textarea>
|
||||
<p class="mt-1 text-sm text-gray-500">Stem meta-description appended to all page meta description tags.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="metakeywords" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tags mr-1"></i>Meta Keywords
|
||||
</label>
|
||||
<textarea id="metakeywords" name="metakeywords" rows="3"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">{{ $site['metakeywords'] ?? '' }}</textarea>
|
||||
<p class="mt-1 text-sm text-gray-500">Stem meta-keywords appended to all page meta keyword tags.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="footer" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-copyright mr-1"></i>Footer
|
||||
</label>
|
||||
<textarea id="footer" name="footer" rows="3"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">{{ $site['footer'] ?? '' }}</textarea>
|
||||
<p class="mt-1 text-sm text-gray-500">Displayed in the footer section of every public page.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="home_link" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-home mr-1"></i>Default Home Page
|
||||
</label>
|
||||
<input type="text" id="home_link" name="home_link" value="{{ $site['home_link'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The relative path to the landing page shown when a user logs in, or clicks the home link.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="dereferrer_link" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-external-link-alt mr-1"></i>Dereferrer Link
|
||||
</label>
|
||||
<input type="text" id="dereferrer_link" name="dereferrer_link" value="{{ $site['dereferrer_link'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">Optional URL to prepend to external links.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="tandc" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-gavel mr-1"></i>Terms and Conditions
|
||||
</label>
|
||||
<textarea id="tandc" name="tandc" rows="15"
|
||||
data-tinymce-api-key="{{ config('tinymce.api_key', 'no-api-key') }}"
|
||||
class="tinymce-editor w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">{{ $site['tandc'] ?? '' }}</textarea>
|
||||
<p class="mt-1 text-sm text-gray-500">Text displayed in the terms and conditions page. Use the rich text editor to format your content.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,44 @@
|
||||
<!-- Movie Trailer Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Movie Trailer Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="trailers_display" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-play-circle mr-1"></i>Fetch/Display Movie Trailers
|
||||
</label>
|
||||
<select id="trailers_display" name="trailers_display" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['trailers_display'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Fetch and display trailers from TraktTV (Requires API key) and/or TrailerAddict on the details page?</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="trailers_size_x" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-arrows-alt-h mr-1"></i>Trailers Width
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="trailers_size_x" name="trailers_size_x" value="{{ $site['trailers_size_x'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">px</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">Maximum width in pixels for the trailer window. (Default: 480)</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="trailers_size_y" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-arrows-alt-v mr-1"></i>Trailers Height
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="trailers_size_y" name="trailers_size_y" value="{{ $site['trailers_size_y'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">px</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">Maximum height in pixels for the trailer window. (Default: 345)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,65 @@
|
||||
<!-- NFO Processing Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">NFO Processing Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="lookupnfo" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-file-alt mr-1"></i>Lookup NFO
|
||||
</label>
|
||||
<select id="lookupnfo" name="lookupnfo" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['lookupnfo'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to attempt to retrieve an nfo file from usenet.<br><strong>NOTE: disabling nfo lookups will disable movie lookups.</strong></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxnfoprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-file-text mr-1"></i>Maximum NFO Files Per Run
|
||||
</label>
|
||||
<input type="text" id="maxnfoprocessed" name="maxnfoprocessed" value="{{ $site['maxnfoprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum amount of NFO files to process per run. This uses NNTP an connection, 1 per thread. This does not query external metadata providers.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxsizetoprocessnfo" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-upload mr-1"></i>Maximum Release Size to Process NFOs
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="maxsizetoprocessnfo" name="maxsizetoprocessnfo" value="{{ $site['maxsizetoprocessnfo'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">GB</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum size in gigabytes of a release to process it for NFOs. If set to 0, then ignored.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="minsizetoprocessnfo" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-download mr-1"></i>Minimum Release Size to Process NFOs
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="minsizetoprocessnfo" name="minsizetoprocessnfo" value="{{ $site['minsizetoprocessnfo'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">MB</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">The minimum size in megabytes of a release to process it for NFOs. If set to 0, then ignored.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxnforetries" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-refresh mr-1"></i>Maximum Amount of Times to Redownload a NFO
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="maxnforetries" name="maxnforetries" value="{{ $site['maxnforetries'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">times</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">How many times to retry when a NFO fails to download. If set to 0, we will not retry. The max is 7.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
<!-- Password Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Password Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="end" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-file-archive mr-1"></i>Download Last Compressed File
|
||||
</label>
|
||||
<select id="end" name="end" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['end'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Try to download the last rar or zip file? (This is good if most of the files are at the end.) Note: The first rar/zip is still downloaded.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="showpasswordedrelease" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-lock mr-1"></i>Show Passworded Releases
|
||||
</label>
|
||||
<select id="showpasswordedrelease" name="showpasswordedrelease" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($passworded['ids'] as $index => $passwordedId)
|
||||
<option value="{{ $passwordedId }}" {{ ($site['showpasswordedrelease'] ?? '') == $passwordedId ? 'selected' : '' }}>
|
||||
{{ $passworded['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to show passworded releases in browse, search, api and rss feeds.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,141 @@
|
||||
<!-- Advanced - Postprocessing Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Advanced - Postprocessing Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="timeoutseconds" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-clock mr-1"></i>Time in Seconds to Kill Unrar/7zip/Mediainfo/FFmpeg/Avconv
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="timeoutseconds" name="timeoutseconds" value="{{ $site['timeoutseconds'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">seconds</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">How much time to wait for unrar/7zip/mediainfo/ffmpeg/avconv before killing it, set to 0 to disable. 60 is a good value. Requires the GNU Timeout path to be set.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="releaseprocessingtimeout" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-hourglass-half mr-1"></i>Per-Release Processing Timeout
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="releaseprocessingtimeout" name="releaseprocessingtimeout" value="{{ $site['releaseprocessingtimeout'] ?? '120' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">seconds</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">Maximum wall-clock seconds to spend processing a single release before skipping it. Keep this below the multiprocessing child timeout. Set to 0 to disable. Default: 120.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxpptimeoutcount" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-trash-alt mr-1"></i>Max PP Timeout Strikes Before Deletion
|
||||
</label>
|
||||
<input type="text" id="maxpptimeoutcount" name="maxpptimeoutcount" value="{{ $site['maxpptimeoutcount'] ?? '3' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">Number of times a release can time out during post-processing before it is permanently deleted. Default: 3.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxaddprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-list-ol mr-1"></i>Maximum Add PP Per Run
|
||||
</label>
|
||||
<input type="text" id="maxaddprocessed" name="maxaddprocessed" value="{{ $site['maxaddprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">Releases claimed per worker batch for passwords, previews, and media info. Larger batches reduce query overhead but increase each worker's memory and runtime. Each thread can hold one NNTP connection.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxpartsprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-download mr-1"></i>Maximum Add PP Parts Downloaded
|
||||
</label>
|
||||
<input type="text" id="maxpartsprocessed" name="maxpartsprocessed" value="{{ $site['maxpartsprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">If a part fails to download while post processing, this will retry up to the amount you set, then give up.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="passchkattempts" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-check-double mr-1"></i>Maximum Add PP Parts Checked
|
||||
</label>
|
||||
<input type="text" id="passchkattempts" name="passchkattempts" value="{{ $site['passchkattempts'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">This overrides the above setting if set above 1. How many parts to check for a password before giving up. This slows down post processing massively, better to leave it 1.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxrageprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tv mr-1"></i>Maximum TV Per Run
|
||||
</label>
|
||||
<input type="text" id="maxrageprocessed" name="maxrageprocessed" value="{{ $site['maxrageprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum amount of TV shows to processper run.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maximdbprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-film mr-1"></i>Maximum Movies Per Run
|
||||
</label>
|
||||
<input type="text" id="maximdbprocessed" name="maximdbprocessed" value="{{ $site['maximdbprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum amount of movies to process with IMDB per run.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxanidbprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-dragon mr-1"></i>Maximum AniDB Per Run
|
||||
</label>
|
||||
<input type="text" id="maxanidbprocessed" name="maxanidbprocessed" value="{{ $site['maxanidbprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum amount of anime to process with anidb per run.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxmusicprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-music mr-1"></i>Maximum Music Per Run
|
||||
</label>
|
||||
<input type="text" id="maxmusicprocessed" name="maxmusicprocessed" value="{{ $site['maxmusicprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum amount of music to process with metadata lookups per run. This does not use an NNTP connection.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxgamesprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-gamepad mr-1"></i>Maximum Games Per Run
|
||||
</label>
|
||||
<input type="text" id="maxgamesprocessed" name="maxgamesprocessed" value="{{ $site['maxgamesprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum amount of games to process with metadata lookups per run. This does not use an NNTP connection.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxbooksprocessed" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-book mr-1"></i>Maximum Books Per Run
|
||||
</label>
|
||||
<input type="text" id="maxbooksprocessed" name="maxbooksprocessed" value="{{ $site['maxbooksprocessed'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum amount of books to process with metadata lookups per run. This does not use an NNTP connection</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="fixnamesperrun" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-edit mr-1"></i>fixReleaseNames Per Run
|
||||
</label>
|
||||
<input type="text" id="fixnamesperrun" name="fixnamesperrun" value="{{ $site['fixnamesperrun'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum number of releases to check per run (threaded script only).</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="amazonsleep" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-hourglass mr-1"></i>Metadata Lookup Sleep Time
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<input type="text" id="amazonsleep" name="amazonsleep" value="{{ $site['amazonsleep'] ?? '' }}"
|
||||
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md">ms</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">Sleep time in milliseconds to wait between external metadata requests. If you thread post-proc, multiply by the number of threads. ie Postprocessing Threads = 12, sleep time = 12000</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!-- Registration Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">User Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-user-plus mr-1"></i>Registration Management
|
||||
</label>
|
||||
<div class="rounded-lg border border-blue-200 bg-blue-50 p-4 dark:border-blue-800 dark:bg-blue-900/20">
|
||||
<p class="text-sm text-blue-800 dark:text-blue-200">
|
||||
Registration status, scheduled open periods, and registration activity are now managed from the dedicated registration admin page.
|
||||
</p>
|
||||
<a href="{{ route('admin.registrations.index') }}"
|
||||
class="mt-3 inline-flex items-center rounded-md border border-blue-600 bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:border-blue-400/40 dark:bg-blue-500/90 dark:hover:bg-blue-400 dark:focus:ring-offset-gray-900">
|
||||
<i class="fas fa-arrow-up-right-from-square mr-2"></i>Open Registration Admin
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="userdownloadpurgedays" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-calendar mr-1"></i>User Downloads Purge Days
|
||||
</label>
|
||||
<input type="text" id="userdownloadpurgedays" name="userdownloadpurgedays" value="{{ $site['userdownloadpurgedays'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of days to preserve user download history, for use when checking limits being hit. Set to zero will remove all records of what users download, but retain history of when, so that role based limits can still be applied.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="userhostexclusion" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-shield mr-1"></i>IP Whitelist
|
||||
</label>
|
||||
<input type="text" id="userhostexclusion" name="userhostexclusion" value="{{ $site['userhostexclusion'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">A comma separated list of IP addresses which will be excluded from user limits on number of requests and downloads per IP address. Include values for google reader and other shared services which may be being used.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,69 @@
|
||||
<!-- Advanced - Threaded Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Advanced - Threaded Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="binarythreads" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tasks mr-1"></i>Update Binaries Threads
|
||||
</label>
|
||||
<input type="text" id="binarythreads" name="binarythreads" value="{{ $site['binarythreads'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of threads for update_binaries. If you notice that you are getting a lot of parts into the missed_parts table, it is possible that you USP is not keeping up with the requests. Try to reduce the threads. At least until the cause can be determined.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="backfillthreads" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tasks mr-1"></i>Backfill Threads
|
||||
</label>
|
||||
<input type="text" id="backfillthreads" name="backfillthreads" value="{{ $site['backfillthreads'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of threads for backfill.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="releasethreads" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tasks mr-1"></i>Update Releases Threads
|
||||
</label>
|
||||
<input type="text" id="releasethreads" name="releasethreads" value="{{ $site['releasethreads'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of threads for releases update scripts.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="postthreads" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tasks mr-1"></i>Postprocessing Additional Threads
|
||||
</label>
|
||||
<input type="text" id="postthreads" name="postthreads" value="{{ $site['postthreads'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">Maximum simultaneous additional-processing workers and NNTP sessions. Workers reuse their process and connection across bounded batches; raise this only within CPU, memory, disk I/O, and provider connection limits.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="nfothreads" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tasks mr-1"></i>NFO Threads
|
||||
</label>
|
||||
<input type="text" id="nfothreads" name="nfothreads" value="{{ $site['nfothreads'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of threads for nfo postprocessing. The max is 16, if you set anything higher it will use 16.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="postthreadsnon" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tasks mr-1"></i>Postprocessing Video Metadata Threads
|
||||
</label>
|
||||
<input type="text" id="postthreadsnon" name="postthreadsnon" value="{{ $site['postthreadsnon'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of threads for video metadata postprocessing. This includes movies, anime and tv lookups.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="fixnamethreads" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-tasks mr-1"></i>fixReleaseNames Threads
|
||||
</label>
|
||||
<input type="text" id="fixnamethreads" name="fixnamethreads" value="{{ $site['fixnamethreads'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of threads for fixReleasesNames. This includes md5, nfos, par2 and filenames.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,186 @@
|
||||
<!-- Usenet Settings -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-6">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Usenet Settings</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label for="nzbsplitlevel" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-folder-tree mr-1"></i>NZB File Path Level Deep
|
||||
</label>
|
||||
<input type="text" id="nzbsplitlevel" name="nzbsplitlevel" value="{{ $site['nzbsplitlevel'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">Levels deep to store the nzb Files. <strong>If you change this you must run the misc/testing/DB/nzb-reorg script!</strong></p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="partretentionhours" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-clock mr-1"></i>Part Retention Hours
|
||||
</label>
|
||||
<input type="text" id="partretentionhours" name="partretentionhours" value="{{ $site['partretentionhours'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of hours incomplete parts and binaries will be retained.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="releaseretentiondays" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-calendar-days mr-1"></i>Release Retention
|
||||
</label>
|
||||
<input type="text" id="releaseretentiondays" name="releaseretentiondays" value="{{ $site['releaseretentiondays'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of days releases will be retained for use throughout site. Set to 0 to disable.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="miscotherretentionhours" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-hourglass mr-1"></i>Other->Misc Retention Hours
|
||||
</label>
|
||||
<input type="text" id="miscotherretentionhours" name="miscotherretentionhours" value="{{ $site['miscotherretentionhours'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of hours releases categorized as Misc->Other will be retained. Set to 0 to disable.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="mischashedretentionhours" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-hashtag mr-1"></i>Other->Hashed Retention Hours
|
||||
</label>
|
||||
<input type="text" id="mischashedretentionhours" name="mischashedretentionhours" value="{{ $site['mischashedretentionhours'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The number of hours releases categorized as Misc->Hashed will be retained. Set to 0 to disable.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="partsdeletechunks" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-trash mr-1"></i>Parts Delete In Chunks
|
||||
</label>
|
||||
<input type="text" id="partsdeletechunks" name="partsdeletechunks" value="{{ $site['partsdeletechunks'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">Default is 0 (off), which will remove parts in one go. If backfilling or importing and parts table is large, using chunks of 5000+ will speed up removal. Normal indexing is fastest with this setting at 0.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="minfilestoformrelease" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-file-alt mr-1"></i>Minimum Files to Make a Release
|
||||
</label>
|
||||
<input type="text" id="minfilestoformrelease" name="minfilestoformrelease" value="{{ $site['minfilestoformrelease'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The minimum number of files to make a release. i.e. if set to two, then releases which only contain one file will not be created.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="minsizetoformrelease" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-compress mr-1"></i>Minimum File Size to Make a Release
|
||||
</label>
|
||||
<input type="text" id="minsizetoformrelease" name="minsizetoformrelease" value="{{ $site['minsizetoformrelease'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The minimum total size in bytes to make a release. If set to 0, then ignored.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxsizetoformrelease" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-expand mr-1"></i>Maximum File Size to Make a Release
|
||||
</label>
|
||||
<input type="text" id="maxsizetoformrelease" name="maxsizetoformrelease" value="{{ $site['maxsizetoformrelease'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum total size in bytes to make a release. If set to 0, then ignored. Only deletes during release creation.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="completionpercent" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-percentage mr-1"></i>Minimum Completion Percent
|
||||
</label>
|
||||
<input type="text" id="completionpercent" name="completionpercent" value="{{ $site['completionpercent'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The minimum completion percent to make a release. i.e. if set to 97, then releases under 97% completion will not be created. If set to 0, then ignored.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="grabstatus" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-sync mr-1"></i>Update Grabs
|
||||
</label>
|
||||
<select id="grabstatus" name="grabstatus" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['grabstatus'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to update download counts when someone downloads a release.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="crossposttime" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-clock mr-1"></i>Crossposted Time Check
|
||||
</label>
|
||||
<input type="text" id="crossposttime" name="crossposttime" value="{{ $site['crossposttime'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The time in hours to check for crossposted releases - this will delete 1 of the releases if the 2 are posted by the same person in the same time period.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="maxmssgs" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-envelope mr-1"></i>Max Messages
|
||||
</label>
|
||||
<input type="text" id="maxmssgs" name="maxmssgs" value="{{ $site['maxmssgs'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum number of messages to fetch at a time from the server.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="max_headers_iteration" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-list-ol mr-1"></i>Max Headers Iteration
|
||||
</label>
|
||||
<input type="text" id="max_headers_iteration" name="max_headers_iteration" value="{{ $site['max_headers_iteration'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The maximum number of headers that update binaries sees as the total range. This ensures that a total of no more than this is attempted to be downloaded at one time per group.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="newgroupscanmethod" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-question-circle mr-1"></i>Where to Start New Groups
|
||||
</label>
|
||||
<select id="newgroupscanmethod" name="newgroupscanmethod" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 mb-2">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['newgroupscanmethod'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $newgroupscan_names[$index] ?? $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<label for="newgroupdaystoscan" class="block text-xs text-gray-600 dark:text-gray-400 mb-1">Days to Scan</label>
|
||||
<input type="text" id="newgroupdaystoscan" name="newgroupdaystoscan" value="{{ $site['newgroupdaystoscan'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
</div>
|
||||
<div>
|
||||
<label for="newgroupmsgstoscan" class="block text-xs text-gray-600 dark:text-gray-400 mb-1">Posts to Scan</label>
|
||||
<input type="text" id="newgroupmsgstoscan" name="newgroupmsgstoscan" value="{{ $site['newgroupmsgstoscan'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-500">Scan back X (posts/days) for each new group? Can backfill to scan further.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="safebackfilldate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-calendar-alt mr-1"></i>Safe Backfill Date
|
||||
</label>
|
||||
<input type="text" id="safebackfilldate" name="safebackfilldate" value="{{ $site['safebackfilldate'] ?? '' }}"
|
||||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
<p class="mt-1 text-sm text-gray-500">The target date for safe backfill. Format: YYYY-MM-DD</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="disablebackfillgroup" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
<i class="fas fa-power-off mr-1"></i>Auto Disable Groups During Backfill
|
||||
</label>
|
||||
<select id="disablebackfillgroup" name="disablebackfillgroup" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100">
|
||||
@foreach($yesno['ids'] as $index => $yesnoId)
|
||||
<option value="{{ $yesnoId }}" {{ ($site['disablebackfillgroup'] ?? '') == $yesnoId ? 'selected' : '' }}>
|
||||
{{ $yesno['names'][$index] }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<p class="mt-1 text-sm text-gray-500">Whether to disable a group automatically during backfill if the target date has been reached.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h1 class="text-2xl font-semibold text-gray-800">
|
||||
@@ -494,5 +494,5 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm">
|
||||
<span class="{{ $incidentStatusBadge($incident->status) }}">{{ $incident->status->label() }}</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">{{ $incident->started_at->format('Y-m-d H:i') }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">{{ formatDateTime($incident->started_at) }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm space-x-2">
|
||||
<a href="{{ route('admin.status.edit', $incident) }}" class="text-blue-600 dark:text-blue-400 hover:underline">Edit</a>
|
||||
@if($incident->status !== IncidentStatusEnum::Resolved)
|
||||
@@ -121,7 +121,13 @@
|
||||
<button type="submit" class="text-emerald-600 dark:text-emerald-400 hover:underline">Resolve</button>
|
||||
</form>
|
||||
@endif
|
||||
<form action="{{ route('admin.status.destroy', $incident) }}" method="post" class="inline" onsubmit="return confirm('Delete this incident?');">
|
||||
<form action="{{ route('admin.status.destroy', $incident) }}" method="post" class="inline"
|
||||
x-data="confirmForm"
|
||||
data-message="Are you sure you want to delete this incident?"
|
||||
data-title="Delete Incident"
|
||||
data-type="danger"
|
||||
data-confirm-text="Delete"
|
||||
@submit.prevent="submit()">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-red-600 dark:text-red-400 hover:underline">Delete</button>
|
||||
|
||||
@@ -78,47 +78,24 @@
|
||||
</div>
|
||||
|
||||
<!-- History Table -->
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-900">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Date
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
User
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Old Role
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
New Role
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Old Expiry
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
New Expiry
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Stacked
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Reason
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Changed By
|
||||
</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
|
||||
<x-admin.data-table>
|
||||
<x-slot:head>
|
||||
<x-admin.th scope="col">Date</x-admin.th>
|
||||
<x-admin.th scope="col">User</x-admin.th>
|
||||
<x-admin.th scope="col">Old Role</x-admin.th>
|
||||
<x-admin.th scope="col">New Role</x-admin.th>
|
||||
<x-admin.th scope="col">Old Expiry</x-admin.th>
|
||||
<x-admin.th scope="col">New Expiry</x-admin.th>
|
||||
<x-admin.th scope="col">Stacked</x-admin.th>
|
||||
<x-admin.th scope="col">Reason</x-admin.th>
|
||||
<x-admin.th scope="col">Changed By</x-admin.th>
|
||||
<x-admin.th scope="col">Actions</x-admin.th>
|
||||
</x-slot:head>
|
||||
|
||||
@forelse($history as $record)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $record->created_at->format('Y-m-d H:i:s') }}
|
||||
{{ formatDateTimeSeconds($record->created_at) }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm">
|
||||
@if($record->user)
|
||||
@@ -148,10 +125,10 @@
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $record->old_expiry_date ? $record->old_expiry_date->format('Y-m-d H:i') : '-' }}
|
||||
{{ $record->old_expiry_date ? formatDateTime($record->old_expiry_date) : '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $record->new_expiry_date ? $record->new_expiry_date->format('Y-m-d H:i') : '-' }}
|
||||
{{ $record->new_expiry_date ? formatDateTime($record->new_expiry_date) : '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-center">
|
||||
@if($record->is_stacked)
|
||||
@@ -196,9 +173,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-admin.data-table>
|
||||
|
||||
<!-- Pagination -->
|
||||
@if($history->hasPages())
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
@forelse($history as $record)
|
||||
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
{{ $record->created_at->format('Y-m-d H:i:s') }}
|
||||
{{ formatDateTimeSeconds($record->created_at) }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">
|
||||
@if($record->oldRole)
|
||||
@@ -122,13 +122,13 @@
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $record->old_expiry_date ? $record->old_expiry_date->format('Y-m-d H:i') : '-' }}
|
||||
{{ $record->old_expiry_date ? formatDateTime($record->old_expiry_date) : '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $record->new_expiry_date ? $record->new_expiry_date->format('Y-m-d H:i') : '-' }}
|
||||
{{ $record->new_expiry_date ? formatDateTime($record->new_expiry_date) : '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ $record->effective_date ? $record->effective_date->format('Y-m-d H:i') : '-' }}
|
||||
{{ $record->effective_date ? formatDateTime($record->effective_date) : '-' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-center">
|
||||
@if($record->is_stacked)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6" x-data="adminDeletedUsers">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -193,10 +193,10 @@
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $user->created_at ? $user->created_at->format('Y-m-d H:i') : 'N/A' }}
|
||||
{{ $user->created_at ? formatDateTime($user->created_at) : 'N/A' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $user->deleted_at ? $user->deleted_at->format('Y-m-d H:i') : 'N/A' }}
|
||||
{{ $user->deleted_at ? formatDateTime($user->deleted_at) : 'N/A' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
@if($user->deleted_by === 'Self')
|
||||
@@ -256,13 +256,9 @@
|
||||
{{ $deletedusers->links() }}
|
||||
</div>
|
||||
@else
|
||||
<div class="px-6 py-12 text-center">
|
||||
<i class="fas fa-trash-restore text-gray-400 dark:text-gray-600 text-5xl mb-4"></i>
|
||||
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No deleted users found</h3>
|
||||
<p class="text-gray-500 dark:text-gray-400">There are no soft-deleted users matching your filters.</p>
|
||||
</div>
|
||||
<x-admin.empty-state icon="fas fa-trash-restore" title="No deleted users found" message="There are no soft-deleted users matching your filters." />
|
||||
@endif
|
||||
</div>
|
||||
</x-admin.card>
|
||||
</div>
|
||||
|
||||
<!-- Hidden form for individual actions -->
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@section('content')
|
||||
<div class="space-y-6">
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm">
|
||||
<x-admin.card>
|
||||
<!-- Header -->
|
||||
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<div class="flex justify-between items-center">
|
||||
@@ -90,7 +90,7 @@
|
||||
</label>
|
||||
@if(!is_array($user) && !empty($user->pending_roles_id))
|
||||
@php
|
||||
$pendingRole = \Spatie\Permission\Models\Role::find($user->pending_roles_id);
|
||||
$pendingRole = $user->pendingRole;
|
||||
@endphp
|
||||
<div class="mb-2 p-2 bg-blue-100 dark:bg-blue-900/30 border border-blue-300 dark:border-blue-700 rounded text-sm text-blue-800 dark:text-blue-200 flex items-center">
|
||||
<i class="fas fa-info-circle mr-2"></i>
|
||||
@@ -110,331 +110,9 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Role Expiry Date -->
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-linear-to-br from-gray-50 to-white dark:from-gray-900 dark:to-gray-800">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label for="rolechangedate" class="text-sm font-medium text-gray-700 dark:text-gray-300 flex items-center">
|
||||
<i class="fas fa-calendar-alt mr-2 text-blue-600 dark:text-blue-400"></i>
|
||||
Role Expiry Date
|
||||
</label>
|
||||
@if(!empty($user->rolechangedate ?? ''))
|
||||
@php
|
||||
$expiryDate = \Carbon\Carbon::parse($user->rolechangedate);
|
||||
$isExpired = $expiryDate->isPast();
|
||||
$daysUntilExpiry = abs($expiryDate->diffInDays(now()));
|
||||
$totalHours = abs($expiryDate->diffInHours(now()));
|
||||
$hoursUntilExpiry = abs($totalHours % 24);
|
||||
@endphp
|
||||
@if($isExpired)
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 animate-pulse">
|
||||
<i class="fas fa-exclamation-triangle mr-1"></i> Expired
|
||||
</span>
|
||||
@elseif($daysUntilExpiry <= 7)
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200">
|
||||
<i class="fas fa-exclamation-circle mr-1"></i> Expiring Soon
|
||||
</span>
|
||||
@elseif($daysUntilExpiry <= 30)
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200">
|
||||
<i class="fas fa-check-circle mr-1"></i> Active
|
||||
</span>
|
||||
@else
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200">
|
||||
<i class="fas fa-check-circle mr-1"></i> Active
|
||||
</span>
|
||||
@endif
|
||||
@else
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-300">
|
||||
<i class="fas fa-infinity mr-1"></i> No Expiry
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
@include('admin.users.partials.role-expiry')
|
||||
|
||||
<!-- Hidden input for form submission -->
|
||||
<input type="hidden" id="rolechangedate" name="rolechangedate" value="{{ is_array($user) ? ($user['rolechangedate'] ?? '') : (isset($user->rolechangedate) ? \Carbon\Carbon::parse($user->rolechangedate)->format('Y-m-d\TH:i:s') : '') }}">
|
||||
|
||||
<!-- Hidden field to store the ORIGINAL user expiry date for proper stacking calculations -->
|
||||
<input type="hidden" id="original_user_expiry" value="{{ is_array($user) ? ($user['rolechangedate'] ?? '') : (isset($user->rolechangedate) ? \Carbon\Carbon::parse($user->rolechangedate)->format('Y-m-d\TH:i:s') : '') }}">
|
||||
|
||||
<!-- Custom DateTime Picker -->
|
||||
<div class="grid grid-cols-5 gap-3">
|
||||
<!-- Year Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-calendar-alt mr-1"></i>Year
|
||||
</label>
|
||||
<select id="expiry_year"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
@for($y = date('Y'); $y <= date('Y') + 50; $y++)
|
||||
<option value="{{ $y }}">{{ $y }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Month Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-calendar mr-1"></i>Month
|
||||
</label>
|
||||
<select id="expiry_month"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
<option value="01">Jan</option>
|
||||
<option value="02">Feb</option>
|
||||
<option value="03">Mar</option>
|
||||
<option value="04">Apr</option>
|
||||
<option value="05">May</option>
|
||||
<option value="06">Jun</option>
|
||||
<option value="07">Jul</option>
|
||||
<option value="08">Aug</option>
|
||||
<option value="09">Sep</option>
|
||||
<option value="10">Oct</option>
|
||||
<option value="11">Nov</option>
|
||||
<option value="12">Dec</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Day Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-calendar-day mr-1"></i>Day
|
||||
</label>
|
||||
<select id="expiry_day"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
@for($d = 1; $d <= 31; $d++)
|
||||
<option value="{{ sprintf('%02d', $d) }}">{{ $d }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Hour Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-clock mr-1"></i>Hour
|
||||
</label>
|
||||
<select id="expiry_hour"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
@for($h = 0; $h <= 23; $h++)
|
||||
<option value="{{ sprintf('%02d', $h) }}">{{ sprintf('%02d', $h) }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Minute Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-hourglass-half mr-1"></i>Min
|
||||
</label>
|
||||
<select id="expiry_minute"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
@for($m = 0; $m <= 59; $m++)
|
||||
<option value="{{ sprintf('%02d', $m) }}">{{ sprintf('%02d', $m) }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Selection Display -->
|
||||
<div id="datetime_preview" class="mt-3 p-3 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg hidden">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">
|
||||
<i class="fas fa-info-circle mr-2"></i>Selected:
|
||||
</span>
|
||||
<span id="datetime_display" class="text-base font-bold text-blue-600 dark:text-blue-400"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Action Buttons -->
|
||||
<div class="mt-3 space-y-2">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button type="button" data-expiry-action="set" data-days="1" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-clock mr-1"></i> +1 Day
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="7" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-calendar-week mr-1"></i> +1 Week
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="30" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-calendar-alt mr-1"></i> +1 Month
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="90" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-calendar mr-1"></i> +3 Months
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="365" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-purple-700 dark:text-purple-300 bg-purple-50 dark:bg-purple-900/30 border border-purple-200 dark:border-purple-800 rounded-md hover:bg-purple-100 dark:hover:bg-purple-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-calendar-check mr-1"></i> +1 Year
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button type="button" data-expiry-action="set" data-days="0" data-hours="1" class="px-3 py-1.5 text-xs font-medium text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-md hover:bg-green-100 dark:hover:bg-green-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-hourglass-start mr-1"></i> +1 Hour
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="0" data-hours="6" class="px-3 py-1.5 text-xs font-medium text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-md hover:bg-green-100 dark:hover:bg-green-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-hourglass-half mr-1"></i> +6 Hours
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="0" data-hours="12" class="px-3 py-1.5 text-xs font-medium text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-md hover:bg-green-100 dark:hover:bg-green-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-hourglass-end mr-1"></i> +12 Hours
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="0" data-hours="24" class="px-3 py-1.5 text-xs font-medium text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-md hover:bg-green-100 dark:hover:bg-green-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-clock mr-1"></i> +24 Hours
|
||||
</button>
|
||||
<button type="button" data-expiry-action="end-of-day" class="px-3 py-1.5 text-xs font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/30 border border-indigo-200 dark:border-indigo-800 rounded-md hover:bg-indigo-100 dark:hover:bg-indigo-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-moon mr-1"></i> End of Today
|
||||
</button>
|
||||
<button type="button" data-expiry-action="clear" class="px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-md hover:bg-gray-100 dark:hover:bg-gray-600 transition-all hover:scale-105">
|
||||
<i class="fas fa-times-circle mr-1"></i> Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status Information -->
|
||||
@if(!empty($user->rolechangedate ?? ''))
|
||||
<div class="mt-3 p-3 rounded-lg {{ $isExpired ? 'bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800' : ($daysUntilExpiry <= 7 ? 'bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800' : 'bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800') }}">
|
||||
<div class="flex items-start">
|
||||
<i class="fa {{ $isExpired ? 'fa-exclamation-triangle text-red-600 dark:text-red-400' : ($daysUntilExpiry <= 7 ? 'fa-clock text-yellow-600 dark:text-yellow-400' : 'fa-info-circle text-blue-600 dark:text-blue-400') }} mt-0.5 mr-2"></i>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm font-medium {{ $isExpired ? 'text-red-800 dark:text-red-200' : ($daysUntilExpiry <= 7 ? 'text-yellow-800 dark:text-yellow-200' : 'text-blue-800 dark:text-blue-200') }}">
|
||||
@if($isExpired)
|
||||
Role expired {{ $expiryDate->diffForHumans() }}
|
||||
@else
|
||||
Role expires {{ $expiryDate->diffForHumans() }}
|
||||
@endif
|
||||
</p>
|
||||
<p class="text-xs {{ $isExpired ? 'text-red-700 dark:text-red-300' : ($daysUntilExpiry <= 7 ? 'text-yellow-700 dark:text-yellow-300' : 'text-blue-700 dark:text-blue-300') }} mt-1">
|
||||
<i class="fas fa-calendar-alt mr-1"></i>{{ $expiryDate->format('F j, Y') }}
|
||||
<span class="mx-2">•</span>
|
||||
<i class="fas fa-clock mr-1"></i>{{ $expiryDate->format('g:i A') }}
|
||||
</p>
|
||||
@if($daysUntilExpiry <= 7 && !$isExpired)
|
||||
<p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">
|
||||
<i class="fas fa-hourglass-half mr-1"></i>{{ $daysUntilExpiry }} day{{ $daysUntilExpiry != 1 ? 's' : '' }} and {{ $hoursUntilExpiry }} hour{{ $hoursUntilExpiry != 1 ? 's' : '' }} remaining
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<p class="mt-3 text-xs text-gray-600 dark:text-gray-400 flex items-center">
|
||||
<i class="fas fa-lightbulb mr-1.5 text-yellow-500"></i>
|
||||
<span>Leave empty for permanent role assignment, or use quick actions above to set an expiry date and time.</span>
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Pending Role Information -->
|
||||
@if(!is_array($user))
|
||||
@php
|
||||
$allPendingRoles = $user->getAllPendingStackedRoles();
|
||||
@endphp
|
||||
@if($allPendingRoles->count() > 0)
|
||||
<div class="border-2 border-blue-300 dark:border-blue-600 rounded-lg p-4 bg-linear-to-br from-blue-50 via-blue-50 to-white dark:from-blue-900/30 dark:via-blue-900/20 dark:to-gray-800 shadow-md">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label class="text-sm font-semibold text-blue-700 dark:text-blue-300 flex items-center">
|
||||
<i class="fas fa-layer-group mr-2 text-lg"></i>
|
||||
Pending Stacked Role{{ $allPendingRoles->count() > 1 ? 's' : '' }}
|
||||
<span class="ml-2 px-2 py-0.5 bg-blue-600 text-white text-xs rounded-full">{{ $allPendingRoles->count() }}</span>
|
||||
</label>
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-bold rounded-full bg-blue-200 dark:bg-blue-800 text-blue-800 dark:text-blue-100 animate-pulse">
|
||||
<i class="fas fa-clock mr-1"></i> SCHEDULED
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
@foreach($allPendingRoles as $index => $pendingRoleInfo)
|
||||
<div class="bg-white dark:bg-gray-700 rounded-md p-3 border border-blue-200 dark:border-blue-700 @if(!$loop->last) mb-2 @endif">
|
||||
<div class="flex items-center mb-2">
|
||||
<span class="flex items-center justify-center w-6 h-6 bg-blue-600 text-white text-xs font-bold rounded-full mr-2">
|
||||
{{ $index + 1 }}
|
||||
</span>
|
||||
<span class="text-sm font-bold text-blue-700 dark:text-blue-300">{{ $pendingRoleInfo['role_name'] }}</span>
|
||||
</div>
|
||||
<div class="ml-8 space-y-1 text-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">
|
||||
<i class="fas fa-play-circle text-green-500 mr-1"></i>Starts:
|
||||
</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-gray-100">{{ $pendingRoleInfo['start_date']->format('M j, Y g:i A') }}</span>
|
||||
</div>
|
||||
@if($pendingRoleInfo['end_date'])
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">
|
||||
<i class="fas fa-stop-circle text-red-400 mr-1"></i>Ends:
|
||||
</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-gray-100">{{ $pendingRoleInfo['end_date']->format('M j, Y g:i A') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">Time until activation:</span>
|
||||
<span class="text-sm font-semibold text-blue-600 dark:text-blue-400">{{ $pendingRoleInfo['start_date']->diffForHumans() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between mt-3">
|
||||
<p class="text-xs text-blue-700 dark:text-blue-300">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
These roles will automatically activate in sequence as each previous role expires
|
||||
</p>
|
||||
@if(!empty($user->pending_roles_id))
|
||||
<label class="inline-flex items-center px-3 py-2 rounded-md bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-700 hover:bg-red-100 dark:hover:bg-red-900/50 cursor-pointer transition-all">
|
||||
<input type="checkbox" name="cancel_pending_role" value="1"
|
||||
class="rounded border-red-300 dark:border-red-600 bg-white dark:bg-gray-700 text-red-600 dark:text-red-500 shadow-sm focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500 dark:focus:border-red-400 cursor-pointer">
|
||||
<span class="ml-2 text-xs text-red-700 dark:text-red-300 font-semibold">Cancel all pending roles</span>
|
||||
</label>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@elseif(!empty($user->pending_roles_id) && !empty($user->pending_role_start_date))
|
||||
{{-- Fallback to old display if no history records but pending_roles_id is set --}}
|
||||
<div class="border-2 border-blue-300 dark:border-blue-600 rounded-lg p-4 bg-linear-to-br from-blue-50 via-blue-50 to-white dark:from-blue-900/30 dark:via-blue-900/20 dark:to-gray-800 shadow-md">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label class="text-sm font-semibold text-blue-700 dark:text-blue-300 flex items-center">
|
||||
<i class="fas fa-layer-group mr-2 text-lg"></i>
|
||||
Pending Stacked Role
|
||||
</label>
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-bold rounded-full bg-blue-200 dark:bg-blue-800 text-blue-800 dark:text-blue-100 animate-pulse">
|
||||
<i class="fas fa-clock mr-1"></i> SCHEDULED
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$pendingRole = \Spatie\Permission\Models\Role::find($user->pending_roles_id);
|
||||
$pendingStartDate = \Carbon\Carbon::parse($user->pending_role_start_date);
|
||||
$daysUntilActivation = $pendingStartDate->diffInDays(now());
|
||||
@endphp
|
||||
|
||||
<div class="bg-white dark:bg-gray-700 rounded-md p-3 mb-3 border border-blue-200 dark:border-blue-700">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Will change to:</span>
|
||||
<span class="text-sm font-bold text-blue-700 dark:text-blue-300">{{ $pendingRole->name ?? 'Unknown' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Activation date:</span>
|
||||
<span class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $pendingStartDate->format('M j, Y g:i A') }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">Time until activation:</span>
|
||||
<span class="text-sm font-semibold text-blue-600 dark:text-blue-400">{{ $pendingStartDate->diffForHumans(['parts' => 2]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<p class="text-xs text-blue-700 dark:text-blue-300">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
This role will automatically activate when the current role expires
|
||||
</p>
|
||||
<label class="inline-flex items-center px-3 py-2 rounded-md bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-700 hover:bg-red-100 dark:hover:bg-red-900/50 cursor-pointer transition-all">
|
||||
<input type="checkbox" name="cancel_pending_role" value="1"
|
||||
class="rounded border-red-300 dark:border-red-600 bg-white dark:bg-gray-700 text-red-600 dark:text-red-500 shadow-sm focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500 dark:focus:border-red-400 cursor-pointer">
|
||||
<span class="ml-2 text-xs text-red-700 dark:text-red-300 font-semibold">Cancel pending role</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
@include('admin.users.partials.pending-role')
|
||||
|
||||
<!-- Role Stacking Option -->
|
||||
@if(!is_array($user) && !empty($user->rolechangedate) && \Carbon\Carbon::parse($user->rolechangedate)->isFuture())
|
||||
@@ -455,78 +133,7 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(!empty($user['id']))
|
||||
<!-- Grabs -->
|
||||
<div>
|
||||
<label for="grabs" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Grabs
|
||||
</label>
|
||||
<input type="number"
|
||||
id="grabs"
|
||||
name="grabs"
|
||||
value="{{ is_array($user) ? ($user['grabs'] ?? 0) : ($user->grabs ?? 0) }}"
|
||||
disabled
|
||||
class="w-full px-3 py-2 bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border border-gray-300 dark:border-gray-600 rounded-md cursor-not-allowed opacity-75">
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
<i class="fas fa-info-circle mr-1"></i>Total grabs is read-only and automatically tracked
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Daily Activity Stats -->
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-linear-to-br from-gray-50 to-white dark:from-gray-900 dark:to-gray-800">
|
||||
<div class="flex items-center mb-3">
|
||||
<i class="fas fa-chart-line mr-2 text-purple-600 dark:text-purple-400"></i>
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Daily Activity (Last 24 Hours)
|
||||
</label>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- Daily API Requests -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-xs text-blue-600 dark:text-blue-400 font-medium uppercase tracking-wide">
|
||||
<i class="fas fa-code mr-1"></i>API Requests
|
||||
</p>
|
||||
<p class="text-2xl font-bold text-blue-900 dark:text-blue-100 mt-1">
|
||||
{{ is_array($user) ? ($user['daily_api_count'] ?? 0) : ($user->daily_api_count ?? 0) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-blue-600 dark:text-blue-400">
|
||||
<i class="fas fa-code text-3xl opacity-20"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-blue-600 dark:text-blue-400 mt-2">
|
||||
<i class="fas fa-clock mr-1"></i>In the last 24 hours
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Daily Downloads -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-xs text-green-600 dark:text-green-400 font-medium uppercase tracking-wide">
|
||||
<i class="fas fa-download mr-1"></i>Downloads
|
||||
</p>
|
||||
<p class="text-2xl font-bold text-green-900 dark:text-green-100 mt-1">
|
||||
{{ is_array($user) ? ($user['daily_download_count'] ?? 0) : ($user->daily_download_count ?? 0) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-green-600 dark:text-green-400">
|
||||
<i class="fas fa-download text-3xl opacity-20"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-green-600 dark:text-green-400 mt-2">
|
||||
<i class="fas fa-clock mr-1"></i>In the last 24 hours
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-3 text-xs text-gray-600 dark:text-gray-400 flex items-center">
|
||||
<i class="fas fa-info-circle mr-1.5 text-blue-500"></i>
|
||||
<span>These counters show activity from the past 24 hours and are automatically updated.</span>
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
@include('admin.users.partials.activity-stats')
|
||||
|
||||
<!-- Invites -->
|
||||
<div>
|
||||
@@ -551,160 +158,7 @@
|
||||
class="w-full px-3 py-2 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500">{{ is_array($user) ? ($user['notes'] ?? '') : ($user->notes ?? '') }}</textarea>
|
||||
</div>
|
||||
|
||||
@if(!empty($user['id']))
|
||||
<!-- View Preferences -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Category Preferences
|
||||
</label>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="movieview"
|
||||
name="movieview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['movieview'] ?? 0) : ($user->movieview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="movieview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Movies</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="musicview"
|
||||
name="musicview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['musicview'] ?? 0) : ($user->musicview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="musicview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Music</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="gameview"
|
||||
name="gameview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['gameview'] ?? 0) : ($user->gameview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="gameview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Games</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="consoleview"
|
||||
name="consoleview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['consoleview'] ?? 0) : ($user->consoleview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="consoleview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Console</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="bookview"
|
||||
name="bookview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['bookview'] ?? 0) : ($user->bookview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="bookview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Books</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if(!is_array($user))
|
||||
<div class="mt-6 rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-900" @if($user->passkeys->isNotEmpty()) x-data="adminUserPasskeys" @endif>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-sm font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-fingerprint mr-2 text-blue-600 dark:text-blue-400"></i>Passkeys (WebAuthn)
|
||||
</h3>
|
||||
<span class="rounded-full bg-blue-100 px-2.5 py-1 text-xs font-medium text-blue-800 dark:bg-blue-900/30 dark:text-blue-200">
|
||||
{{ $user->passkeys->count() }} registered
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if($user->passkeys->isEmpty())
|
||||
<div class="mt-4 rounded-lg border border-dashed border-gray-300 p-3 text-sm text-gray-600 dark:border-gray-600 dark:text-gray-300">
|
||||
This user has no registered passkeys. They can create one from their profile security settings.
|
||||
</div>
|
||||
@else
|
||||
<div class="mt-4 overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<table class="min-w-full divide-y divide-gray-200 text-sm dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-800">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-700 dark:text-gray-300">Name</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-700 dark:text-gray-300">Created</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-700 dark:text-gray-300">Last used</th>
|
||||
<th class="px-4 py-3 text-right font-semibold text-gray-700 dark:text-gray-300">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-900">
|
||||
@foreach($user->passkeys as $passkey)
|
||||
<tr>
|
||||
<td class="px-4 py-3 text-gray-900 dark:text-gray-100">{{ $passkey->name }}</td>
|
||||
<td class="px-4 py-3 text-gray-600 dark:text-gray-300">{{ optional($passkey->created_at)->diffForHumans() ?? 'Unknown' }}</td>
|
||||
<td class="px-4 py-3 text-gray-600 dark:text-gray-300">{{ optional($passkey->last_used_at)->diffForHumans() ?? 'Not used yet' }}</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<form method="POST" action="{{ route('admin.user-passkey.destroy', $passkey) }}" class="inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button
|
||||
type="submit"
|
||||
data-confirm="Delete this passkey? The user will lose this device as a login method."
|
||||
class="confirm-link rounded-lg border border-red-300 px-3 py-1.5 text-xs font-medium text-red-700 hover:bg-red-50 dark:border-red-700 dark:text-red-300 dark:hover:bg-red-900/30"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($user->passkeys->isNotEmpty())
|
||||
<div class="mt-4 rounded-lg border border-red-200 bg-red-50 p-4 dark:border-red-800 dark:bg-red-900/20">
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleDangerZone()"
|
||||
class="text-sm font-semibold text-red-700 hover:text-red-800 dark:text-red-300 dark:hover:text-red-200"
|
||||
>
|
||||
<i class="fas fa-exclamation-triangle mr-2"></i>Emergency actions
|
||||
</button>
|
||||
|
||||
<div x-show="showDangerZone" x-cloak class="mt-3 space-y-3">
|
||||
<p class="text-xs text-red-700 dark:text-red-300">
|
||||
Remove all passkeys if this user lost or replaced their passkey device.
|
||||
</p>
|
||||
|
||||
<form method="POST" action="{{ route('admin.user-passkeys.wipe') }}" class="space-y-3">
|
||||
@csrf
|
||||
<input type="hidden" name="user_id" value="{{ $user->id }}">
|
||||
|
||||
<div>
|
||||
<label for="passkey_wipe_confirmation" class="block text-xs font-medium text-red-700 dark:text-red-300">Type WIPE to confirm</label>
|
||||
<input
|
||||
id="passkey_wipe_confirmation"
|
||||
name="confirmation"
|
||||
x-model="wipeConfirm"
|
||||
type="text"
|
||||
required
|
||||
class="mt-1 w-full rounded-md border border-red-300 px-3 py-2 text-sm text-gray-900 focus:border-red-500 focus:ring-2 focus:ring-red-500 dark:border-red-700 dark:bg-gray-800 dark:text-gray-100"
|
||||
>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="!canWipe()"
|
||||
class="rounded-lg bg-red-600 px-4 py-2 text-xs font-semibold text-white hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-red-700 dark:hover:bg-red-800"
|
||||
>
|
||||
Remove ALL passkeys
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@include('admin.users.partials.view-preferences')
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex gap-3 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
@@ -717,6 +171,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-admin.card>
|
||||
@endsection
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
|
||||
<!-- Top Scrollbar -->
|
||||
<div class="overflow-x-auto border-b border-gray-200 dark:border-gray-700" id="topScroll">
|
||||
<div style="height: 1px;" id="topScrollContent"></div>
|
||||
<div class="h-px" id="topScrollContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto" id="bottomScroll">
|
||||
@@ -311,7 +311,7 @@
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ $user->created_at ? $user->created_at->format('Y-m-d') : 'N/A' }}
|
||||
{{ $user->created_at ? formatDate($user->created_at) : 'N/A' }}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100" title="API requests in last 24 hours">
|
||||
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
@if(!empty($user['id']))
|
||||
<!-- Grabs -->
|
||||
<div>
|
||||
<label for="grabs" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Grabs
|
||||
</label>
|
||||
<input type="number"
|
||||
id="grabs"
|
||||
name="grabs"
|
||||
value="{{ is_array($user) ? ($user['grabs'] ?? 0) : ($user->grabs ?? 0) }}"
|
||||
disabled
|
||||
class="w-full px-3 py-2 bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border border-gray-300 dark:border-gray-600 rounded-md cursor-not-allowed opacity-75">
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
<i class="fas fa-info-circle mr-1"></i>Total grabs is read-only and automatically tracked
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Daily Activity Stats -->
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-linear-to-br from-gray-50 to-white dark:from-gray-900 dark:to-gray-800">
|
||||
<div class="flex items-center mb-3">
|
||||
<i class="fas fa-chart-line mr-2 text-purple-600 dark:text-purple-400"></i>
|
||||
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Daily Activity (Last 24 Hours)
|
||||
</label>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- Daily API Requests -->
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-xs text-blue-600 dark:text-blue-400 font-medium uppercase tracking-wide">
|
||||
<i class="fas fa-code mr-1"></i>API Requests
|
||||
</p>
|
||||
<p class="text-2xl font-bold text-blue-900 dark:text-blue-100 mt-1">
|
||||
{{ is_array($user) ? ($user['daily_api_count'] ?? 0) : ($user->daily_api_count ?? 0) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-blue-600 dark:text-blue-400">
|
||||
<i class="fas fa-code text-3xl opacity-20"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-blue-600 dark:text-blue-400 mt-2">
|
||||
<i class="fas fa-clock mr-1"></i>In the last 24 hours
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Daily Downloads -->
|
||||
<div class="bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-xs text-green-600 dark:text-green-400 font-medium uppercase tracking-wide">
|
||||
<i class="fas fa-download mr-1"></i>Downloads
|
||||
</p>
|
||||
<p class="text-2xl font-bold text-green-900 dark:text-green-100 mt-1">
|
||||
{{ is_array($user) ? ($user['daily_download_count'] ?? 0) : ($user->daily_download_count ?? 0) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-green-600 dark:text-green-400">
|
||||
<i class="fas fa-download text-3xl opacity-20"></i>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-green-600 dark:text-green-400 mt-2">
|
||||
<i class="fas fa-clock mr-1"></i>In the last 24 hours
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-3 text-xs text-gray-600 dark:text-gray-400 flex items-center">
|
||||
<i class="fas fa-info-circle mr-1.5 text-blue-500"></i>
|
||||
<span>These counters show activity from the past 24 hours and are automatically updated.</span>
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,113 @@
|
||||
<!-- Pending Role Information -->
|
||||
@if(!is_array($user))
|
||||
@php
|
||||
$allPendingRoles = $user->getAllPendingStackedRoles();
|
||||
@endphp
|
||||
@if($allPendingRoles->count() > 0)
|
||||
<div class="border-2 border-blue-300 dark:border-blue-600 rounded-lg p-4 bg-linear-to-br from-blue-50 via-blue-50 to-white dark:from-blue-900/30 dark:via-blue-900/20 dark:to-gray-800 shadow-md">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label class="text-sm font-semibold text-blue-700 dark:text-blue-300 flex items-center">
|
||||
<i class="fas fa-layer-group mr-2 text-lg"></i>
|
||||
Pending Stacked Role{{ $allPendingRoles->count() > 1 ? 's' : '' }}
|
||||
<span class="ml-2 px-2 py-0.5 bg-blue-600 text-white text-xs rounded-full">{{ $allPendingRoles->count() }}</span>
|
||||
</label>
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-bold rounded-full bg-blue-200 dark:bg-blue-800 text-blue-800 dark:text-blue-100 animate-pulse">
|
||||
<i class="fas fa-clock mr-1"></i> SCHEDULED
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
@foreach($allPendingRoles as $index => $pendingRoleInfo)
|
||||
<div class="bg-white dark:bg-gray-700 rounded-md p-3 border border-blue-200 dark:border-blue-700 @if(!$loop->last) mb-2 @endif">
|
||||
<div class="flex items-center mb-2">
|
||||
<span class="flex items-center justify-center w-6 h-6 bg-blue-600 text-white text-xs font-bold rounded-full mr-2">
|
||||
{{ $index + 1 }}
|
||||
</span>
|
||||
<span class="text-sm font-bold text-blue-700 dark:text-blue-300">{{ $pendingRoleInfo['role_name'] }}</span>
|
||||
</div>
|
||||
<div class="ml-8 space-y-1 text-sm">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">
|
||||
<i class="fas fa-play-circle text-green-500 mr-1"></i>Starts:
|
||||
</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-gray-100">{{ $pendingRoleInfo['start_date']->format('M j, Y g:i A') }}</span>
|
||||
</div>
|
||||
@if($pendingRoleInfo['end_date'])
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">
|
||||
<i class="fas fa-stop-circle text-red-400 mr-1"></i>Ends:
|
||||
</span>
|
||||
<span class="font-semibold text-gray-900 dark:text-gray-100">{{ $pendingRoleInfo['end_date']->format('M j, Y g:i A') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">Time until activation:</span>
|
||||
<span class="text-sm font-semibold text-blue-600 dark:text-blue-400">{{ $pendingRoleInfo['start_date']->diffForHumans() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between mt-3">
|
||||
<p class="text-xs text-blue-700 dark:text-blue-300">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
These roles will automatically activate in sequence as each previous role expires
|
||||
</p>
|
||||
@if(!empty($user->pending_roles_id))
|
||||
<label class="inline-flex items-center px-3 py-2 rounded-md bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-700 hover:bg-red-100 dark:hover:bg-red-900/50 cursor-pointer transition-all">
|
||||
<input type="checkbox" name="cancel_pending_role" value="1"
|
||||
class="rounded border-red-300 dark:border-red-600 bg-white dark:bg-gray-700 text-red-600 dark:text-red-500 shadow-sm focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500 dark:focus:border-red-400 cursor-pointer">
|
||||
<span class="ml-2 text-xs text-red-700 dark:text-red-300 font-semibold">Cancel all pending roles</span>
|
||||
</label>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@elseif(!empty($user->pending_roles_id) && !empty($user->pending_role_start_date))
|
||||
{{-- Fallback to old display if no history records but pending_roles_id is set --}}
|
||||
<div class="border-2 border-blue-300 dark:border-blue-600 rounded-lg p-4 bg-linear-to-br from-blue-50 via-blue-50 to-white dark:from-blue-900/30 dark:via-blue-900/20 dark:to-gray-800 shadow-md">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label class="text-sm font-semibold text-blue-700 dark:text-blue-300 flex items-center">
|
||||
<i class="fas fa-layer-group mr-2 text-lg"></i>
|
||||
Pending Stacked Role
|
||||
</label>
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-bold rounded-full bg-blue-200 dark:bg-blue-800 text-blue-800 dark:text-blue-100 animate-pulse">
|
||||
<i class="fas fa-clock mr-1"></i> SCHEDULED
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$pendingRole = $user->pendingRole;
|
||||
$pendingStartDate = \Carbon\Carbon::parse($user->pending_role_start_date);
|
||||
$daysUntilActivation = $pendingStartDate->diffInDays(now());
|
||||
@endphp
|
||||
|
||||
<div class="bg-white dark:bg-gray-700 rounded-md p-3 mb-3 border border-blue-200 dark:border-blue-700">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Will change to:</span>
|
||||
<span class="text-sm font-bold text-blue-700 dark:text-blue-300">{{ $pendingRole->name ?? 'Unknown' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-xs font-medium text-gray-600 dark:text-gray-300">Activation date:</span>
|
||||
<span class="text-sm font-semibold text-gray-900 dark:text-gray-100">{{ $pendingStartDate->format('M j, Y g:i A') }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">Time until activation:</span>
|
||||
<span class="text-sm font-semibold text-blue-600 dark:text-blue-400">{{ $pendingStartDate->diffForHumans(['parts' => 2]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<p class="text-xs text-blue-700 dark:text-blue-300">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
This role will automatically activate when the current role expires
|
||||
</p>
|
||||
<label class="inline-flex items-center px-3 py-2 rounded-md bg-red-50 dark:bg-red-900/30 border border-red-200 dark:border-red-700 hover:bg-red-100 dark:hover:bg-red-900/50 cursor-pointer transition-all">
|
||||
<input type="checkbox" name="cancel_pending_role" value="1"
|
||||
class="rounded border-red-300 dark:border-red-600 bg-white dark:bg-gray-700 text-red-600 dark:text-red-500 shadow-sm focus:ring-2 focus:ring-red-500 dark:focus:ring-red-400 focus:border-red-500 dark:focus:border-red-400 cursor-pointer">
|
||||
<span class="ml-2 text-xs text-red-700 dark:text-red-300 font-semibold">Cancel pending role</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
@@ -0,0 +1,211 @@
|
||||
<!-- Role Expiry Date -->
|
||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4 bg-linear-to-br from-gray-50 to-white dark:from-gray-900 dark:to-gray-800">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label for="rolechangedate" class="text-sm font-medium text-gray-700 dark:text-gray-300 flex items-center">
|
||||
<i class="fas fa-calendar-alt mr-2 text-blue-600 dark:text-blue-400"></i>
|
||||
Role Expiry Date
|
||||
</label>
|
||||
@if(!empty($user->rolechangedate ?? ''))
|
||||
@php
|
||||
$expiryDate = \Carbon\Carbon::parse($user->rolechangedate);
|
||||
$isExpired = $expiryDate->isPast();
|
||||
$daysUntilExpiry = abs($expiryDate->diffInDays(now()));
|
||||
$totalHours = abs($expiryDate->diffInHours(now()));
|
||||
$hoursUntilExpiry = abs($totalHours % 24);
|
||||
@endphp
|
||||
@if($isExpired)
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200 animate-pulse">
|
||||
<i class="fas fa-exclamation-triangle mr-1"></i> Expired
|
||||
</span>
|
||||
@elseif($daysUntilExpiry <= 7)
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200">
|
||||
<i class="fas fa-exclamation-circle mr-1"></i> Expiring Soon
|
||||
</span>
|
||||
@elseif($daysUntilExpiry <= 30)
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200">
|
||||
<i class="fas fa-check-circle mr-1"></i> Active
|
||||
</span>
|
||||
@else
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200">
|
||||
<i class="fas fa-check-circle mr-1"></i> Active
|
||||
</span>
|
||||
@endif
|
||||
@else
|
||||
<span class="px-3 py-1 inline-flex items-center text-xs leading-5 font-semibold rounded-full bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-300">
|
||||
<i class="fas fa-infinity mr-1"></i> No Expiry
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Hidden input for form submission -->
|
||||
<input type="hidden" id="rolechangedate" name="rolechangedate" value="{{ is_array($user) ? ($user['rolechangedate'] ?? '') : (isset($user->rolechangedate) ? \Carbon\Carbon::parse($user->rolechangedate)->format('Y-m-d\TH:i:s') : '') }}">
|
||||
|
||||
<!-- Hidden field to store the ORIGINAL user expiry date for proper stacking calculations -->
|
||||
<input type="hidden" id="original_user_expiry" value="{{ is_array($user) ? ($user['rolechangedate'] ?? '') : (isset($user->rolechangedate) ? \Carbon\Carbon::parse($user->rolechangedate)->format('Y-m-d\TH:i:s') : '') }}">
|
||||
|
||||
<!-- Custom DateTime Picker -->
|
||||
<div class="grid grid-cols-5 gap-3">
|
||||
<!-- Year Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-calendar-alt mr-1"></i>Year
|
||||
</label>
|
||||
<select id="expiry_year"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
@for($y = date('Y'); $y <= date('Y') + 50; $y++)
|
||||
<option value="{{ $y }}">{{ $y }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Month Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-calendar mr-1"></i>Month
|
||||
</label>
|
||||
<select id="expiry_month"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
<option value="01">Jan</option>
|
||||
<option value="02">Feb</option>
|
||||
<option value="03">Mar</option>
|
||||
<option value="04">Apr</option>
|
||||
<option value="05">May</option>
|
||||
<option value="06">Jun</option>
|
||||
<option value="07">Jul</option>
|
||||
<option value="08">Aug</option>
|
||||
<option value="09">Sep</option>
|
||||
<option value="10">Oct</option>
|
||||
<option value="11">Nov</option>
|
||||
<option value="12">Dec</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Day Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-calendar-day mr-1"></i>Day
|
||||
</label>
|
||||
<select id="expiry_day"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
@for($d = 1; $d <= 31; $d++)
|
||||
<option value="{{ sprintf('%02d', $d) }}">{{ $d }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Hour Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-clock mr-1"></i>Hour
|
||||
</label>
|
||||
<select id="expiry_hour"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
@for($h = 0; $h <= 23; $h++)
|
||||
<option value="{{ sprintf('%02d', $h) }}">{{ sprintf('%02d', $h) }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Minute Selector -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1.5">
|
||||
<i class="fas fa-hourglass-half mr-1"></i>Min
|
||||
</label>
|
||||
<select id="expiry_minute"
|
||||
class="w-full px-2 py-3 text-lg font-semibold bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 border-2 border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400 transition-all shadow-sm hover:shadow-md hover:border-blue-400 dark:hover:border-blue-500">
|
||||
<option value="">--</option>
|
||||
@for($m = 0; $m <= 59; $m++)
|
||||
<option value="{{ sprintf('%02d', $m) }}">{{ sprintf('%02d', $m) }}</option>
|
||||
@endfor
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Selection Display -->
|
||||
<div id="datetime_preview" class="mt-3 p-3 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg hidden">
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm text-gray-600 dark:text-gray-400">
|
||||
<i class="fas fa-info-circle mr-2"></i>Selected:
|
||||
</span>
|
||||
<span id="datetime_display" class="text-base font-bold text-blue-600 dark:text-blue-400"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Action Buttons -->
|
||||
<div class="mt-3 space-y-2">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button type="button" data-expiry-action="set" data-days="1" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-clock mr-1"></i> +1 Day
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="7" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-calendar-week mr-1"></i> +1 Week
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="30" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-calendar-alt mr-1"></i> +1 Month
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="90" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-900/30 border border-blue-200 dark:border-blue-800 rounded-md hover:bg-blue-100 dark:hover:bg-blue-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-calendar mr-1"></i> +3 Months
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="365" data-hours="0" class="px-3 py-1.5 text-xs font-medium text-purple-700 dark:text-purple-300 bg-purple-50 dark:bg-purple-900/30 border border-purple-200 dark:border-purple-800 rounded-md hover:bg-purple-100 dark:hover:bg-purple-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-calendar-check mr-1"></i> +1 Year
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button type="button" data-expiry-action="set" data-days="0" data-hours="1" class="px-3 py-1.5 text-xs font-medium text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-md hover:bg-green-100 dark:hover:bg-green-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-hourglass-start mr-1"></i> +1 Hour
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="0" data-hours="6" class="px-3 py-1.5 text-xs font-medium text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-md hover:bg-green-100 dark:hover:bg-green-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-hourglass-half mr-1"></i> +6 Hours
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="0" data-hours="12" class="px-3 py-1.5 text-xs font-medium text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-md hover:bg-green-100 dark:hover:bg-green-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-hourglass-end mr-1"></i> +12 Hours
|
||||
</button>
|
||||
<button type="button" data-expiry-action="set" data-days="0" data-hours="24" class="px-3 py-1.5 text-xs font-medium text-green-700 dark:text-green-300 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-800 rounded-md hover:bg-green-100 dark:hover:bg-green-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-clock mr-1"></i> +24 Hours
|
||||
</button>
|
||||
<button type="button" data-expiry-action="end-of-day" class="px-3 py-1.5 text-xs font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/30 border border-indigo-200 dark:border-indigo-800 rounded-md hover:bg-indigo-100 dark:hover:bg-indigo-900/50 transition-all hover:scale-105">
|
||||
<i class="fas fa-moon mr-1"></i> End of Today
|
||||
</button>
|
||||
<button type="button" data-expiry-action="clear" class="px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded-md hover:bg-gray-100 dark:hover:bg-gray-600 transition-all hover:scale-105">
|
||||
<i class="fas fa-times-circle mr-1"></i> Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status Information -->
|
||||
@if(!empty($user->rolechangedate ?? ''))
|
||||
<div class="mt-3 p-3 rounded-lg {{ $isExpired ? 'bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800' : ($daysUntilExpiry <= 7 ? 'bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800' : 'bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800') }}">
|
||||
<div class="flex items-start">
|
||||
<i class="fa {{ $isExpired ? 'fa-exclamation-triangle text-red-600 dark:text-red-400' : ($daysUntilExpiry <= 7 ? 'fa-clock text-yellow-600 dark:text-yellow-400' : 'fa-info-circle text-blue-600 dark:text-blue-400') }} mt-0.5 mr-2"></i>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm font-medium {{ $isExpired ? 'text-red-800 dark:text-red-200' : ($daysUntilExpiry <= 7 ? 'text-yellow-800 dark:text-yellow-200' : 'text-blue-800 dark:text-blue-200') }}">
|
||||
@if($isExpired)
|
||||
Role expired {{ $expiryDate->diffForHumans() }}
|
||||
@else
|
||||
Role expires {{ $expiryDate->diffForHumans() }}
|
||||
@endif
|
||||
</p>
|
||||
<p class="text-xs {{ $isExpired ? 'text-red-700 dark:text-red-300' : ($daysUntilExpiry <= 7 ? 'text-yellow-700 dark:text-yellow-300' : 'text-blue-700 dark:text-blue-300') }} mt-1">
|
||||
<i class="fas fa-calendar-alt mr-1"></i>{{ $expiryDate->format('F j, Y') }}
|
||||
<span class="mx-2">•</span>
|
||||
<i class="fas fa-clock mr-1"></i>{{ $expiryDate->format('g:i A') }}
|
||||
</p>
|
||||
@if($daysUntilExpiry <= 7 && !$isExpired)
|
||||
<p class="text-xs text-yellow-700 dark:text-yellow-300 mt-1">
|
||||
<i class="fas fa-hourglass-half mr-1"></i>{{ $daysUntilExpiry }} day{{ $daysUntilExpiry != 1 ? 's' : '' }} and {{ $hoursUntilExpiry }} hour{{ $hoursUntilExpiry != 1 ? 's' : '' }} remaining
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<p class="mt-3 text-xs text-gray-600 dark:text-gray-400 flex items-center">
|
||||
<i class="fas fa-lightbulb mr-1.5 text-yellow-500"></i>
|
||||
<span>Leave empty for permanent role assignment, or use quick actions above to set an expiry date and time.</span>
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,154 @@
|
||||
@if(!empty($user['id']))
|
||||
<!-- View Preferences -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Category Preferences
|
||||
</label>
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="movieview"
|
||||
name="movieview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['movieview'] ?? 0) : ($user->movieview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="movieview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Movies</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="musicview"
|
||||
name="musicview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['musicview'] ?? 0) : ($user->musicview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="musicview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Music</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="gameview"
|
||||
name="gameview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['gameview'] ?? 0) : ($user->gameview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="gameview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Games</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="consoleview"
|
||||
name="consoleview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['consoleview'] ?? 0) : ($user->consoleview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="consoleview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Console</label>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<input type="checkbox"
|
||||
id="bookview"
|
||||
name="bookview"
|
||||
value="1"
|
||||
{{ (is_array($user) ? ($user['bookview'] ?? 0) : ($user->bookview ?? 0)) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
|
||||
<label for="bookview" class="ml-2 text-sm text-gray-700 dark:text-gray-300">Books</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if(!is_array($user))
|
||||
<div class="mt-6 rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-900" @if($user->passkeys->isNotEmpty()) x-data="adminUserPasskeys" @endif>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3 class="text-sm font-semibold text-gray-800 dark:text-gray-200">
|
||||
<i class="fas fa-fingerprint mr-2 text-blue-600 dark:text-blue-400"></i>Passkeys (WebAuthn)
|
||||
</h3>
|
||||
<span class="rounded-full bg-blue-100 px-2.5 py-1 text-xs font-medium text-blue-800 dark:bg-blue-900/30 dark:text-blue-200">
|
||||
{{ $user->passkeys->count() }} registered
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if($user->passkeys->isEmpty())
|
||||
<div class="mt-4 rounded-lg border border-dashed border-gray-300 p-3 text-sm text-gray-600 dark:border-gray-600 dark:text-gray-300">
|
||||
This user has no registered passkeys. They can create one from their profile security settings.
|
||||
</div>
|
||||
@else
|
||||
<div class="mt-4 overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700">
|
||||
<table class="min-w-full divide-y divide-gray-200 text-sm dark:divide-gray-700">
|
||||
<thead class="bg-gray-50 dark:bg-gray-800">
|
||||
<tr>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-700 dark:text-gray-300">Name</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-700 dark:text-gray-300">Created</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-700 dark:text-gray-300">Last used</th>
|
||||
<th class="px-4 py-3 text-right font-semibold text-gray-700 dark:text-gray-300">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 bg-white dark:divide-gray-700 dark:bg-gray-900">
|
||||
@foreach($user->passkeys as $passkey)
|
||||
<tr>
|
||||
<td class="px-4 py-3 text-gray-900 dark:text-gray-100">{{ $passkey->name }}</td>
|
||||
<td class="px-4 py-3 text-gray-600 dark:text-gray-300">{{ optional($passkey->created_at)->diffForHumans() ?? 'Unknown' }}</td>
|
||||
<td class="px-4 py-3 text-gray-600 dark:text-gray-300">{{ optional($passkey->last_used_at)->diffForHumans() ?? 'Not used yet' }}</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<form method="POST" action="{{ route('admin.user-passkey.destroy', $passkey) }}" class="inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button
|
||||
type="submit"
|
||||
data-confirm="Delete this passkey? The user will lose this device as a login method."
|
||||
class="confirm-link rounded-lg border border-red-300 px-3 py-1.5 text-xs font-medium text-red-700 hover:bg-red-50 dark:border-red-700 dark:text-red-300 dark:hover:bg-red-900/30"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($user->passkeys->isNotEmpty())
|
||||
<div class="mt-4 rounded-lg border border-red-200 bg-red-50 p-4 dark:border-red-800 dark:bg-red-900/20">
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleDangerZone()"
|
||||
class="text-sm font-semibold text-red-700 hover:text-red-800 dark:text-red-300 dark:hover:text-red-200"
|
||||
>
|
||||
<i class="fas fa-exclamation-triangle mr-2"></i>Emergency actions
|
||||
</button>
|
||||
|
||||
<div x-show="showDangerZone" x-cloak class="mt-3 space-y-3">
|
||||
<p class="text-xs text-red-700 dark:text-red-300">
|
||||
Remove all passkeys if this user lost or replaced their passkey device.
|
||||
</p>
|
||||
|
||||
<form method="POST" action="{{ route('admin.user-passkeys.wipe') }}" class="space-y-3">
|
||||
@csrf
|
||||
<input type="hidden" name="user_id" value="{{ $user->id }}">
|
||||
|
||||
<div>
|
||||
<label for="passkey_wipe_confirmation" class="block text-xs font-medium text-red-700 dark:text-red-300">Type WIPE to confirm</label>
|
||||
<input
|
||||
id="passkey_wipe_confirmation"
|
||||
name="confirmation"
|
||||
x-model="wipeConfirm"
|
||||
type="text"
|
||||
required
|
||||
class="mt-1 w-full rounded-md border border-red-300 px-3 py-2 text-sm text-gray-900 focus:border-red-500 focus:ring-2 focus:ring-red-500 dark:border-red-700 dark:bg-gray-800 dark:text-gray-100"
|
||||
>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="!canWipe()"
|
||||
class="rounded-lg bg-red-600 px-4 py-2 text-xs font-semibold text-white hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-red-700 dark:hover:bg-red-800"
|
||||
>
|
||||
Remove ALL passkeys
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@@ -99,6 +99,7 @@
|
||||
<img src="{{ getImageAssetUrl('book', (string) $result->id, asset('assets/images/no-cover.png')) }}"
|
||||
alt="{{ $result->title }}"
|
||||
class="w-full h-64 object-cover"
|
||||
loading="lazy"
|
||||
data-fallback-src="{{ url('/images/no-cover.png') }}">
|
||||
@else
|
||||
<div class="w-full h-64 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
|
||||
@@ -149,15 +150,14 @@
|
||||
{{ $results->links() }}
|
||||
</div>
|
||||
@else
|
||||
<!-- No Results -->
|
||||
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-8 text-center">
|
||||
<i class="fa fa-book text-yellow-600 text-5xl mb-4"></i>
|
||||
<h3 class="text-xl font-semibold text-gray-800 dark:text-gray-200 mb-2">No books found</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-4">Try adjusting your search filters or browse all books.</p>
|
||||
<a href="{{ url('/Books/All') }}" class="inline-flex items-center px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
|
||||
<i class="fa fa-book mr-2"></i> Browse All Books
|
||||
</a>
|
||||
</div>
|
||||
<x-empty-state
|
||||
icon="fa fa-book"
|
||||
title="No books found"
|
||||
message="Try adjusting your search filters or browse all books."
|
||||
:action-url="url('/Books/All')"
|
||||
action-label="Browse All Books"
|
||||
action-icon="fa fa-book"
|
||||
/>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
$hasValidCover = $coverUrl && !str_contains($coverUrl, 'no-cover.png');
|
||||
@endphp
|
||||
@if($hasValidCover)
|
||||
<a href="{{ url('/details/' . $result->guid) }}" class="shrink-0 bg-gray-100 dark:bg-gray-700 rounded mr-3" x-show="showThumbs" @unless(request()->query('thumbs') === '1') style="display:none" @endunless>
|
||||
<a href="{{ url('/details/' . $result->guid) }}" class="shrink-0 bg-gray-100 dark:bg-gray-700 rounded mr-3" x-show="showThumbs" @unless(request()->query('thumbs') === '1') x-cloak @endunless>
|
||||
<img src="{{ request()->query('thumbs') === '1' ? $coverUrl : '' }}" x-bind:src="showThumbs ? '{{ $coverUrl }}' : ''" class="w-12 h-16 object-cover rounded shadow-sm hover:shadow-md transition" alt="Cover" loading="lazy">
|
||||
</a>
|
||||
@endif
|
||||
@@ -209,7 +209,7 @@
|
||||
$mHasCover = $mCoverUrl && !str_contains($mCoverUrl, 'no-cover.png');
|
||||
@endphp
|
||||
@if($mHasCover)
|
||||
<a href="{{ url('/details/' . $result->guid) }}" class="block mb-2 bg-gray-100 dark:bg-gray-700 rounded-lg" x-show="showThumbs" @unless(request()->query('thumbs') === '1') style="display:none" @endunless>
|
||||
<a href="{{ url('/details/' . $result->guid) }}" class="block mb-2 bg-gray-100 dark:bg-gray-700 rounded-lg" x-show="showThumbs" @unless(request()->query('thumbs') === '1') x-cloak @endunless>
|
||||
<img src="{{ request()->query('thumbs') === '1' ? $mCoverUrl : '' }}" x-bind:src="showThumbs ? '{{ $mCoverUrl }}' : ''" class="w-16 h-20 object-cover rounded-lg shadow-sm" alt="Cover" loading="lazy">
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
<img class="w-32 h-48 object-cover"
|
||||
src="{{ getReleaseCover($result) }}"
|
||||
alt="{{ $result->title }}"
|
||||
loading="lazy"
|
||||
data-fallback-src="{{ url('/images/no-cover.png') }}">
|
||||
@if($totalFailed > 0)
|
||||
<div class="absolute top-2 right-2">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,245 @@
|
||||
<!-- Anime Information -->
|
||||
@if(!empty($anidb))
|
||||
@php
|
||||
$anidbData = is_object($anidb) ? get_object_vars($anidb) : $anidb;
|
||||
$anidbTitle = $anidbData['title'] ?? ($anidb->title ?? null);
|
||||
$anidbEnglishTitle = $anidbData['english_title'] ?? ($anidb->english_title ?? null);
|
||||
$anidbOriginalTitle = $anidbData['original_title'] ?? ($anidb->original_title ?? null);
|
||||
$anidbOriginalLang = $anidbData['original_lang'] ?? ($anidb->original_lang ?? null);
|
||||
$anidbRomajiTitle = $anidbData['romaji_title'] ?? ($anidb->romaji_title ?? null);
|
||||
$anidbHashtag = $anidbData['hashtag'] ?? ($anidb->hashtag ?? null);
|
||||
$anidbType = $anidbData['type'] ?? ($anidb->type ?? null);
|
||||
$anidbMediaType = $anidbData['media_type'] ?? ($anidb->media_type ?? null);
|
||||
$anidbCountry = $anidbData['country'] ?? ($anidb->country ?? null);
|
||||
$anidbEpisodes = $anidbData['episodes'] ?? ($anidb->episodes ?? null);
|
||||
$anidbDuration = $anidbData['duration'] ?? ($anidb->duration ?? null);
|
||||
$anidbStatus = $anidbData['status'] ?? ($anidb->status ?? null);
|
||||
$anidbSource = $anidbData['source'] ?? ($anidb->source ?? null);
|
||||
$anidbStartDate = $anidbData['startdate'] ?? ($anidb->startdate ?? null);
|
||||
$anidbEndDate = $anidbData['enddate'] ?? ($anidb->enddate ?? null);
|
||||
$anidbRating = $anidbData['rating'] ?? ($anidb->rating ?? null);
|
||||
$anidbDescription = $anidbData['description'] ?? ($anidb->description ?? null);
|
||||
$anidbCategories = $anidbData['categories'] ?? ($anidb->categories ?? null);
|
||||
$anidbCreators = $anidbData['creators'] ?? ($anidb->creators ?? null);
|
||||
|
||||
// Country model/name are resolved in DetailsController
|
||||
$country = $anidbCountryModel ?? null;
|
||||
$countryName = $anidbCountryName ?? null;
|
||||
|
||||
// Format status
|
||||
$statusLabels = [
|
||||
'FINISHED' => 'Finished',
|
||||
'RELEASING' => 'Releasing',
|
||||
'NOT_YET_RELEASED' => 'Not Yet Released',
|
||||
'CANCELLED' => 'Cancelled',
|
||||
'HIATUS' => 'Hiatus',
|
||||
];
|
||||
$anidbStatusLabel = $statusLabels[$anidbStatus] ?? $anidbStatus;
|
||||
|
||||
// Format source
|
||||
$sourceLabels = [
|
||||
'ORIGINAL' => 'Original',
|
||||
'MANGA' => 'Manga',
|
||||
'LIGHT_NOVEL' => 'Light Novel',
|
||||
'VISUAL_NOVEL' => 'Visual Novel',
|
||||
'VIDEO_GAME' => 'Video Game',
|
||||
'OTHER' => 'Other',
|
||||
'NOVEL' => 'Novel',
|
||||
'DOUJINSHI' => 'Doujinshi',
|
||||
'ANIME' => 'Anime',
|
||||
'WEB_MANGA' => 'Web Manga',
|
||||
'MUSIC' => 'Music',
|
||||
'4_KOMA_MANGA' => '4-Koma Manga',
|
||||
];
|
||||
$anidbSourceLabel = $sourceLabels[$anidbSource] ?? $anidbSource;
|
||||
@endphp
|
||||
<div class="surface-panel-alt rounded-lg p-6 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-dragon mr-2 text-primary-600 dark:text-primary-400"></i>
|
||||
@if($anidbMediaType === 'MANGA')
|
||||
Manga Information
|
||||
@else
|
||||
Anime Information
|
||||
@endif
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@if(!empty($anidbEnglishTitle) || !empty($anidbOriginalTitle) || !empty($anidbRomajiTitle) || !empty($anidbHashtag))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-2">Titles</dt>
|
||||
<dd class="mt-1 space-y-2">
|
||||
@if(!empty($anidbEnglishTitle))
|
||||
<div class="text-sm text-gray-900 dark:text-gray-100 font-semibold">
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400 font-normal mr-2">English:</span>
|
||||
{{ $anidbEnglishTitle }}
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbOriginalTitle) && $anidbOriginalLang === 'ja')
|
||||
<div class="text-sm text-gray-900 dark:text-gray-100 font-semibold">
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400 font-normal mr-2">Native:</span>
|
||||
{{ $anidbOriginalTitle }}
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbRomajiTitle))
|
||||
<div class="text-sm text-gray-900 dark:text-gray-100 font-semibold">
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400 font-normal mr-2">Romaji:</span>
|
||||
{{ $anidbRomajiTitle }}
|
||||
</div>
|
||||
@elseif(!empty($anidbOriginalTitle) && $anidbOriginalLang === 'x-jat')
|
||||
<div class="text-sm text-gray-900 dark:text-gray-100 font-semibold">
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400 font-normal mr-2">Romaji:</span>
|
||||
{{ $anidbOriginalTitle }}
|
||||
</div>
|
||||
@elseif(!empty($anidbOriginalTitle) && $anidbOriginalLang !== 'ja')
|
||||
<div class="text-sm text-gray-900 dark:text-gray-100 font-semibold">
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400 font-normal mr-2">Original:</span>
|
||||
{{ $anidbOriginalTitle }}
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbHashtag))
|
||||
<div class="text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400 font-normal mr-2">Hashtag:</span>
|
||||
<span class="font-mono text-primary-600 dark:text-primary-400">{{ $anidbHashtag }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if(empty($anidbEnglishTitle) && empty($anidbOriginalTitle) && empty($anidbRomajiTitle) && !empty($anidbTitle))
|
||||
<div class="text-sm text-gray-900 dark:text-gray-100 font-semibold">
|
||||
{{ $anidbTitle }}
|
||||
</div>
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
@elseif(!empty($anidbTitle))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Title</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $anidbTitle }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbMediaType))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Media Type</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium
|
||||
{{ $anidbMediaType === 'ANIME' ? 'bg-primary-100 text-primary-800 dark:bg-primary-900/50 dark:text-primary-200' : 'bg-primary-100 text-primary-800 dark:bg-primary-900/50 dark:text-primary-200' }}">
|
||||
{{ $anidbMediaType === 'ANIME' ? 'Anime' : 'Manga' }}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbType))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Format</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ ucfirst(str_replace('_', ' ', strtolower($anidbType))) }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($countryName))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Country</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center">
|
||||
@if(!empty($country))
|
||||
<img src="{{ asset('assets/images/flags/' . strtolower($anidbCountry) . '.png') }}"
|
||||
alt="{{ $countryName }}"
|
||||
class="w-4 h-3 mr-1"
|
||||
data-hide-on-error="true">
|
||||
@endif
|
||||
{{ $countryName }}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbStatus))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Status</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $anidbStatusLabel }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbSource))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Source</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $anidbSourceLabel }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbEpisodes))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Episodes</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ number_format($anidbEpisodes) }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbDuration))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Duration</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $anidbDuration }} minutes</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbStartDate))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Start Date</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $anidbStartDate }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbEndDate))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">End Date</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $anidbEndDate }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbRating))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Rating</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center">
|
||||
<i class="fas fa-star text-yellow-500 dark:text-yellow-400 mr-1"></i>
|
||||
@php
|
||||
$ratingValue = is_numeric($anidbRating) ? (float)$anidbRating : 0;
|
||||
// AniList rating is out of 100, convert to /10
|
||||
$displayRating = $ratingValue > 10 ? number_format($ratingValue / 10, 1) : number_format($ratingValue, 1);
|
||||
@endphp
|
||||
{{ $displayRating }} / 10
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbCategories))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Genres</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $anidbCategories }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbCreators))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Studios</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $anidbCreators }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($anidbDescription))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Description</dt>
|
||||
<dd class="mt-1 text-sm text-gray-700 dark:text-gray-300">{{ $anidbDescription }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- External Links -->
|
||||
@php
|
||||
$anilistId = $anidbData['anilist_id'] ?? ($anidb->anilist_id ?? null);
|
||||
$malId = $anidbData['mal_id'] ?? ($anidb->mal_id ?? null);
|
||||
@endphp
|
||||
@if(!empty($anilistId) || !empty($malId))
|
||||
<div class="mt-4 pt-4 border-t border-pink-200 dark:border-pink-700">
|
||||
<h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3">External Links</h4>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
@if(!empty($anilistId))
|
||||
<a href="{{ $site['dereferrer_link'] ?? '' }}https://anilist.co/anime/{{ $anilistId }}" target="_blank" class="inline-flex items-center px-4 py-2 bg-primary-100 text-primary-800 rounded-lg hover:bg-primary-200 transition dark:bg-primary-900/30 dark:text-primary-200 dark:hover:bg-primary-800/30">
|
||||
<i class="fas fa-external-link-alt mr-2"></i> View on AniList
|
||||
</a>
|
||||
@endif
|
||||
@if(!empty($malId))
|
||||
<a href="{{ $site['dereferrer_link'] ?? '' }}https://myanimelist.net/anime/{{ $malId }}" target="_blank" class="inline-flex items-center px-4 py-2 bg-yellow-100 text-yellow-800 rounded-lg hover:bg-yellow-200 transition">
|
||||
<i class="fas fa-external-link-alt mr-2"></i> View on MyAnimeList
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,48 @@
|
||||
<!-- Book Information -->
|
||||
@if(!empty($book))
|
||||
@php
|
||||
$bookData = is_object($book) ? get_object_vars($book) : $book;
|
||||
$bookTitle = $bookData['title'] ?? ($book->title ?? null);
|
||||
$bookAuthor = $bookData['author'] ?? ($book->author ?? null);
|
||||
$bookPublisher = $bookData['publisher'] ?? ($book->publisher ?? null);
|
||||
$bookPublishDate = $bookData['publishdate'] ?? ($book->publishdate ?? null);
|
||||
$bookOverview = $bookData['overview'] ?? ($book->overview ?? null);
|
||||
@endphp
|
||||
<div class="surface-panel-alt rounded-lg p-6 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-book mr-2 text-primary-600 dark:text-primary-400"></i> Book Information
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@if(!empty($bookTitle))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Title</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $bookTitle }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($bookAuthor))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Author</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $bookAuthor }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($bookPublisher))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Publisher</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $bookPublisher }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($bookPublishDate))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Published</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $bookPublishDate }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($bookOverview))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Overview</dt>
|
||||
<dd class="mt-1 text-sm text-gray-700 dark:text-gray-300">{{ $bookOverview }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,94 @@
|
||||
<!-- Comments Section -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-comments mr-2 text-primary-600 dark:text-primary-400"></i>
|
||||
Comments ({{ isset($comments) ? count($comments) : 0 }})
|
||||
</h3>
|
||||
|
||||
<!-- Flash Messages -->
|
||||
@if(session('success'))
|
||||
<div class="bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg p-4 mb-4">
|
||||
<p class="text-sm text-green-800 dark:text-green-200 flex items-center">
|
||||
<i class="fas fa-check-circle mr-2"></i>
|
||||
{{ session('success') }}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-4 mb-4">
|
||||
<p class="text-sm text-red-800 dark:text-red-200 flex items-center">
|
||||
<i class="fas fa-exclamation-circle mr-2"></i>
|
||||
{{ session('error') }}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Add Comment Form -->
|
||||
@auth
|
||||
<div class="surface-panel rounded-lg p-4 mb-6 border shadow-sm">
|
||||
<form method="POST" action="{{ url('/details/' . $release->guid) }}" id="commentForm">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label for="txtAddComment" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Add a Comment
|
||||
</label>
|
||||
<textarea
|
||||
name="txtAddComment"
|
||||
id="txtAddComment"
|
||||
rows="4"
|
||||
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500"
|
||||
placeholder="Share your thoughts about this release..."
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
class="px-6 py-2 bg-primary-600 dark:bg-primary-700 text-white rounded-lg hover:bg-primary-700 dark:hover:bg-primary-800 transition inline-flex items-center font-medium shadow-sm"
|
||||
>
|
||||
<i class="fas fa-paper-plane mr-2"></i>
|
||||
Post Comment
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4 mb-6">
|
||||
<p class="text-sm text-yellow-800 dark:text-yellow-200">
|
||||
<i class="fas fa-info-circle mr-2"></i>
|
||||
Please <a href="{{ route('login') }}" class="font-semibold underline hover:text-yellow-900 dark:hover:text-yellow-100">log in</a> to add a comment.
|
||||
</p>
|
||||
</div>
|
||||
@endauth
|
||||
|
||||
<!-- Comments List -->
|
||||
@if(isset($comments) && count($comments) > 0)
|
||||
<div class="space-y-4">
|
||||
@foreach($comments as $comment)
|
||||
<div class="detail-comment-card surface-panel-alt rounded-lg p-4 border transition hover:border-gray-300 dark:hover:border-gray-600">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="flex items-center">
|
||||
<div class="w-10 h-10 bg-primary-600 dark:bg-primary-700 rounded-full flex items-center justify-center text-white font-bold mr-3 shadow-sm">
|
||||
{{ strtoupper(substr($comment['username'] ?? 'U', 0, 1)) }}
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-semibold text-gray-800 dark:text-gray-200">{{ $comment['username'] ?? 'Anonymous' }}</p>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
<i class="far fa-clock mr-1"></i>
|
||||
{{ \Carbon\Carbon::parse($comment['created_at'])->diffForHumans() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-gray-700 dark:text-gray-300 leading-relaxed">{{ $comment['text'] ?? '' }}</p>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="detail-empty-comments surface-panel-alt rounded-lg p-8 border text-center">
|
||||
<i class="fas fa-comments text-4xl text-gray-400 dark:text-gray-600 mb-3"></i>
|
||||
<p class="text-gray-500 dark:text-gray-400">No comments yet. Be the first to comment!</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
<!-- Console Information -->
|
||||
@if(!empty($con))
|
||||
@php
|
||||
$conData = is_object($con) ? get_object_vars($con) : $con;
|
||||
$conTitle = $conData['title'] ?? ($con->title ?? null);
|
||||
$conPublisher = $conData['publisher'] ?? ($con->publisher ?? null);
|
||||
$conReleaseDate = $conData['releasedate'] ?? ($con->releasedate ?? null);
|
||||
@endphp
|
||||
<div class="surface-panel-alt rounded-lg p-6 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-gamepad mr-2 text-primary-600 dark:text-primary-400"></i> Console Game Information
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@if(!empty($conTitle))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Title</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $conTitle }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($conPublisher))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Publisher</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $conPublisher }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($conReleaseDate))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Release Date</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $conReleaseDate }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,104 @@
|
||||
<!-- Cover Image and Title -->
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-4">
|
||||
<div class="flex gap-4 mb-4">
|
||||
<!-- Cover Image -->
|
||||
<div class="shrink-0">
|
||||
<img src="{{ getReleaseCover($release) }}"
|
||||
alt="{{ $release->searchname }}"
|
||||
class="detail-cover-image w-48 h-72 object-cover max-w-[192px] max-h-[288px]"
|
||||
data-fallback-src="{{ asset('assets/images/no-cover.png') }}">
|
||||
</div>
|
||||
|
||||
<!-- Title and Actions -->
|
||||
<div class="flex-1">
|
||||
<div class="mb-3">
|
||||
<h2 class="text-xl font-bold text-gray-800 dark:text-gray-200 mb-2 wrap-break-word break-all">{{ $release->searchname }}</h2>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@if(!empty($totalReportCount) && $totalReportCount > 0)
|
||||
<div class="inline-flex items-center px-3 py-1.5 rounded-lg text-sm font-medium bg-orange-100 dark:bg-orange-900 text-orange-800 dark:text-orange-200 border border-orange-200 dark:border-orange-800"
|
||||
title="Reported: {{ $allReportReasons ?? 'Unknown' }}">
|
||||
<i class="fas fa-flag mr-2"></i>
|
||||
<span>Reported ({{ $totalReportCount }}): {{ $allReportReasons ?? 'Unknown' }}</span>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($failed) && $failed > 0)
|
||||
<div class="inline-flex items-center px-3 py-1.5 rounded-lg text-sm font-medium bg-red-100 text-red-800 border border-red-200">
|
||||
<i class="fas fa-exclamation-triangle mr-2"></i>
|
||||
<span>{{ $failed }} user{{ $failed > 1 ? 's' : '' }} reported download failure</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="{{ url('/getnzb/' . $release->guid) }}" class="download-nzb release-action release-action-download px-4 py-2">
|
||||
<i class="fas fa-download mr-2"></i> Download NZB
|
||||
</a>
|
||||
<a href="#" class="add-to-cart release-action release-action-primary px-4 py-2" data-guid="{{ $release->guid }}">
|
||||
<i class="icon_cart fas fa-shopping-basket mr-2"></i> Add to Cart
|
||||
</a>
|
||||
@if(isset($release->nfostatus) && $release->nfostatus == 1)
|
||||
<button type="button" class="nfo-badge release-action release-action-primary px-4 py-2" data-guid="{{ $release->guid }}" title="View NFO file">
|
||||
<i class="fas fa-file-alt mr-2"></i> View NFO
|
||||
</button>
|
||||
@endif
|
||||
@if(($release->totalpart ?? 0) > 0)
|
||||
<button type="button" class="filelist-badge release-action release-action-muted px-4 py-2" data-guid="{{ $release->guid }}" title="View file list">
|
||||
<i class="fas fa-list mr-2"></i> View Files
|
||||
</button>
|
||||
@endif
|
||||
@auth
|
||||
@if(auth()->user()->hasRole('Admin') || auth()->user()->hasRole('Moderator'))
|
||||
<a href="{{ route('admin.release-edit', ['id' => $release->guid]) }}" class="release-action release-action-primary px-4 py-2" title="Edit Release">
|
||||
<i class="fas fa-edit mr-2"></i> Edit Release
|
||||
</a>
|
||||
@endif
|
||||
@endauth
|
||||
<x-report-button :release-id="$release->id" :reported-count="$totalReportCount ?? 0" variant="button-lg" />
|
||||
</div>
|
||||
@if(!empty($originalReportData) && $originalReportData->count() > 0)
|
||||
<div class="mt-4 rounded-lg border border-orange-200 dark:border-orange-800 bg-orange-50 dark:bg-orange-900/20 p-4">
|
||||
<h3 class="text-sm font-semibold text-orange-800 dark:text-orange-200 mb-3 flex items-center">
|
||||
<i class="fas fa-flag mr-2"></i> Original report
|
||||
</h3>
|
||||
<div class="space-y-3">
|
||||
@foreach($originalReportData as $originalReport)
|
||||
<div class="text-sm text-orange-900 dark:text-orange-100">
|
||||
<div class="flex flex-wrap items-center gap-2 mb-1">
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-orange-100 dark:bg-orange-900 text-orange-800 dark:text-orange-200 border border-orange-200 dark:border-orange-800">
|
||||
{{ $originalReport->reason_label }}
|
||||
</span>
|
||||
<span class="text-xs text-orange-700 dark:text-orange-300">
|
||||
{{ ucfirst($originalReport->status) }} · Reported {{ $originalReport->created_at->format('M d, Y H:i') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="whitespace-pre-wrap break-words">
|
||||
{{ $originalReport->description ?: 'No additional report details were provided.' }}
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($publicReportResponses) && $publicReportResponses->count() > 0)
|
||||
<div class="mt-4 rounded-lg border border-blue-200 dark:border-blue-800 bg-blue-50 dark:bg-blue-900/20 p-4">
|
||||
<h3 class="text-sm font-semibold text-blue-800 dark:text-blue-200 mb-3 flex items-center">
|
||||
<i class="fas fa-reply mr-2"></i> Staff response
|
||||
</h3>
|
||||
<div class="space-y-3">
|
||||
@foreach($publicReportResponses as $responseReport)
|
||||
<div class="text-sm text-blue-900 dark:text-blue-100">
|
||||
<div class="whitespace-pre-wrap break-words">{{ $responseReport->response }}</div>
|
||||
<div class="mt-2 text-xs text-blue-700 dark:text-blue-300">
|
||||
{{ $responseReport->responded_at ? 'Responded ' . $responseReport->responded_at->format('M d, Y H:i') : 'Staff response' }}
|
||||
@if($responseReport->responder)
|
||||
by {{ $responseReport->responder->username }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!-- Game Information -->
|
||||
@if(!empty($game))
|
||||
@php
|
||||
$gameData = is_object($game) ? get_object_vars($game) : $game;
|
||||
$gameTitle = $gameData['title'] ?? ($game->title ?? null);
|
||||
$gamePublisher = $gameData['publisher'] ?? ($game->publisher ?? null);
|
||||
$gameReleaseDate = $gameData['releasedate'] ?? ($game->releasedate ?? null);
|
||||
$gameGenres = $gameData['genres'] ?? ($game->genres ?? null);
|
||||
@endphp
|
||||
<div class="surface-panel-alt rounded-lg p-6 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-gamepad mr-2 text-primary-600 dark:text-primary-400"></i> Game Information
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@if(!empty($gameTitle))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Title</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $gameTitle }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($gamePublisher))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Publisher</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $gamePublisher }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($gameReleaseDate))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Release Date</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $gameReleaseDate }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($gameGenres))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Genres</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $gameGenres }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,12 @@
|
||||
<!-- Image Modal -->
|
||||
<div id="imageModal" class="image-modal-backdrop hidden modal-hidden">
|
||||
<div class="image-modal-container">
|
||||
<button type="button" class="image-modal-close" data-close-image-modal>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<div class="text-center">
|
||||
<h3 id="imageModalTitle" class="text-white text-xl font-semibold mb-4 drop-shadow-lg"></h3>
|
||||
<img id="imageModalImage" src="" alt="Image" class="max-w-full max-h-[85vh] mx-auto rounded-2xl shadow-2xl">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,56 @@
|
||||
<!-- Sidebar -->
|
||||
<div class="lg:col-span-1">
|
||||
<div class="detail-info-sidebar surface-panel-alt rounded-lg p-4 sticky top-4 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Information</h3>
|
||||
<dl class="space-y-3">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Category</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $release->category_name ?? 'Other' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Size</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ number_format($release->size / 1073741824, 2) }} GB</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Files</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $release->totalpart ?? 0 }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Added</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ userDate($release->adddate, 'M d, Y H:i') }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Group</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $release->group_name ?? 'Unknown' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Posted</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ userDate($release->postdate, 'M d, Y H:i') }}</dd>
|
||||
</div>
|
||||
@if(!empty($release->fromname))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Posted By</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 break-all">
|
||||
<span class="inline-flex items-center px-2 py-1 rounded bg-primary-100 dark:bg-primary-900/50 text-primary-800 dark:text-primary-200 text-xs font-mono">
|
||||
<i class="fas fa-user mr-1"></i>{{ $release->fromname }}
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">Grabs</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $release->grabs ?? 0 }}</dd>
|
||||
</div>
|
||||
@if($release->imdbid ?? false)
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400">IMDB</dt>
|
||||
<dd class="mt-1">
|
||||
<a href="{{ $site['dereferrer_link'] }}https://www.imdb.com/title/tt{{ $release->imdbid }}" target="_blank" class="text-sm text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300">
|
||||
View on IMDB <i class="fas fa-external-link-alt text-xs"></i>
|
||||
</a>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,144 @@
|
||||
<!-- Video/Audio Metadata -->
|
||||
@if(!empty($reVideo) || !empty($reAudio) || !empty($reSubs))
|
||||
<div class="surface-panel-alt rounded-lg p-6 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-photo-video mr-2 text-primary-600 dark:text-primary-400"></i> Media Information
|
||||
</h3>
|
||||
|
||||
@if(!empty($reVideo))
|
||||
<div class="mb-6">
|
||||
<h4 class="text-md font-semibold text-gray-700 dark:text-gray-300 mb-3 flex items-center">
|
||||
<i class="fas fa-video mr-2 text-primary-500 dark:text-primary-400"></i> Video Details
|
||||
</h4>
|
||||
<div class="surface-panel rounded-lg p-4 shadow-sm border">
|
||||
<dl class="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
@if(!empty($reVideo['containerformat']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Container Format</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $reVideo['containerformat'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($reVideo['videocodec']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Video Codec</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $reVideo['videocodec'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($reVideo['videoformat']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Video Format</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $reVideo['videoformat'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($reVideo['videowidth']) && !empty($reVideo['videoheight']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Resolution</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $reVideo['videowidth'] }}x{{ $reVideo['videoheight'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($reVideo['videoaspect']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Aspect Ratio</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $reVideo['videoaspect'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($reVideo['videoframerate']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Frame Rate</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $reVideo['videoframerate'] }} fps</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($reVideo['videoduration']))
|
||||
@php
|
||||
$durationMs = intval($reVideo['videoduration']);
|
||||
$durationMinutes = $durationMs > 0 ? round($durationMs / 1000 / 60) : 0;
|
||||
@endphp
|
||||
@if($durationMinutes > 0)
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Duration</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $durationMinutes }} minutes</dd>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
@if(!empty($reVideo['overallbitrate']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Bit Rate</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $reVideo['overallbitrate'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($reVideo['videolibrary']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Encoder Library</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $reVideo['videolibrary'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(!empty($reAudio))
|
||||
<div class="mb-6">
|
||||
<h4 class="text-md font-semibold text-gray-700 dark:text-gray-300 mb-3 flex items-center">
|
||||
<i class="fas fa-volume-up mr-2 text-primary-500 dark:text-primary-400"></i> Audio Details
|
||||
</h4>
|
||||
@foreach($reAudio as $index => $audio)
|
||||
<div class="surface-panel rounded-lg p-4 shadow-sm border {{ $index > 0 ? 'mt-3' : '' }}">
|
||||
@if(count($reAudio) > 1)
|
||||
<p class="text-xs font-semibold text-gray-500 dark:text-gray-400 mb-2">Track {{ $index + 1 }}</p>
|
||||
@endif
|
||||
<dl class="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
@if(!empty($audio['audioformat']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Audio Format</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $audio['audioformat'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($audio['audiocodec']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Codec</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $audio['audiocodec'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($audio['audiochannels']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Channels</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $audio['audiochannels'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($audio['audiobitrate']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Bit Rate</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $audio['audiobitrate'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($audio['audiolanguage']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Language</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $audio['audiolanguage'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($audio['audiosamplerate']))
|
||||
<div>
|
||||
<dt class="text-xs font-medium text-gray-600 dark:text-gray-400 mb-1">Sample Rate</dt>
|
||||
<dd class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $audio['audiosamplerate'] }} Hz</dd>
|
||||
</div>
|
||||
@endif
|
||||
</dl>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(!empty($reSubs))
|
||||
<div>
|
||||
<h4 class="text-md font-semibold text-gray-700 dark:text-gray-300 mb-3 flex items-center">
|
||||
<i class="fas fa-closed-captioning mr-2 text-primary-500"></i> Subtitles
|
||||
</h4>
|
||||
<div class="surface-panel rounded-lg p-4 shadow-sm">
|
||||
<p class="text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $reSubs->subs }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,90 @@
|
||||
<!-- Movie Information -->
|
||||
@if(!empty($movie))
|
||||
@php
|
||||
$movieData = is_object($movie) ? get_object_vars($movie) : $movie;
|
||||
$movieTitle = $movieData['title'] ?? ($movie->title ?? null);
|
||||
$movieYear = $movieData['year'] ?? ($movie->year ?? null);
|
||||
$movieTagline = $movieData['tagline'] ?? ($movie->tagline ?? null);
|
||||
$movieRating = $movieData['rating'] ?? ($movie->rating ?? null);
|
||||
$moviePlot = $movieData['plot'] ?? ($movie->plot ?? null);
|
||||
$movieGenre = $movieData['genre'] ?? ($movie->genre ?? null);
|
||||
$movieDirector = $movieData['director'] ?? ($movie->director ?? null);
|
||||
$movieActors = $movieData['actors'] ?? ($movie->actors ?? null);
|
||||
$movieLanguage = $movieData['language'] ?? ($movie->language ?? null);
|
||||
$movieTrailer = $movieData['trailer'] ?? ($movie->trailer ?? null);
|
||||
@endphp
|
||||
<div class="surface-panel-alt rounded-lg p-6 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-film mr-2 text-primary-600 dark:text-primary-400"></i> Movie Information
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@if(!empty($movieTitle))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Title</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $movieTitle }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($movieYear))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Year</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $movieYear }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($movieTagline))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Tagline</dt>
|
||||
<dd class="mt-1 text-sm text-gray-700 dark:text-gray-300 italic">"{{ $movieTagline }}"</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($movieRating))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">IMDB Rating</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">
|
||||
<span class="inline-flex items-center">
|
||||
<i class="fas fa-star text-yellow-500 dark:text-yellow-400 mr-1"></i>
|
||||
{{ $movieRating }}/10
|
||||
</span>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($moviePlot))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Plot Synopsis</dt>
|
||||
<dd class="mt-1 text-sm text-gray-700 dark:text-gray-300 leading-relaxed">{{ $moviePlot }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($movieGenre))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Genre</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{!! $movieGenre !!}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($movieDirector))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Director</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{!! $movieDirector !!}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($movieActors))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Cast</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{!! $movieActors !!}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($movieLanguage))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Language</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $movieLanguage }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@if(!empty($movieTrailer))
|
||||
<div class="mt-4">
|
||||
<h4 class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-2">Trailer</h4>
|
||||
<div class="aspect-video">
|
||||
{!! $movieTrailer !!}
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,48 @@
|
||||
<!-- Music Information -->
|
||||
@if(!empty($music))
|
||||
@php
|
||||
$musicData = is_object($music) ? get_object_vars($music) : $music;
|
||||
$musicTitle = $musicData['title'] ?? ($music->title ?? null);
|
||||
$musicArtist = $musicData['artist'] ?? ($music->artist ?? null);
|
||||
$musicPublisher = $musicData['publisher'] ?? ($music->publisher ?? null);
|
||||
$musicReleaseDate = $musicData['releasedate'] ?? ($music->releasedate ?? null);
|
||||
$musicGenres = $musicData['genres'] ?? ($music->genres ?? null);
|
||||
@endphp
|
||||
<div class="surface-panel-alt rounded-lg p-6 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-music mr-2 text-primary-600 dark:text-primary-400"></i> Music Information
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@if(!empty($musicTitle))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Album</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $musicTitle }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($musicArtist))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Artist</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $musicArtist }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($musicPublisher))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Publisher</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $musicPublisher }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($musicReleaseDate))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Release Date</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $musicReleaseDate }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($musicGenres))
|
||||
<div class="md:col-span-2">
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Genres</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $musicGenres }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,46 @@
|
||||
<!-- Password Information -->
|
||||
@if(isset($release->passwordstatus) && $release->passwordstatus > 0)
|
||||
<div class="detail-password-card bg-linear-to-r from-red-50 to-orange-50 dark:from-red-900 dark:to-orange-900 rounded-lg p-6 border border-red-100 dark:border-red-800">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-lock mr-2 text-red-600 dark:text-red-400"></i> Password Protected Release
|
||||
</h3>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Password Status</dt>
|
||||
<dd class="mt-1">
|
||||
@if($release->passwordstatus == 1)
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200">
|
||||
<i class="fas fa-question-circle mr-1"></i> Password Unknown
|
||||
</span>
|
||||
@elseif($release->passwordstatus == 2)
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
|
||||
<i class="fas fa-check-circle mr-1"></i> Password Available
|
||||
</span>
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-2">Password</dt>
|
||||
<dd class="mt-1">
|
||||
@if(!empty($release->password))
|
||||
<div class="surface-panel rounded-lg p-3 border">
|
||||
<code class="text-sm text-gray-900 dark:text-gray-100 font-mono break-all">{{ $release->password }}</code>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-2">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
This password is embedded in the NZB file and will be automatically recognized by NZBGet or SABnzbd.
|
||||
</p>
|
||||
@else
|
||||
<div class="surface-panel rounded-lg p-3 border">
|
||||
<code class="text-sm text-gray-500 dark:text-gray-400 font-mono">None</code>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-2">
|
||||
<i class="fas fa-exclamation-triangle mr-1"></i>
|
||||
No password information available in the database.
|
||||
</p>
|
||||
@endif
|
||||
</dd>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,34 @@
|
||||
<!-- PreDB Information -->
|
||||
@if(!empty($predb) && is_array($predb))
|
||||
<div class="surface-panel-alt rounded-lg p-6 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-database mr-2 text-primary-600"></i> PreDB Information
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@if(!empty($predb['title']))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Title</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-mono">{{ $predb['title'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($predb['source']))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Source</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $predb['source'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($predb['predate']))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Pre Date</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $predb['predate'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($predb['category']))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Category</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $predb['category'] }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,53 @@
|
||||
<!-- Sample/Preview Images -->
|
||||
@php
|
||||
$hasPreviewImage = isset($release->haspreview) && $release->haspreview == 1;
|
||||
$hasSampleImage = isset($release->jpgstatus) && $release->jpgstatus == 1;
|
||||
$previewImageUrl = $hasPreviewImage
|
||||
? getImageAssetUrl('preview', $release->guid . '_thumb', asset('assets/images/no-cover.png'))
|
||||
: null;
|
||||
$sampleImageUrl = $hasSampleImage
|
||||
? getImageAssetUrl('sample', $release->guid . '_thumb', asset('assets/images/no-cover.png'))
|
||||
: null;
|
||||
@endphp
|
||||
|
||||
@if($hasPreviewImage || $hasSampleImage)
|
||||
<div class="border-b border-gray-200 dark:border-gray-700 pb-4">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-3 flex items-center">
|
||||
<i class="fas fa-images mr-2 text-primary-600"></i>
|
||||
@if($hasPreviewImage && $hasSampleImage)
|
||||
Preview & Sample Images
|
||||
@elseif($hasPreviewImage)
|
||||
Preview Image
|
||||
@else
|
||||
Sample Image
|
||||
@endif
|
||||
</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||||
@if($hasPreviewImage)
|
||||
<!-- Preview image -->
|
||||
<div>
|
||||
<div class="block cursor-pointer image-modal-trigger" data-image-url="{{ $previewImageUrl }}" data-image-title="Preview Image">
|
||||
<img src="{{ $previewImageUrl }}"
|
||||
alt="Preview"
|
||||
class="detail-gallery-image w-full h-auto rounded-lg"
|
||||
loading="lazy">
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mt-1 text-center">Preview</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($hasSampleImage)
|
||||
<!-- Sample image -->
|
||||
<div>
|
||||
<div class="block cursor-pointer image-modal-trigger" data-image-url="{{ $sampleImageUrl }}" data-image-title="Sample Image">
|
||||
<img src="{{ $sampleImageUrl }}"
|
||||
alt="Sample"
|
||||
class="detail-gallery-image w-full h-auto rounded-lg"
|
||||
loading="lazy">
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mt-1 text-center">Sample</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,38 @@
|
||||
<!-- TV Show Information -->
|
||||
@if(!empty($show))
|
||||
@php
|
||||
$showData = is_object($show) ? get_object_vars($show) : $show;
|
||||
$showTitle = $showData['title'] ?? ($show->title ?? null);
|
||||
$showStarted = $showData['started'] ?? ($show->started ?? null);
|
||||
$showTvdb = $showData['tvdb'] ?? ($show->tvdb ?? null);
|
||||
@endphp
|
||||
<div class="surface-panel-alt rounded-lg p-6 border">
|
||||
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center">
|
||||
<i class="fas fa-tv mr-2 text-primary-600 dark:text-primary-400"></i> TV Show Information
|
||||
</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
@if(!empty($showTitle))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Show Title</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100 font-semibold">{{ $showTitle }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($showStarted))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">Started</dt>
|
||||
<dd class="mt-1 text-sm text-gray-900 dark:text-gray-100">{{ $showStarted }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if(!empty($showTvdb))
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-gray-600 dark:text-gray-400">TVDB</dt>
|
||||
<dd class="mt-1">
|
||||
<a href="{{ $site['dereferrer_link'] }}https://thetvdb.com/?tab=series&id={{ $showTvdb }}" target="_blank" class="text-sm text-primary-600 dark:text-primary-400 hover:text-primary-700 dark:hover:text-primary-300">
|
||||
View on TVDB <i class="fas fa-external-link-alt text-xs"></i>
|
||||
</a>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@@ -127,6 +127,7 @@
|
||||
<img src="{{ url('/covers/games/' . $result->cover) }}"
|
||||
alt="{{ $result->title ?? $result->searchname }}"
|
||||
class="w-32 h-48 object-cover"
|
||||
loading="lazy"
|
||||
data-fallback-src="{{ url('/images/no-cover.png') }}">
|
||||
@else
|
||||
<div class="w-32 h-48 bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
|
||||
@@ -188,15 +189,14 @@
|
||||
{{ $results->links() }}
|
||||
</div>
|
||||
@else
|
||||
<!-- No Results -->
|
||||
<div class="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-700 rounded-lg p-8 text-center">
|
||||
<i class="fa fa-gamepad text-yellow-600 dark:text-yellow-500 text-5xl mb-4"></i>
|
||||
<h3 class="text-xl font-semibold text-gray-800 dark:text-gray-200 mb-2">No games found</h3>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-4">Try adjusting your search filters or browse all games.</p>
|
||||
<a href="{{ url('/Games') }}" class="inline-flex items-center px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
|
||||
<i class="fa fa-gamepad mr-2"></i> Browse All Games
|
||||
</a>
|
||||
</div>
|
||||
<x-empty-state
|
||||
icon="fa fa-gamepad"
|
||||
title="No games found"
|
||||
message="Try adjusting your search filters or browse all games."
|
||||
:action-url="url('/Games')"
|
||||
action-label="Browse All Games"
|
||||
action-icon="fa fa-gamepad"
|
||||
/>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<meta name="description" content="{{ $meta_description ?? 'Admin panel' }}">
|
||||
|
||||
<!-- Dark Mode - Set via meta tag for CSP compliance -->
|
||||
<meta name="theme-preference" content="{{ auth()->check() ? (auth()->user()->theme_preference ?? 'light') : 'light' }}">
|
||||
<meta name="theme-preference" content="{{ $userTheme }}">
|
||||
|
||||
<!-- TinyMCE API Key -->
|
||||
<meta name="tinymce-api-key" content="{{ config('tinymce.api_key', 'no-api-key') }}">
|
||||
@@ -51,26 +51,18 @@
|
||||
<h1 class="text-lg font-semibold text-gray-200">{{ $page_title ?? 'Admin Dashboard' }}</h1>
|
||||
<div class="flex items-center space-x-4">
|
||||
<button id="theme-toggle" class="bg-gray-700 dark:bg-gray-800 text-gray-100 dark:text-gray-200 px-3 py-2 rounded-lg shadow hover:bg-gray-600 dark:hover:bg-gray-700 transition-all duration-200 flex items-center gap-2 touch-target"
|
||||
title="{{ ucfirst(auth()->check() ? (auth()->user()->theme_preference ?? 'light') : 'light') }} Mode">
|
||||
title="{{ ucfirst($userTheme) }} Mode">
|
||||
<i id="theme-icon" class="fas
|
||||
@if(auth()->check())
|
||||
@if((auth()->user()->theme_preference ?? 'light') === 'dark')
|
||||
fa-moon
|
||||
@elseif((auth()->user()->theme_preference ?? 'light') === 'system')
|
||||
fa-desktop
|
||||
@else
|
||||
fa-sun
|
||||
@endif
|
||||
@if($userTheme === 'dark')
|
||||
fa-moon
|
||||
@elseif($userTheme === 'system')
|
||||
fa-desktop
|
||||
@else
|
||||
fa-sun
|
||||
@endif
|
||||
"></i>
|
||||
<span id="theme-label" class="text-xs font-medium hidden sm:inline">
|
||||
@if(auth()->check())
|
||||
{{ ucfirst(auth()->user()->theme_preference ?? 'light') }}
|
||||
@else
|
||||
Light
|
||||
@endif
|
||||
{{ ucfirst($userTheme) }}
|
||||
</span>
|
||||
</button>
|
||||
<a href="{{ url('/') }}" class="text-gray-300 dark:text-gray-400 hover:text-white transition">
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
<meta name="description" content="{{ $meta_description ?? '' }}">
|
||||
|
||||
<!-- Theme Preference - Set via meta tag for CSP compliance -->
|
||||
<meta name="theme-preference" content="{{ auth()->check() ? (auth()->user()->theme_preference ?? 'light') : 'light' }}">
|
||||
<meta name="color-scheme-preference" content="{{ auth()->check() ? (auth()->user()->color_scheme ?? 'blue') : 'blue' }}">
|
||||
<meta name="theme-preference" content="{{ $userTheme }}">
|
||||
<meta name="color-scheme-preference" content="{{ $userColorScheme }}">
|
||||
<!-- CSP Nonce for dynamic script loading -->
|
||||
<meta name="csp-nonce" content="{{ csp_nonce() }}">
|
||||
@auth
|
||||
@@ -103,7 +103,7 @@
|
||||
@include('partials.back-to-top')
|
||||
|
||||
<!-- Theme Toggle -->
|
||||
@php $themePreference = auth()->check() ? (auth()->user()->theme_preference ?? 'light') : 'light'; @endphp
|
||||
@php $themePreference = $userTheme; @endphp
|
||||
@guest
|
||||
<button id="theme-toggle" class="fixed z-50 bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 px-4 py-3 rounded-full shadow-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-all duration-200 flex items-center gap-2 touch-target bottom-[max(1rem,env(safe-area-inset-bottom))] right-[max(1rem,env(safe-area-inset-right))]"
|
||||
title="{{ ucfirst($themePreference) }} Mode">
|
||||
@@ -124,11 +124,11 @@
|
||||
@stack('scripts')
|
||||
|
||||
<!-- Theme Management Data (moved to csp-safe.js) -->
|
||||
@php $colorScheme = auth()->check() ? (auth()->user()->color_scheme ?? 'blue') : 'blue'; @endphp
|
||||
@php $colorScheme = $userColorScheme; @endphp
|
||||
<div id="current-theme-data"
|
||||
data-theme="{{ $themePreference }}"
|
||||
data-color-scheme="{{ $colorScheme }}"
|
||||
data-authenticated="{{ auth()->check() ? 'true' : 'false' }}"
|
||||
data-authenticated="{{ $loggedin ? 'true' : 'false' }}"
|
||||
data-update-url="{{ route('profile.update-theme') }}"
|
||||
class="hidden">
|
||||
</div>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<div class="shrink-0 relative p-4">
|
||||
<a href="{{ route('movie.view', ['imdbid' => $movie->imdbid]) }}" class="block">
|
||||
@if($movie->cover)
|
||||
<img src="{{ $movie->cover }}" alt="{{ $movie->title }}" class="w-full md:w-64 h-96 object-cover rounded-xl shadow-xl">
|
||||
<img src="{{ $movie->cover }}" alt="{{ $movie->title }}" class="w-full md:w-64 h-96 object-cover rounded-xl shadow-xl" loading="lazy">
|
||||
@else
|
||||
<div class="w-full md:w-64 h-96 bg-gray-200 dark:bg-gray-700 flex items-center justify-center rounded-xl shadow-xl">
|
||||
<i class="fas fa-film text-gray-400 text-5xl"></i>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<!-- Movie Poster -->
|
||||
<div class="lg:col-span-1">
|
||||
@if(!empty($movie['cover'] ?? null))
|
||||
<img src="{{ $movie['cover'] }}" alt="{{ $movie['title'] ?? 'Movie' }}" class="movie-detail-poster w-full rounded-lg">
|
||||
<img src="{{ $movie['cover'] }}" alt="{{ $movie['title'] ?? 'Movie' }}" class="movie-detail-poster w-full rounded-lg" loading="lazy">
|
||||
@else
|
||||
<div class="movie-detail-poster w-full h-96 bg-gray-200 dark:bg-gray-700 rounded-lg flex items-center justify-center">
|
||||
<i class="fas fa-film text-gray-400 text-6xl"></i>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
@if(isset($movie['cover']) && $movie['cover'])
|
||||
<div class="lg:col-span-1">
|
||||
<img src="{{ $movie['cover'] }}" alt="{{ $movie['title'] }}" class="w-full rounded-lg shadow-lg">
|
||||
<img src="{{ $movie['cover'] }}" alt="{{ $movie['title'] }}" class="w-full rounded-lg shadow-lg" loading="lazy">
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user