Add missed files

This commit is contained in:
DariusIII
2025-10-10 14:42:50 +02:00
parent fedfb9ed9e
commit 24c46c58ea
36 changed files with 8582 additions and 0 deletions
@@ -0,0 +1,203 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-edit mr-2"></i>{{ $title ?? 'Binary Black/Whitelist Edit' }}
</h1>
<a href="{{ url('/admin/binaryblacklist-list') }}" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200">
<i class="fa fa-arrow-left mr-2"></i>Back to List
</a>
</div>
</div>
<!-- Form -->
<form action="{{ url('/admin/binaryblacklist-edit?action=submit') }}" method="POST" id="blacklistForm" class="px-6 py-6">
@csrf
<input type="hidden" name="id" value="{{ $regex->id ?? '' }}"/>
<!-- Error Message -->
@if($error)
<div class="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
<div class="flex">
<i class="fa fa-exclamation-triangle text-red-500 mr-3"></i>
<p class="text-red-800">{{ $error }}</p>
</div>
</div>
@endif
<!-- Group Name -->
<div class="mb-6">
<label for="groupname" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Group Name: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-layer-group text-gray-400"></i>
</div>
<input type="text"
id="groupname"
name="groupname"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ htmlspecialchars($regex->groupname ?? '') }}"
required>
</div>
<p class="mt-2 text-sm text-gray-500">
<i class="fa fa-info-circle mr-1"></i>The full name of a valid newsgroup. (Wildcard in the format 'alt.binaries.*')
</p>
</div>
<!-- Regex -->
<div class="mb-6">
<label for="regex" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Regex: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-code text-gray-400"></i>
</div>
<textarea id="regex"
name="regex"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
rows="4"
required>{{ htmlspecialchars($regex->regex ?? '') }}</textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
<i class="fa fa-info-circle mr-1"></i>The regex to be applied. (Note: Beginning and Ending / are already included)
</p>
</div>
<!-- Description -->
<div class="mb-6">
<label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Description:
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-align-left text-gray-400"></i>
</div>
<textarea id="description"
name="description"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
rows="3">{{ htmlspecialchars($regex->description ?? '') }}</textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
<i class="fa fa-info-circle mr-1"></i>A description for this regex
</p>
</div>
<!-- Message Field -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Message Field:
</label>
<div class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
@foreach($msgcol_ids as $i => $id)
<div class="flex items-center {{ $loop->last ? '' : 'mb-3' }}">
<input type="radio"
name="msgcol"
id="msgcol{{ $id }}"
value="{{ $id }}"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ ($regex->msgcol ?? 1) == $id ? 'checked' : '' }}>
<label for="msgcol{{ $id }}" class="ml-3 text-sm text-gray-700">
{{ $msgcol_names[$i] }}
</label>
</div>
@endforeach
</div>
<p class="mt-2 text-sm text-gray-500">
<i class="fa fa-info-circle mr-1"></i>Which field in the message to apply the black/white list to.
</p>
</div>
<!-- Status -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Status:
</label>
<div class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
@foreach($status_ids as $i => $id)
<div class="flex items-center {{ $loop->last ? '' : 'mb-3' }}">
<input type="radio"
name="status"
id="status{{ $id }}"
value="{{ $id }}"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ ($regex->status ?? 1) == $id ? 'checked' : '' }}>
<label for="status{{ $id }}" class="ml-3 text-sm text-gray-700">
{{ $status_names[$i] }}
</label>
</div>
@endforeach
</div>
<p class="mt-2 text-sm text-gray-500">
<i class="fa fa-info-circle mr-1"></i>Only active regexes are applied during the release process.
</p>
</div>
<!-- Type -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Type:
</label>
<div class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
@foreach($optype_ids as $i => $id)
<div class="flex items-center {{ $loop->last ? '' : 'mb-3' }}">
<input type="radio"
name="optype"
id="optype{{ $id }}"
value="{{ $id }}"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ ($regex->optype ?? 1) == $id ? 'checked' : '' }}>
<label for="optype{{ $id }}" class="ml-3 text-sm text-gray-700">
{{ $optype_names[$i] }}
</label>
</div>
@endforeach
</div>
<p class="mt-2 text-sm text-gray-500">
<i class="fa fa-info-circle mr-1"></i>Black will exclude all messages for a group which match this regex. White will include only those which match.
</p>
</div>
</form>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between">
<a href="{{ url('/admin/binaryblacklist-list') }}" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Cancel
</a>
<button type="submit" form="blacklistForm" class="px-4 py-2 bg-green-600 dark:bg-green-700 text-white rounded-lg hover:bg-green-700">
<i class="fa fa-save mr-2"></i>Save Changes
</button>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('blacklistForm');
if (form) {
form.addEventListener('submit', function(event) {
const groupname = document.getElementById('groupname');
const regex = document.getElementById('regex');
if (!groupname.value.trim() || !regex.value.trim()) {
event.preventDefault();
alert('Please fill in all required fields.');
return false;
}
});
}
});
</script>
@endpush
@endsection
@@ -0,0 +1,202 @@
@extends('layouts.admin')
@section('content')
<div class="max-w-full px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-ban mr-2"></i>{{ $title ?? 'Binary Black/White List' }}
</h1>
<a href="{{ url('/admin/binaryblacklist-edit') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add New Blacklist
</a>
</div>
</div>
<!-- Info Alert -->
<div class="px-6 py-4 bg-blue-50 border-b border-blue-100">
<div class="flex">
<div class="flex-shrink-0">
<i class="fa fa-info-circle text-blue-500 text-2xl"></i>
</div>
<div class="ml-3">
<p class="text-sm text-blue-700">
Binaries can be prevented from being added to the index if they match a regex in the blacklist.
They can also be included only if they match a regex (whitelist).
<strong>Click Edit or on the blacklist to enable/disable.</strong>
</p>
</div>
</div>
</div>
<!-- Messages -->
<div id="message" class="px-6"></div>
<!-- Table -->
@if(count($binlist) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-16">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Group</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-20">Type</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Field</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-20">Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Regex</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-36">Last Activity</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-28">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($binlist as $bin)
<tr id="row-{{ $bin->id }}" class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-semibold text-gray-900">{{ $bin->id }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<span class="inline-block truncate max-w-[150px]" title="{{ $bin->groupname }}">
{{ str_replace('alt.binaries', 'a.b', $bin->groupname) }}
</span>
</td>
<td class="px-6 py-4 text-sm text-gray-500">
<span class="inline-block truncate max-w-[180px]" title="{{ $bin->description }}">
{{ $bin->description }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
@if($bin->optype == 1)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Black</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">White</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
@if($bin->msgcol == 1)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">Subject</span>
@elseif($bin->msgcol == 2)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">Poster</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800">MessageID</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
@if($bin->status == 1)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Disabled</span>
@endif
</td>
<td class="px-6 py-4 text-sm">
<div class="max-w-[200px] break-words">
<a href="{{ url('/admin/binaryblacklist-edit?id=' . $bin->id) }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-900" title="{{ htmlspecialchars($bin->regex) }}">
<code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs break-all">{{ htmlspecialchars($bin->regex) }}</code>
</a>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-500">
@if($bin->last_activity)
<span title="{{ $bin->last_activity }}">
<i class="fa fa-clock-o mr-1 text-gray-400"></i>{{ $bin->last_activity }}
</span>
@else
<span class="text-gray-400">Never</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-medium">
<div class="flex gap-2 justify-center">
<a href="{{ url('/admin/binaryblacklist-edit?id=' . $bin->id) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900"
title="Edit this blacklist">
<i class="fa fa-edit"></i>
</a>
<button type="button"
class="text-red-600 hover:text-red-900"
onclick="if(confirm('Are you sure? This will delete the blacklist from this list.')) { ajax_binaryblacklist_delete({{ $bin->id }}) }"
title="Delete this blacklist">
<i class="fa fa-trash"></i>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">Total entries: {{ count($binlist) }}</span>
<a href="{{ url('/admin/binaryblacklist-edit') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-800 text-sm">
<i class="fa fa-plus mr-2"></i>Add New Blacklist
</a>
</div>
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-ban text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No blacklist entries found</h3>
<p class="text-gray-500 mb-4">Get started by adding your first blacklist entry.</p>
<a href="{{ url('/admin/binaryblacklist-edit') }}" 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-plus mr-2"></i>Add New Blacklist
</a>
</div>
@endif
</div>
</div>
@push('scripts')
<script>
function ajax_binaryblacklist_delete(id) {
fetch('{{ url("/admin/binaryblacklist-delete") }}?id=' + id, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const row = document.getElementById('row-' + id);
row.style.transition = 'opacity 0.3s';
row.style.opacity = '0';
setTimeout(() => row.remove(), 300);
showMessage('Blacklist entry deleted successfully', 'success');
} else {
showMessage('Error deleting blacklist entry', 'error');
}
})
.catch(error => {
showMessage('Error deleting blacklist entry', 'error');
});
}
function showMessage(message, type = 'success') {
const messageDiv = document.getElementById('message');
const bgColor = type === 'success' ? 'bg-green-50 border-green-200' : 'bg-red-50 border-red-200';
const textColor = type === 'success' ? 'text-green-800' : 'text-red-800';
const icon = type === 'success' ? 'check-circle' : 'exclamation-circle';
messageDiv.innerHTML = `
<div class="mt-4 p-4 ${bgColor} border rounded-lg">
<div class="flex items-center justify-between">
<p class="${textColor}">
<i class="fa fa-${icon} mr-2"></i>${message}
</p>
<button type="button" class="${textColor} hover:opacity-75" onclick="this.parentElement.parentElement.remove()">
<i class="fa fa-times"></i>
</button>
</div>
</div>
`;
setTimeout(() => {
messageDiv.innerHTML = '';
}, 5000);
}
</script>
@endpush
@endsection
@@ -0,0 +1,174 @@
@extends('layouts.admin')
@section('title', $title ?? 'Category Edit')
@section('content')
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h4 class="mb-0">{{ $title }}</h4>
<a href="{{ url('/admin/category-list') }}" class="btn btn-outline-secondary">
<i class="fa fa-arrow-left me-2"></i>Back to Categories
</a>
</div>
</div>
@if(session('error'))
<div class="alert alert-danger m-3">
<i class="fa fa-exclamation-circle me-2"></i>{{ session('error') }}
</div>
@endif
<div class="card-body">
@if($category)
<form action="{{ url('/admin/category-edit?action=submit') }}" method="POST" id="categoryForm">
@csrf
<input type="hidden" name="id" value="{{ $category->id }}"/>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label class="form-label fw-bold">Title:</label>
</div>
<div class="col-lg-9 col-md-8">
<p class="form-control-plaintext">{{ $category->title }}</p>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="parentcat" class="form-label fw-bold">Parent Category:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-folder-open"></i></span>
<input type="text" class="form-control" value="{{ $category->parent->title ?? 'N/A' }}" disabled>
</div>
<small class="text-muted">Parent category cannot be changed from this interface</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="description" class="form-label fw-bold">Description:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-align-left"></i></span>
<input type="text" id="description" name="description" class="form-control" value="{{ $category->description ?? '' }}"/>
</div>
<small class="text-muted">Brief explanation of what belongs in this category</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="minsizetoformrelease" class="form-label fw-bold">Minimum Size (Bytes):</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-download"></i></span>
<input type="number" id="minsizetoformrelease" name="minsizetoformrelease" class="form-control" value="{{ $category->minsizetoformrelease ?? 0 }}"/>
</div>
<small class="text-muted">Minimum file size for releases in this category (in bytes). Set to 0 to disable.</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="maxsizetoformrelease" class="form-label fw-bold">Maximum Size (Bytes):</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-upload"></i></span>
<input type="number" id="maxsizetoformrelease" name="maxsizetoformrelease" class="form-control" value="{{ $category->maxsizetoformrelease ?? 0 }}"/>
</div>
<small class="text-muted">Maximum file size for releases in this category (in bytes). Set to 0 to disable.</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label class="form-label fw-bold">Status:</label>
</div>
<div class="col-lg-9 col-md-8">
@foreach($status_ids as $index => $statusId)
<div class="form-check form-check-inline">
<input type="radio" id="status_{{ $statusId }}" name="status" value="{{ $statusId }}"
class="form-check-input" {{ ($category->status ?? 0) == $statusId ? 'checked' : '' }}>
<label for="status_{{ $statusId }}" class="form-check-label">{{ $status_names[$index] }}</label>
</div>
@endforeach
<div class="mt-2">
<small class="text-muted">Inactive categories won't appear in menus but can still be used for release matching</small>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label class="form-label fw-bold">Preview:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="form-check form-check-inline">
<input type="radio" id="disablepreview_0" name="disablepreview" value="0"
class="form-check-input" {{ ($category->disablepreview ?? 0) == 0 ? 'checked' : '' }}>
<label for="disablepreview_0" class="form-check-label">Enabled</label>
</div>
<div class="form-check form-check-inline">
<input type="radio" id="disablepreview_1" name="disablepreview" value="1"
class="form-check-input" {{ ($category->disablepreview ?? 0) == 1 ? 'checked' : '' }}>
<label for="disablepreview_1" class="form-check-label">Disabled</label>
</div>
<div class="mt-2">
<small class="text-muted">Disabling prevents ffmpeg from generating previews for releases in this category</small>
</div>
</div>
</div>
<div class="card-footer">
<div class="d-flex justify-content-between">
<button type="button" class="btn btn-outline-secondary" onclick="window.location='{{ url('/admin/category-list') }}'">
<i class="fa fa-times me-2"></i>Cancel
</button>
<button type="submit" class="btn btn-success">
<i class="fa fa-save me-2"></i>Save Changes
</button>
</div>
</div>
</form>
@else
<div class="alert alert-warning">
<i class="fa fa-exclamation-triangle me-2"></i>No category selected. Please select a category to edit.
</div>
<div class="text-center mt-3">
<a href="{{ url('/admin/category-list') }}" class="btn btn-primary">
<i class="fa fa-list me-2"></i>View Category List
</a>
</div>
@endif
</div>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Form validation
const form = document.getElementById('categoryForm');
if (form) {
form.addEventListener('submit', function(event) {
const minSize = document.getElementById('minsizetoformrelease').value;
const maxSize = document.getElementById('maxsizetoformrelease').value;
// Validate min/max size relationship
if (parseInt(minSize) > parseInt(maxSize) && parseInt(maxSize) > 0) {
event.preventDefault();
alert('Minimum size cannot be greater than maximum size');
return false;
}
});
}
});
</script>
@endpush
@endsection
@@ -0,0 +1,224 @@
@extends('layouts.admin')
@section('title', $title ?? 'Category List')
@section('content')
<div class="card">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h4 class="mb-0">{{ $title }}</h4>
<a href="{{ url('/admin/category-add') }}" class="btn btn-sm btn-primary">
<i class="fa fa-plus me-2"></i>Add New Category
</a>
</div>
</div>
<div class="card-body">
<div class="alert alert-info mb-4">
<i class="fa fa-info-circle me-2"></i>
Make a category inactive to remove it from the menu. This does not prevent binaries being matched into an
appropriate category. Disable preview prevents ffmpeg being used for releases in the category.
</div>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead class="thead-light">
<tr>
<th>
<div class="d-flex align-items-center gap-2">
<span>ID</span>
<div class="sort-controls">
<a href="?sort=id&order=asc" class="sort-icon {{ (request('sort') == 'id' && request('order') == 'asc') ? 'active' : '' }}" title="Sort Ascending">
<i class="fas fa-sort-numeric-down"></i>
</a>
<a href="?sort=id&order=desc" class="sort-icon {{ (request('sort') == 'id' && request('order') == 'desc') ? 'active' : '' }}" title="Sort Descending">
<i class="fas fa-sort-numeric-down-alt"></i>
</a>
</div>
</div>
</th>
<th>
<div class="d-flex align-items-center gap-2">
<span>Title</span>
<div class="sort-controls">
<a href="?sort=title&order=asc" class="sort-icon {{ (request('sort') == 'title' && request('order') == 'asc') ? 'active' : '' }}" title="Sort Ascending">
<i class="fas fa-sort-alpha-down"></i>
</a>
<a href="?sort=title&order=desc" class="sort-icon {{ (request('sort') == 'title' && request('order') == 'desc') ? 'active' : '' }}" title="Sort Descending">
<i class="fas fa-sort-alpha-down-alt"></i>
</a>
</div>
</div>
</th>
<th>Parent</th>
<th>Min Size</th>
<th>Max Size</th>
<th class="text-center">Status</th>
<th class="text-center">Preview</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
@foreach($categorylist as $category)
<tr>
<td>{{ $category->id }}</td>
<td>
<a href="{{ url('/admin/category-edit?id=' . $category->id) }}" class="fw-medium text-decoration-none">
{{ $category->title }}
</a>
</td>
<td>
@if($category->parent)
<span class="badge bg-secondary rounded-pill">
{{ $category->parent->title }}
</span>
@else
<span class="text-muted">N/A</span>
@endif
</td>
<td>
@if($category->minsizetoformrelease != 0)
<div class="d-flex align-items-center">
<i class="fa fa-file-archive-o text-muted me-2"></i>
<span>{{ \Blacklight\utility\Utility::bytesToSizeString($category->minsizetoformrelease) }}</span>
</div>
@else
<span class="text-muted"></span>
@endif
</td>
<td>
@if($category->maxsizetoformrelease != 0)
<div class="d-flex align-items-center">
<i class="fa fa-file-archive-o text-muted me-2"></i>
<span>{{ \Blacklight\utility\Utility::bytesToSizeString($category->maxsizetoformrelease) }}</span>
</div>
@else
<span class="text-muted"></span>
@endif
</td>
<td class="text-center">
<span class="badge {{ $category->status == 1 ? 'bg-success' : 'bg-danger' }}">
{{ $category->status == 1 ? 'Active' : 'Inactive' }}
</span>
</td>
<td class="text-center">
<span class="badge {{ $category->disablepreview == 1 ? 'bg-warning text-dark' : 'bg-success' }}">
{{ $category->disablepreview == 1 ? 'Disabled' : 'Enabled' }}
</span>
</td>
<td class="text-end">
<div class="btn-group" role="group">
<a href="{{ url('/admin/category-edit?id=' . $category->id) }}" class="btn btn-sm btn-outline-secondary" data-bs-toggle="tooltip" title="Edit Category">
<i class="fa fa-edit"></i>
</a>
<button type="button" class="btn btn-sm btn-outline-danger" onclick="confirmDelete({{ $category->id }})" data-bs-toggle="tooltip" title="Delete Category">
<i class="fa fa-trash"></i>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@if(count($categorylist) == 0)
<div class="alert alert-info">
<i class="fa fa-info-circle me-2"></i>No categories found.
</div>
@endif
</div>
<div class="card-footer">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted">Total: {{ count($categorylist) }} categories</span>
</div>
<div>
<a href="{{ url('/admin/category-add') }}" class="btn btn-sm btn-primary">
<i class="fa fa-plus me-2"></i>Add New Category
</a>
</div>
</div>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Confirm Delete</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete this category? This may impact site functionality and cannot be undone.</p>
<p class="text-danger"><strong>Warning:</strong> Deleting a category with child categories or releases will cause orphaned data.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<a href="#" id="confirmDeleteLink" class="btn btn-danger">Delete</a>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
});
// Delete confirmation handling
function confirmDelete(id) {
const modal = new bootstrap.Modal(document.getElementById('deleteModal'));
const deleteLink = document.getElementById('confirmDeleteLink');
deleteLink.href = `{{ url('/admin/category-delete?id=') }}${id}`;
modal.show();
}
</script>
@endpush
@push('styles')
<style>
/* Table improvements */
.table td, .table th {
vertical-align: middle;
}
/* Sort controls styling */
.sort-icon {
color: #aaa;
font-size: 0.8rem;
display: block;
line-height: 0.8;
}
.sort-icon.active {
color: #0d6efd;
}
.sort-controls {
display: flex;
flex-direction: column;
gap: 2px;
}
/* Badge improvements */
.badge {
font-weight: 500;
padding: 0.35em 0.65em;
}
/* Button group improvements */
.btn-group .btn {
padding: 0.25rem 0.5rem;
}
</style>
@endpush
@endsection
+250
View File
@@ -0,0 +1,250 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-file-alt mr-2"></i>{{ $title }}
</h1>
</div>
<!-- Content Form -->
<form method="post" action="{{ url('admin/content-add') }}" class="p-6">
@csrf
<input type="hidden" name="action" value="submit">
@if(!empty($content['id']))
<input type="hidden" name="id" value="{{ is_array($content) ? $content['id'] : $content->id }}">
@endif
<div class="space-y-6">
<!-- Title -->
<div>
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Title <span class="text-red-500">*</span>
</label>
<input type="text"
id="title"
name="title"
value="{{ is_array($content) ? ($content['title'] ?? '') : ($content->title ?? '') }}"
required
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">
</div>
<!-- URL -->
<div>
<label for="url" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
URL
</label>
<input type="text"
id="url"
name="url"
value="{{ is_array($content) ? ($content['url'] ?? '') : ($content->url ?? '') }}"
placeholder="/page-slug or https://example.com"
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">
<p class="mt-1 text-sm text-gray-500">Internal URL (e.g., /about) or external URL (e.g., https://example.com)</p>
</div>
<!-- Body -->
<div>
<label for="body" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Body Content
</label>
<textarea id="body"
name="body"
rows="15"
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">{{ is_array($content) ? trim(($content['body'] ?? ''), '\'"') : trim(($content->body ?? ''), '\'"') }}</textarea>
<p class="mt-1 text-sm text-gray-500">Use the rich text editor to format your content</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Content Type -->
<div>
<label for="contenttype" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Content Type <span class="text-red-500">*</span>
</label>
<select id="contenttype"
name="contenttype"
required
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">
@foreach($contenttypelist as $typeId => $typeName)
<option value="{{ $typeId }}"
{{ (is_array($content) ? ($content['contenttype'] ?? '') : ($content->contenttype ?? '')) == $typeId ? 'selected' : '' }}>
{{ $typeName }}
</option>
@endforeach
</select>
</div>
<!-- Role -->
<div>
<label for="role" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Visible To <span class="text-red-500">*</span>
</label>
<select id="role"
name="role"
required
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">
@foreach($rolelist as $roleId => $roleName)
<option value="{{ $roleId }}"
{{ (is_array($content) ? ($content['role'] ?? '') : ($content->role ?? '')) == $roleId ? 'selected' : '' }}>
{{ $roleName }}
</option>
@endforeach
</select>
</div>
<!-- Status -->
<div>
<label for="status" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Status <span class="text-red-500">*</span>
</label>
<select id="status"
name="status"
required
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">
@foreach($status_ids as $index => $statusId)
<option value="{{ $statusId }}"
{{ (is_array($content) ? ($content['status'] ?? '') : ($content->status ?? '')) == $statusId ? 'selected' : '' }}>
{{ $status_names[$index] }}
</option>
@endforeach
</select>
</div>
<!-- Ordinal -->
<div>
<label for="ordinal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Order (Ordinal)
</label>
<input type="number"
id="ordinal"
name="ordinal"
value="{{ is_array($content) ? ($content['ordinal'] ?? 0) : ($content->ordinal ?? 0) }}"
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">
<p class="mt-1 text-sm text-gray-500">Lower numbers appear first</p>
</div>
</div>
<!-- Meta Description -->
<div>
<label for="metadescription" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
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">{{ is_array($content) ? ($content['metadescription'] ?? '') : ($content->metadescription ?? '') }}</textarea>
<p class="mt-1 text-sm text-gray-500">SEO meta description</p>
</div>
<!-- Meta Keywords -->
<div>
<label for="metakeywords" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Meta Keywords
</label>
<input type="text"
id="metakeywords"
name="metakeywords"
value="{{ is_array($content) ? ($content['metakeywords'] ?? '') : ($content->metakeywords ?? '') }}"
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">
<p class="mt-1 text-sm text-gray-500">Comma-separated keywords for SEO</p>
</div>
<!-- Action Buttons -->
<div class="flex gap-3 pt-4 border-t border-gray-200">
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-save mr-2"></i>{{ !empty($content['id']) ? 'Update' : 'Create' }} Content
</button>
<a href="{{ url('admin/content-list') }}" class="px-6 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Cancel
</a>
</div>
</div>
</form>
</div>
</div>
@push('scripts')
<!-- TinyMCE 8 (Latest Version) -->
<script src="https://cdn.tiny.cloud/1/{{ config('tinymce.api_key', 'no-api-key') }}/tinymce/8/tinymce.min.js" referrerpolicy="origin"></script>
<script>
tinymce.init({
selector: '#body',
height: 500,
menubar: true,
plugins: [
'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
'insertdatetime', 'media', 'table', 'help', 'wordcount', 'emoticons'
],
toolbar: 'undo redo | blocks fontfamily fontsize | ' +
'bold italic underline strikethrough | forecolor backcolor | ' +
'alignleft aligncenter alignright alignjustify | ' +
'bullist numlist outdent indent | ' +
'link image media table emoticons | ' +
'removeformat code fullscreen | help',
toolbar_mode: 'sliding',
content_style: 'body { font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.6; }',
branding: false,
promotion: false,
resize: true,
statusbar: true,
elementpath: true,
images_upload_url: false,
automatic_uploads: false,
file_picker_types: 'image',
/* Font options */
font_family_formats: 'Arial=arial,helvetica,sans-serif; Courier New=courier new,courier,monospace; Georgia=georgia,palatino,serif; Tahoma=tahoma,arial,helvetica,sans-serif; Times New Roman=times new roman,times,serif; Verdana=verdana,geneva,sans-serif',
font_size_formats: '8pt 10pt 12pt 14pt 16pt 18pt 24pt 36pt 48pt',
/* Enable automatic link creation */
autolink_pattern: /^(https?:\/\/|www\.|(?!www\.)[a-z0-9\-]+\.[a-z]{2,13})/i,
link_default_protocol: 'https',
link_assume_external_targets: true,
/* Link target options */
link_target_list: [
{title: 'None', value: ''},
{title: 'New window', value: '_blank'},
{title: 'Same window', value: '_self'}
],
/* Block formats */
block_formats: 'Paragraph=p; Heading 1=h1; Heading 2=h2; Heading 3=h3; Heading 4=h4; Heading 5=h5; Heading 6=h6; Preformatted=pre; Blockquote=blockquote',
/* Content filtering - allow all HTML */
valid_elements: '*[*]',
extended_valid_elements: '*[*]',
valid_children: '+body[style]',
/* Paste settings */
paste_as_text: false,
paste_block_drop: false,
paste_data_images: true,
paste_retain_style_properties: 'all',
/* Image settings */
image_advtab: true,
image_caption: true,
image_description: true,
image_dimensions: true,
image_title: true,
/* Table settings */
table_default_attributes: {
border: '1'
},
table_default_styles: {
'border-collapse': 'collapse',
'width': '100%'
},
table_responsive_width: true,
/* Auto-save */
setup: function(editor) {
editor.on('change', function() {
editor.save();
});
editor.on('blur', function() {
editor.save();
});
}
});
</script>
@endpush
@endsection
@@ -0,0 +1,120 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-file-alt mr-2"></i>{{ $title }}
</h1>
<a href="{{ url('admin/content-add?action=add') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add New Content
</a>
</div>
</div>
<!-- Content Table -->
@if(count($contentlist) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Title</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">URL</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Ordinal</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($contentlist as $item)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ $item->id }}</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">{{ $item->title }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
@if(!empty($item->url))
<a href="{{ $item->url }}" target="_blank" class="text-blue-600 dark:text-blue-400 hover:text-blue-800">
{{ Str::limit($item->url, 30) }}
</a>
@else
N/A
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
@if($item->contenttype == 1)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">
Useful Link
</span>
@elseif($item->contenttype == 2)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
Article
</span>
@elseif($item->contenttype == 3)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-purple-100 text-purple-800">
Homepage
</span>
@else
N/A
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
@if($item->role == 1)
Everyone
@elseif($item->role == 2)
Logged in Users
@elseif($item->role == 3)
Admins
@else
N/A
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap">
@if($item->status == 1)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check mr-1"></i>Enabled
</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times mr-1"></i>Disabled
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $item->ordinal ?? 0 }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div class="flex gap-2">
<a href="{{ url('admin/content-add?id=' . $item->id) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900"
title="Edit">
<i class="fa fa-edit"></i>
</a>
<a href="{{ url('admin/content-delete?id=' . $item->id) }}"
class="text-red-600 hover:text-red-900"
title="Delete"
onclick="return confirm('Are you sure you want to delete \'{{ $item->title }}\'?')">
<i class="fa fa-trash"></i>
</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-file-alt text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No content found</h3>
<p class="text-gray-500">Create your first content to get started.</p>
</div>
@endif
</div>
</div>
@endsection
+215
View File
@@ -0,0 +1,215 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-gamepad mr-2"></i>{{ $title }}
</h1>
</div>
<!-- Success/Error/Warning Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg">
<p class="text-red-800 dark:text-red-200">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
@if(session('warning'))
<div class="mx-6 mt-4 p-4 bg-yellow-50 dark:bg-yellow-900 border border-yellow-200 dark:border-yellow-700 rounded-lg">
<p class="text-yellow-800 dark:text-yellow-200">
<i class="fa fa-exclamation-triangle mr-2"></i>{{ session('warning') }}
</p>
</div>
@endif
<!-- Edit Game Form -->
<form method="POST" action="{{ url('admin/game-edit') }}" enctype="multipart/form-data" class="p-6">
@csrf
<input type="hidden" name="id" value="{{ $game['id'] }}">
<input type="hidden" name="action" value="submit">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Left Column -->
<div class="space-y-6">
<!-- Title -->
<div>
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Title <span class="text-red-500">*</span>
</label>
<input type="text"
id="title"
name="title"
value="{{ $game['title'] ?? '' }}"
required
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- Publisher -->
<div>
<label for="publisher" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Publisher
</label>
<input type="text"
id="publisher"
name="publisher"
value="{{ $game['publisher'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- ASIN -->
<div>
<label for="asin" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
ASIN
</label>
<input type="text"
id="asin"
name="asin"
value="{{ $game['asin'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- URL -->
<div>
<label for="url" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
URL
</label>
<input type="url"
id="url"
name="url"
value="{{ $game['url'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- Trailer URL -->
<div>
<label for="trailerurl" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Trailer URL
</label>
<input type="url"
id="trailerurl"
name="trailerurl"
value="{{ $game['trailerurl'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
</div>
<!-- Right Column -->
<div class="space-y-6">
<!-- Genre -->
<div>
<label for="genre" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Genre
</label>
<select id="genre"
name="genre"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
<option value="">-- Select Genre --</option>
@if(!empty($genres))
@foreach($genres as $genreItem)
<option value="{{ $genreItem['id'] }}"
{{ (isset($game['genre_id']) && $game['genre_id'] == $genreItem['id']) ? 'selected' : '' }}>
{{ $genreItem['title'] }}
</option>
@endforeach
@endif
</select>
</div>
<!-- ESRB Rating -->
<div>
<label for="esrb" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
ESRB Rating
</label>
<select id="esrb"
name="esrb"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
<option value="">-- Select Rating --</option>
<option value="eC - Early Childhood" {{ (isset($game['esrb']) && $game['esrb'] == 'eC - Early Childhood') ? 'selected' : '' }}>eC - Early Childhood</option>
<option value="E - Everyone" {{ (isset($game['esrb']) && $game['esrb'] == 'E - Everyone') ? 'selected' : '' }}>E - Everyone</option>
<option value="E10+ - Everyone 10+" {{ (isset($game['esrb']) && $game['esrb'] == 'E10+ - Everyone 10+') ? 'selected' : '' }}>E10+ - Everyone 10+</option>
<option value="T - Teen" {{ (isset($game['esrb']) && $game['esrb'] == 'T - Teen') ? 'selected' : '' }}>T - Teen</option>
<option value="M - Mature" {{ (isset($game['esrb']) && $game['esrb'] == 'M - Mature') ? 'selected' : '' }}>M - Mature</option>
<option value="AO - Adults Only" {{ (isset($game['esrb']) && $game['esrb'] == 'AO - Adults Only') ? 'selected' : '' }}>AO - Adults Only</option>
<option value="RP - Rating Pending" {{ (isset($game['esrb']) && $game['esrb'] == 'RP - Rating Pending') ? 'selected' : '' }}>RP - Rating Pending</option>
</select>
</div>
<!-- Release Date -->
<div>
<label for="releasedate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Release Date
</label>
<input type="date"
id="releasedate"
name="releasedate"
value="{{ !empty($game['releasedate']) ? date('Y-m-d', $game['releasedate']) : '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- Cover Image -->
<div>
<label for="cover" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Cover Image
</label>
@if(!empty($game['cover']) && $game['cover'] == 1)
<div class="mb-3">
<img src="{{ asset('storage/covers/games/' . $game['id'] . '.jpg') }}"
alt="Game Cover"
class="max-w-xs rounded-lg shadow-md border border-gray-300 dark:border-gray-600">
</div>
@endif
<input type="file"
id="cover"
name="cover"
accept="image/jpeg,image/jpg"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Upload a new cover image (JPG format)
</p>
</div>
<!-- Current Cover Status -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Current Cover Status
</label>
@if(!empty($game['cover']) && $game['cover'] == 1)
<span class="inline-flex items-center px-3 py-1 text-sm font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check mr-2"></i> Cover Available
</span>
@else
<span class="inline-flex items-center px-3 py-1 text-sm font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times mr-2"></i> No Cover
</span>
@endif
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="mt-8 flex gap-3 border-t border-gray-200 pt-6">
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-save mr-2"></i>Save Changes
</button>
<a href="{{ route('admin.game-list') }}" class="px-6 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
<i class="fa fa-arrow-left mr-2"></i>Back to Game List
</a>
</div>
</form>
</div>
</div>
@endsection
+145
View File
@@ -0,0 +1,145 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-gamepad mr-2"></i>{{ $title }}
</h1>
</div>
</div>
<!-- Success/Error/Warning Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg">
<p class="text-red-800 dark:text-red-200">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
@if(session('warning'))
<div class="mx-6 mt-4 p-4 bg-yellow-50 dark:bg-yellow-900 border border-yellow-200 dark:border-yellow-700 rounded-lg">
<p class="text-yellow-800 dark:text-yellow-200">
<i class="fa fa-exclamation-triangle mr-2"></i>{{ session('warning') }}
</p>
</div>
@endif
<!-- Search Form -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<form method="GET" action="{{ url('admin/game-list') }}">
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-search text-gray-400"></i>
</div>
<input type="text"
name="gamesearch"
value="{{ $lastSearch ?? '' }}"
placeholder="Search by game title..."
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<button type="submit" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
Search
</button>
@if(!empty($lastSearch))
<a href="{{ url('admin/game-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
Clear
</a>
@endif
</div>
</form>
</div>
<!-- Game List Table -->
@if(!empty($gamelist) && count($gamelist) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Title</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Publisher</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Genre</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">ESRB</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Release Date</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Cover</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@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">
{{ $game['id'] ?? 'N/A' }}
</td>
<td class="px-6 py-4">
<div class="text-sm font-medium text-gray-900 dark:text-gray-200">
{{ $game['title'] ?? 'N/A' }}
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{{ $game['publisher'] ?? 'N/A' }}
</td>
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{{ $game['genre'] ?? 'N/A' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{{ $game['esrb'] ?? 'N/A' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
@if(!empty($game['releasedate']))
{{ date('Y-m-d', $game['releasedate']) }}
@else
N/A
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
@if(!empty($game['cover']) && $game['cover'] == 1)
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check"></i> Yes
</span>
@else
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times"></i> No
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<a href="{{ url('admin/game-edit?id=' . $game['id']) }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-900">
<i class="fa fa-edit"></i> Edit
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa 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>
@endif
</div>
</div>
@endsection
+209
View File
@@ -0,0 +1,209 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-plus-square mr-2"></i>{{ $title ?? 'Bulk Add Newsgroups' }}
</h1>
<a href="{{ url('/admin/group-list') }}" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200">
<i class="fa fa-list mr-2"></i>View All Groups
</a>
</div>
</div>
<div class="px-6 py-6">
@if(!empty($groupmsglist))
<!-- Success Info -->
<div class="mb-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<div class="flex">
<i class="fa fa-info-circle text-blue-500 text-xl mr-3"></i>
<p class="text-blue-700">
The following groups have been processed. You can now view them in the group list.
</p>
</div>
</div>
<!-- Results Table -->
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Group</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($groupmsglist as $group)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<i class="fa fa-users text-gray-400 mr-3"></i>
<span class="font-medium text-gray-900">{{ $group['group'] }}</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
@if(strpos($group['msg'], 'Error') !== false)
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-red-100 text-red-800">
<i class="fa fa-exclamation-circle mr-1"></i>{{ $group['msg'] }}
</span>
@elseif(strpos($group['msg'], 'exists') !== false)
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-yellow-100 text-yellow-800">
<i class="fa fa-exclamation-triangle mr-1"></i>{{ $group['msg'] }}
</span>
@else
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">
<i class="fa fa-check-circle mr-1"></i>{{ $group['msg'] }}
</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<!-- Info Alert -->
<div class="mb-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<div class="flex">
<i class="fa fa-info-circle text-blue-500 text-xl mr-3"></i>
<p class="text-blue-700">
Enter a regular expression to match multiple groups for bulk addition to the system.
</p>
</div>
</div>
<!-- Form -->
<form action="{{ url('/admin/group-bulk?action=submit') }}" method="POST" id="groupBulkForm">
@csrf
<!-- Group Pattern -->
<div class="mb-6">
<label for="groupfilter" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Group Pattern: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-filter text-gray-400"></i>
</div>
<textarea id="groupfilter"
name="groupfilter"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
rows="5"
placeholder="e.g. alt.binaries.cd.image.linux|alt.binaries.warez.linux"></textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
A regular expression to match against group names. Separate multiple patterns with the pipe symbol (|).
<br>Example: <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs text-pink-600">alt.binaries.cd.image.linux|alt.binaries.warez.linux</code>
</p>
</div>
<!-- Active Status -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Active:</label>
<div class="flex items-center gap-6">
<div class="flex items-center">
<input type="radio"
name="active"
id="active_yes"
value="1"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
checked>
<label for="active_yes" class="ml-2 text-sm text-gray-700">Yes</label>
</div>
<div class="flex items-center">
<input type="radio"
name="active"
id="active_no"
value="0"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500">
<label for="active_no" class="ml-2 text-sm text-gray-700">No</label>
</div>
</div>
<p class="mt-2 text-sm text-gray-500">
Inactive groups will not have headers downloaded for them.
</p>
</div>
<!-- Backfill Status -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Backfill:</label>
<div class="flex items-center gap-6">
<div class="flex items-center">
<input type="radio"
name="backfill"
id="backfill_yes"
value="1"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500">
<label for="backfill_yes" class="ml-2 text-sm text-gray-700">Yes</label>
</div>
<div class="flex items-center">
<input type="radio"
name="backfill"
id="backfill_no"
value="0"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
checked>
<label for="backfill_no" class="ml-2 text-sm text-gray-700">No</label>
</div>
</div>
<p class="mt-2 text-sm text-gray-500">
Inactive groups will not have backfill headers downloaded for them.
</p>
</div>
</form>
@endif
</div>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between">
<a href="{{ url('/admin/group-list') }}" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-arrow-left mr-2"></i>Back to Groups
</a>
@if(empty($groupmsglist))
<button type="submit" form="groupBulkForm" class="px-4 py-2 bg-green-600 dark:bg-green-700 text-white rounded-lg hover:bg-green-700">
<i class="fa fa-plus-circle mr-2"></i>Add Groups
</button>
@else
<a href="{{ url('/admin/group-bulk') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus-circle mr-2"></i>Add More Groups
</a>
@endif
</div>
</div>
</div>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('groupBulkForm');
if (form) {
form.addEventListener('submit', function(event) {
const groupfilter = document.getElementById('groupfilter').value.trim();
if (groupfilter === '') {
event.preventDefault();
alert('Please enter a group pattern');
return false;
}
// Simple regex validation
try {
new RegExp(groupfilter);
} catch(e) {
event.preventDefault();
alert('Invalid regex pattern: ' + e.message);
return false;
}
});
}
});
</script>
@endpush
@endsection
+266
View File
@@ -0,0 +1,266 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-edit mr-2"></i>{{ $title ?? 'Group Edit' }}
</h1>
<a href="{{ url('/admin/group-list') }}" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200">
<i class="fa fa-arrow-left mr-2"></i>Back to Groups
</a>
</div>
</div>
<!-- Error Message -->
@if(isset($error) && $error != '')
<div class="mx-6 mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
<div class="flex">
<i class="fa fa-exclamation-circle text-red-500 mr-3"></i>
<p class="text-red-800">{{ $error }}</p>
</div>
</div>
@endif
<!-- Form -->
<form action="{{ url('/admin/group-edit?action=submit') }}" method="POST" id="groupForm" class="px-6 py-6">
@csrf
<input type="hidden" name="id" value="{{ $group['id'] ?? '' }}"/>
<!-- Group Name -->
<div class="mb-6">
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Group Name:
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-users text-gray-400"></i>
</div>
<input type="text"
id="name"
name="name"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $group['name'] ?? '' }}"/>
</div>
<p class="mt-2 text-sm text-gray-500">
Changing the name to an invalid group will break things.
</p>
</div>
<!-- Description -->
<div class="mb-6">
<label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Description:
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-align-left text-gray-400"></i>
</div>
<textarea id="description"
name="description"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
rows="3">{{ $group['description'] ?? '' }}</textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Brief explanation of this group's content
</p>
</div>
<!-- Backfill Days -->
<div class="mb-6">
<label for="backfill_target" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Backfill Days:
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-calendar text-gray-400"></i>
</div>
<input type="number"
id="backfill_target"
name="backfill_target"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $group['backfill_target'] ?? 0 }}"/>
</div>
<p class="mt-2 text-sm text-gray-500">
Number of days to attempt to backfill this group. Adjust as necessary.
</p>
</div>
<!-- Minimum Files -->
<div class="mb-6">
<label for="minfilestoformrelease" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Minimum Files To Form Release:
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-file text-gray-400"></i>
</div>
<input type="number"
id="minfilestoformrelease"
name="minfilestoformrelease"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $group['minfilestoformrelease'] ?? 0 }}"/>
</div>
<p class="mt-2 text-sm text-gray-500">
The minimum number of files to make a release. If left blank, will use the site wide setting.
</p>
</div>
<!-- Minimum Size -->
<div class="mb-6">
<label for="minsizetoformrelease" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Minimum File Size (bytes):
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-download text-gray-400"></i>
</div>
<input type="number"
id="minsizetoformrelease"
name="minsizetoformrelease"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $group['minsizetoformrelease'] ?? 0 }}"/>
</div>
<p class="mt-2 text-sm text-gray-500">
The minimum total size in bytes to make a release. If left blank, will use the site wide setting.
</p>
</div>
<!-- First Record -->
<div class="mb-6">
<label for="first_record" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
First Record ID:
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-angle-double-left text-gray-400"></i>
</div>
<input type="number"
id="first_record"
name="first_record"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $group['first_record'] ?? 0 }}"/>
</div>
<p class="mt-2 text-sm text-gray-500">
The oldest record number for the group.
</p>
</div>
<!-- Last Record -->
<div class="mb-6">
<label for="last_record" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Last Record ID:
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-angle-double-right text-gray-400"></i>
</div>
<input type="number"
id="last_record"
name="last_record"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $group['last_record'] ?? 0 }}"/>
</div>
<p class="mt-2 text-sm text-gray-500">
The newest record number for the group.
</p>
</div>
<!-- Active Status -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Active:</label>
<div class="flex items-center gap-6">
<div class="flex items-center">
<input type="radio"
id="active_1"
name="active"
value="1"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ (isset($group['active']) && $group['active'] == 1) ? 'checked' : '' }}>
<label for="active_1" class="ml-2 text-sm text-gray-700">Yes</label>
</div>
<div class="flex items-center">
<input type="radio"
id="active_0"
name="active"
value="0"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ (isset($group['active']) && $group['active'] == 0) ? 'checked' : '' }}>
<label for="active_0" class="ml-2 text-sm text-gray-700">No</label>
</div>
</div>
<p class="mt-2 text-sm text-gray-500">
Inactive groups will not have headers downloaded for them.
</p>
</div>
<!-- Backfill Status -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Backfill:</label>
<div class="flex items-center gap-6">
<div class="flex items-center">
<input type="radio"
id="backfill_1"
name="backfill"
value="1"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ (isset($group['backfill']) && $group['backfill'] == 1) ? 'checked' : '' }}>
<label for="backfill_1" class="ml-2 text-sm text-gray-700">Yes</label>
</div>
<div class="flex items-center">
<input type="radio"
id="backfill_0"
name="backfill"
value="0"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ (isset($group['backfill']) && $group['backfill'] == 0) ? 'checked' : '' }}>
<label for="backfill_0" class="ml-2 text-sm text-gray-700">No</label>
</div>
</div>
<p class="mt-2 text-sm text-gray-500">
If set to No, backfill will ignore this group. This works even if the above setting is No.
</p>
</div>
</form>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between">
<button type="button"
onclick="window.location='{{ url('/admin/group-list') }}'"
class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Cancel
</button>
<button type="submit"
form="groupForm"
class="px-4 py-2 bg-green-600 dark:bg-green-700 text-white rounded-lg hover:bg-green-700">
<i class="fa fa-save mr-2"></i>Save Changes
</button>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('groupForm');
form.addEventListener('submit', function(event) {
const firstRecord = document.getElementById('first_record').value;
const lastRecord = document.getElementById('last_record').value;
// Validate record IDs relationship
if (parseInt(firstRecord) > parseInt(lastRecord) && parseInt(lastRecord) > 0) {
event.preventDefault();
alert('First record ID cannot be greater than last record ID');
return false;
}
});
});
</script>
@endpush
@endsection
@@ -0,0 +1,474 @@
@extends('layouts.admin')
@section('content')
<div class="max-w-full px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex flex-wrap justify-between items-center gap-3">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-users mr-2"></i>{{ $title ?? 'Group List' }}
</h1>
<div class="flex flex-wrap gap-2">
<a href="{{ url('/admin/group-list-active') }}" class="px-3 py-2 bg-blue-600 dark:bg-blue-700 text-white text-sm rounded-lg hover:bg-blue-700">
<i class="fa fa-check-circle mr-1"></i>Active Groups
</a>
<a href="{{ url('/admin/group-list-inactive') }}" class="px-3 py-2 bg-gray-600 text-white text-sm rounded-lg hover:bg-gray-700">
<i class="fa fa-times-circle mr-1"></i>Inactive Groups
</a>
<a href="{{ url('/admin/group-list') }}" class="px-3 py-2 bg-indigo-600 text-white text-sm rounded-lg hover:bg-indigo-700">
<i class="fa fa-list mr-1"></i>All Groups
</a>
<a href="{{ url('/admin/group-bulk') }}" class="px-3 py-2 bg-green-600 dark:bg-green-700 text-white text-sm rounded-lg hover:bg-green-700">
<i class="fa fa-plus-circle mr-1"></i>Bulk Add
</a>
</div>
</div>
</div>
<!-- Info Alert -->
<div class="px-6 py-4 bg-blue-50 border-b border-blue-100">
<div class="flex">
<i class="fa fa-info-circle text-blue-500 text-xl mr-3"></i>
<p class="text-sm text-blue-700">
Below is a list of all usenet groups available to be indexed. Click 'Activate' to start indexing a group.
Backfill works independently of active.
</p>
</div>
</div>
@if(isset($msg) && $msg != '')
<div class="mx-6 mt-4 p-4 bg-green-50 border border-green-200 rounded-lg" id="message">
<p class="text-green-800">{{ $msg }}</p>
</div>
@endif
@if($grouplist && $grouplist->count() > 0)
<!-- Search and Actions Bar -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
<!-- Search Form -->
<div>
<form name="groupsearch" method="GET">
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-search text-gray-400"></i>
</div>
<input id="groupname"
type="text"
name="groupname"
value="{{ $groupname ?? '' }}"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Search for group...">
</div>
<button type="submit" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
Go
</button>
</div>
</form>
</div>
<!-- Pagination -->
<div class="flex justify-center items-center">
{{ $grouplist->onEachSide(5)->links() }}
</div>
<!-- Bulk Actions -->
<div class="flex justify-end items-center">
<div class="flex gap-2">
<button type="button"
onclick="showResetAllModal()"
class="px-3 py-2 bg-yellow-600 text-white text-sm rounded-lg hover:bg-yellow-700">
<i class="fa fa-refresh mr-1"></i> Reset All
</button>
<button type="button"
onclick="showPurgeAllModal()"
class="px-3 py-2 bg-red-600 dark:bg-red-700 text-white text-sm rounded-lg hover:bg-red-700">
<i class="fa fa-trash mr-1"></i> Purge All
</button>
</div>
</div>
</div>
</div>
<!-- Groups Table -->
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Group</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">First Post</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Post</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Updated</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-32">Status</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-32">Backfill</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Releases</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Min Files</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Min Size</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-32">Backfill Days</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-40">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($grouplist as $group)
<tr id="grouprow-{{ $group->id }}" class="hover:bg-gray-50">
<td class="px-6 py-4">
<a href="{{ url('/admin/group-edit?id=' . $group->id) }}" class="font-semibold text-blue-600 dark:text-blue-400 hover:text-blue-800">
{{ str_replace('alt.binaries', 'a.b', $group->name) }}
</a>
@if($group->description)
<div class="text-sm text-gray-500">{{ $group->description }}</div>
@endif
</td>
<td class="px-6 py-4 text-sm">
<div class="flex flex-col">
<span class="text-gray-900">{{ $group->first_record_postdate }}</span>
<small class="text-gray-500">{{ \Carbon\Carbon::parse($group->first_record_postdate)->diffForHumans() }}</small>
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-900">{{ $group->last_record_postdate }}</td>
<td class="px-6 py-4 text-sm text-gray-500" title="{{ $group->last_updated }}">
{{ \Carbon\Carbon::parse($group->last_updated)->diffForHumans() }}
</td>
<td class="px-6 py-4 text-center" id="group-{{ $group->id }}">
@if($group->active == 1)
<button type="button"
onclick="ajax_group_status({{ $group->id }}, 0)"
class="inline-flex items-center px-3 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800 hover:bg-green-200">
<i class="fa fa-check-circle mr-1"></i>Active
</button>
@else
<button type="button"
onclick="ajax_group_status({{ $group->id }}, 1)"
class="inline-flex items-center px-3 py-1 text-xs font-semibold rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 hover:bg-gray-200">
<i class="fa fa-times-circle mr-1"></i>Inactive
</button>
@endif
</td>
<td class="px-6 py-4 text-center" id="backfill-{{ $group->id }}">
@if($group->backfill == 1)
<button type="button"
onclick="ajax_backfill_status({{ $group->id }}, 0)"
class="inline-flex items-center px-3 py-1 text-xs font-semibold rounded-full bg-blue-100 text-blue-800 hover:bg-blue-200">
<i class="fa fa-check-circle mr-1"></i>Enabled
</button>
@else
<button type="button"
onclick="ajax_backfill_status({{ $group->id }}, 1)"
class="inline-flex items-center px-3 py-1 text-xs font-semibold rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800 dark:text-gray-200 hover:bg-gray-200">
<i class="fa fa-times-circle mr-1"></i>Disabled
</button>
@endif
</td>
<td class="px-6 py-4 text-center">
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800">
{{ $group->num_releases ?? 0 }}
</span>
</td>
<td class="px-6 py-4 text-center">
@if(empty($group->minfilestoformrelease))
<span class="text-gray-400">n/a</span>
@else
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800">
{{ $group->minfilestoformrelease }}
</span>
@endif
</td>
<td class="px-6 py-4 text-center">
@if(empty($group->minsizetoformrelease))
<span class="text-gray-400">n/a</span>
@else
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800">
{{ human_filesize($group->minsizetoformrelease) }}
</span>
@endif
</td>
<td class="px-6 py-4 text-center">
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800">
{{ $group->backfill_target }}
</span>
</td>
<td class="px-6 py-4 text-center" id="groupdel-{{ $group->id }}">
<div class="flex gap-1 justify-center">
<a href="{{ url('/admin/group-edit?id=' . $group->id) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900"
title="Edit this group">
<i class="fa fa-pencil"></i>
</a>
<button type="button"
onclick="ajax_group_reset({{ $group->id }})"
class="text-yellow-600 hover:text-yellow-900"
title="Reset this group">
<i class="fa fa-refresh"></i>
</button>
<button type="button"
onclick="confirmGroupDelete({{ $group->id }})"
class="text-red-600 hover:text-red-900"
title="Delete this group">
<i class="fa fa-trash"></i>
</button>
<button type="button"
onclick="confirmGroupPurge({{ $group->id }})"
class="text-red-600 hover:text-red-900"
title="Purge this group">
<i class="fa fa-eraser"></i>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">
Showing {{ $grouplist->count() }} of {{ $grouplist->total() }} groups
</span>
<div>
{{ $grouplist->onEachSide(5)->links() }}
</div>
</div>
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-exclamation-triangle text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No groups available</h3>
<p class="text-gray-500 mb-4">No groups have been added yet.</p>
<a href="{{ url('/admin/group-bulk') }}" class="inline-flex items-center px-4 py-2 bg-green-600 dark:bg-green-700 text-white rounded-lg hover:bg-green-700">
<i class="fa fa-plus-circle mr-2"></i>Add Groups
</a>
</div>
@endif
</div>
</div>
<!-- Reset All Modal -->
<div id="resetAllModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div class="mt-3">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Confirm Reset All Groups</h3>
<p class="text-sm text-red-600 mb-2">
<i class="fa fa-exclamation-triangle mr-2"></i>Are you sure you want to reset all groups?
</p>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
This will reset the article pointers for all groups back to their current state.
</p>
<div class="flex justify-end gap-3">
<button type="button"
onclick="hideResetAllModal()"
class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
Cancel
</button>
<button type="button"
onclick="ajax_group_reset_all()"
class="px-4 py-2 bg-yellow-600 text-white rounded-lg hover:bg-yellow-700">
Reset All
</button>
</div>
</div>
</div>
</div>
<!-- Purge All Modal -->
<div id="purgeAllModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
<div class="mt-3">
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-4">Confirm Purge All Groups</h3>
<p class="text-sm text-red-600 mb-2">
<i class="fa fa-exclamation-triangle mr-2"></i>Are you sure you want to purge all groups?
</p>
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
This will delete all releases and binaries for all groups. This action cannot be undone!
</p>
<div class="flex justify-end gap-3">
<button type="button"
onclick="hidePurgeAllModal()"
class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
Cancel
</button>
<button type="button"
onclick="ajax_group_purge_all()"
class="px-4 py-2 bg-red-600 dark:bg-red-700 text-white rounded-lg hover:bg-red-700">
Purge All
</button>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
function showResetAllModal() {
document.getElementById('resetAllModal').classList.remove('hidden');
}
function hideResetAllModal() {
document.getElementById('resetAllModal').classList.add('hidden');
}
function showPurgeAllModal() {
document.getElementById('purgeAllModal').classList.remove('hidden');
}
function hidePurgeAllModal() {
document.getElementById('purgeAllModal').classList.add('hidden');
}
function ajax_group_status(id, status) {
fetch('{{ url('/admin/ajax') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
action: 'toggle_group_active_status',
group_id: id,
group_status: status
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('group-' + id).innerHTML = data.html;
}
});
}
function ajax_backfill_status(id, status) {
fetch('{{ url('/admin/ajax') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
action: 'toggle_group_backfill',
group_id: id,
backfill: status
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('backfill-' + id).innerHTML = data.html;
}
});
}
function ajax_group_reset(id) {
if (confirm('Are you sure you want to reset this group?')) {
fetch('{{ url('/admin/ajax') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
action: 'reset_group',
group_id: id
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
});
}
}
function confirmGroupDelete(id) {
if (confirm('Are you sure you want to delete this group?')) {
fetch('{{ url('/admin/ajax') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
action: 'delete_group',
group_id: id
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
const row = document.getElementById('grouprow-' + id);
row.style.transition = 'opacity 0.3s';
row.style.opacity = '0';
setTimeout(() => row.remove(), 300);
}
});
}
}
function confirmGroupPurge(id) {
if (confirm('Are you sure you want to purge this group? This will delete all releases and binaries!')) {
fetch('{{ url('/admin/ajax') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
action: 'purge_group',
group_id: id
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
location.reload();
}
});
}
}
function ajax_group_reset_all() {
fetch('{{ url('/admin/ajax') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
action: 'reset_all_groups'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
hideResetAllModal();
location.reload();
}
});
}
function ajax_group_purge_all() {
fetch('{{ url('/admin/ajax') }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify({
action: 'purge_all_groups'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
hidePurgeAllModal();
location.reload();
}
});
}
</script>
@endpush
@endsection
+104
View File
@@ -0,0 +1,104 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-film mr-2"></i>{{ $title }}
</h1>
</div>
<!-- Success/Error/Warning Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg">
<p class="text-red-800 dark:text-red-200">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
@if(session('warning'))
<div class="mx-6 mt-4 p-4 bg-yellow-50 dark:bg-yellow-900 border border-yellow-200 dark:border-yellow-700 rounded-lg">
<p class="text-yellow-800 dark:text-yellow-200">
<i class="fa fa-exclamation-triangle mr-2"></i>{{ session('warning') }}
</p>
</div>
@endif
<!-- Info Alert -->
<div class="px-6 py-4 bg-blue-50 dark:bg-blue-900 border-b border-blue-100">
<div class="flex">
<i class="fa fa-info-circle text-blue-500 text-xl mr-3"></i>
<div class="text-sm text-blue-700 dark:text-blue-300">
<p class="mb-2">
Enter an IMDB ID (numeric only, without the 'tt' prefix) to add movie information to the database.
</p>
<p class="text-xs text-blue-600 dark:text-blue-400">
Example: For <code class="px-1 bg-blue-100 dark:bg-blue-800 rounded">https://www.imdb.com/title/tt0111161/</code>, enter <code class="px-1 bg-blue-100 dark:bg-blue-800 rounded">0111161</code>
</p>
</div>
</div>
</div>
<!-- Add Movie Form -->
<form method="GET" action="{{ url('admin/movie-add') }}" class="p-6">
<div class="max-w-2xl">
<div class="mb-6">
<label for="id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
IMDB ID <span class="text-red-500">*</span>
</label>
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span class="text-gray-500 dark:text-gray-400 font-mono">tt</span>
</div>
<input type="text"
id="id"
name="id"
required
pattern="[0-9]+"
placeholder="0111161"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white"
value="">
</div>
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700 whitespace-nowrap">
<i class="fa fa-plus mr-2"></i>Add Movie
</button>
</div>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Enter only the numeric part of the IMDB ID (without 'tt' prefix)
</p>
</div>
<div class="mt-6 p-4 bg-gray-50 dark:bg-gray-700 rounded-lg">
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-2">
<i class="fa fa-lightbulb-o mr-1"></i>How it works:
</h3>
<ul class="text-sm text-gray-600 dark:text-gray-400 space-y-1 list-disc list-inside">
<li>The system will fetch movie information from TMDB (The Movie Database)</li>
<li>Movie details, posters, and backdrops will be automatically downloaded</li>
<li>Associated releases will be linked to the movie information</li>
</ul>
</div>
<div class="mt-6 flex gap-3">
<a href="{{ url('admin/movie-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
<i class="fa fa-arrow-left mr-2"></i>Back to Movie List
</a>
</div>
</div>
</form>
</div>
</div>
@endsection
+392
View File
@@ -0,0 +1,392 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-edit mr-2"></i>{{ $title }}
</h1>
</div>
<!-- Success/Error/Warning Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg">
<p class="text-red-800 dark:text-red-200">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
@if(session('warning'))
<div class="mx-6 mt-4 p-4 bg-yellow-50 dark:bg-yellow-900 border border-yellow-200 dark:border-yellow-700 rounded-lg">
<p class="text-yellow-800 dark:text-yellow-200">
<i class="fa fa-exclamation-triangle mr-2"></i>{{ session('warning') }}
</p>
</div>
@endif
<!-- Edit Movie Form -->
<form method="POST" action="{{ url('admin/movie-edit') }}" enctype="multipart/form-data" class="p-6">
@csrf
<input type="hidden" name="action" value="submit">
<input type="hidden" name="id" value="{{ $movie['imdbid'] ?? $movie->imdbid ?? '' }}">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Left Column - Movie Images -->
<div class="space-y-6">
<!-- Cover Image -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Cover Image
</label>
<div class="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-4">
@php
$imdbid = $movie['imdbid'] ?? $movie->imdbid ?? '';
$coverPath = public_path('covers/movies/' . $imdbid . '-cover.jpg');
$hasCover = file_exists($coverPath);
@endphp
@if($hasCover)
<img src="{{ asset('covers/movies/' . $imdbid . '-cover.jpg') }}"
alt="Movie Cover"
class="w-full h-auto rounded-lg mb-3">
@else
<div class="flex flex-col items-center justify-center py-8 text-gray-400">
<i class="fa fa-image text-5xl mb-2"></i>
<p class="text-sm">No cover image</p>
</div>
@endif
<input type="file"
name="cover"
accept="image/*"
class="w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
<p class="mt-2 text-xs text-gray-500">Upload a new cover image (JPG/PNG)</p>
</div>
</div>
<!-- Backdrop Image -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Backdrop Image
</label>
<div class="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-4">
@php
$backdropPath = public_path('covers/movies/' . $imdbid . '-backdrop.jpg');
$hasBackdrop = file_exists($backdropPath);
@endphp
@if($hasBackdrop)
<img src="{{ asset('covers/movies/' . $imdbid . '-backdrop.jpg') }}"
alt="Movie Backdrop"
class="w-full h-auto rounded-lg mb-3">
@else
<div class="flex flex-col items-center justify-center py-8 text-gray-400">
<i class="fa fa-image text-5xl mb-2"></i>
<p class="text-sm">No backdrop image</p>
</div>
@endif
<input type="file"
name="backdrop"
accept="image/*"
class="w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100">
<p class="mt-2 text-xs text-gray-500">Upload a new backdrop image (JPG/PNG)</p>
</div>
</div>
</div>
<!-- Right Column - Movie Details -->
<div class="lg:col-span-2 space-y-6">
<!-- IMDB ID (Read-only) -->
<div>
<label for="imdbid" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
IMDB ID
</label>
<div class="flex gap-2">
<input type="text"
id="imdbid"
value="{{ $movie['imdbid'] ?? $movie->imdbid ?? '' }}"
readonly
class="flex-1 px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-gray-100 dark:bg-gray-700 dark:text-gray-300">
<a href="https://www.imdb.com/title/tt{{ $movie['imdbid'] ?? $movie->imdbid ?? '' }}"
target="_blank"
class="px-4 py-2 bg-yellow-500 text-white rounded-md hover:bg-yellow-600">
<i class="fa fa-external-link"></i> View on IMDB
</a>
</div>
</div>
<!-- Title -->
<div>
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Title <span class="text-red-500">*</span>
</label>
<input type="text"
id="title"
name="title"
value="{{ $movie['title'] ?? $movie->title ?? '' }}"
required
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 dark:bg-gray-700 dark:text-white">
</div>
<!-- Tagline -->
<div>
<label for="tagline" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Tagline
</label>
<input type="text"
id="tagline"
name="tagline"
value="{{ $movie['tagline'] ?? $movie->tagline ?? '' }}"
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 dark:bg-gray-700 dark:text-white">
</div>
<!-- Year and Rating -->
<div class="grid grid-cols-2 gap-4">
<div>
<label for="year" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Year
</label>
<input type="number"
id="year"
name="year"
value="{{ $movie['year'] ?? $movie->year ?? '' }}"
min="1800"
max="2100"
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 dark:bg-gray-700 dark:text-white">
</div>
<div>
<label for="rating" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Rating
</label>
<input type="text"
id="rating"
name="rating"
value="{{ $movie['rating'] ?? $movie->rating ?? '' }}"
placeholder="e.g., 8.5"
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 dark:bg-gray-700 dark:text-white">
</div>
</div>
<!-- Genre -->
<div>
<label for="genre" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Genre
</label>
<input type="text"
id="genre"
name="genre"
value="{{ $movie['genre'] ?? $movie->genre ?? '' }}"
placeholder="e.g., Action, Drama, Comedy"
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 dark:bg-gray-700 dark:text-white">
</div>
<!-- Director -->
<div>
<label for="director" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Director
</label>
<input type="text"
id="director"
name="director"
value="{{ $movie['director'] ?? $movie->director ?? '' }}"
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 dark:bg-gray-700 dark:text-white">
</div>
<!-- Actors -->
<div>
<label for="actors" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Actors
</label>
<input type="text"
id="actors"
name="actors"
value="{{ $movie['actors'] ?? $movie->actors ?? '' }}"
placeholder="Comma-separated list of actors"
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 dark:bg-gray-700 dark:text-white">
</div>
<!-- Language -->
<div>
<label for="language" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Language
</label>
<input type="text"
id="language"
name="language"
value="{{ $movie['language'] ?? $movie->language ?? '' }}"
placeholder="e.g., English"
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 dark:bg-gray-700 dark:text-white">
</div>
<!-- Plot -->
<div>
<label for="plot" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Plot
</label>
<textarea id="plot"
name="plot"
rows="6"
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 dark:bg-gray-700 dark:text-white">{{ $movie['plot'] ?? $movie->plot ?? '' }}</textarea>
</div>
<!-- Action Buttons -->
<div class="flex gap-3 pt-4 border-t border-gray-200">
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-save mr-2"></i>Save Changes
</button>
<a href="{{ url('admin/movie-list') }}" class="px-6 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
<i class="fa fa-times mr-2"></i>Cancel
</a>
<a href="{{ url('admin/movie-edit?id=' . ($movie['imdbid'] ?? $movie->imdbid ?? '') . '&update=1') }}"
class="ml-auto px-6 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700"
onclick="return confirm('This will fetch and update movie data from TMDB. Continue?')">
<i class="fa fa-refresh mr-2"></i>Update from TMDB
</a>
</div>
</div>
</div>
</form>
</div>
</div>
@endsection
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-film mr-2"></i>{{ $title }}
</h1>
<a href="{{ url('admin/movie-add') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add Movie
</a>
</div>
</div>
<!-- Search Form -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<form method="GET" action="{{ url('admin/movie-list') }}">
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-search text-gray-400"></i>
</div>
<input type="text"
name="moviesearch"
value="{{ $lastSearch ?? '' }}"
placeholder="Search by IMDB ID or movie title..."
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<button type="submit" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
Search
</button>
@if(!empty($lastSearch))
<a href="{{ url('admin/movie-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
Clear
</a>
@endif
</div>
</form>
</div>
<!-- Movie List Table -->
@if(!empty($movielist) && count($movielist) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">IMDB ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Title</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Year</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Rating</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Genre</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Cover</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Backdrop</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($movielist as $movie)
<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">
<a href="https://www.imdb.com/title/tt{{ $movie->imdbid }}" target="_blank" class="text-blue-600 dark:text-blue-400 hover:underline">
{{ $movie->imdbid }}
</a>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900 dark:text-gray-200">{{ $movie->title ?? 'N/A' }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{{ $movie->year ?? 'N/A' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{{ $movie->rating ?? 'N/A' }}
</td>
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{{ \Illuminate\Support\Str::limit($movie->genre ?? 'N/A', 30) }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
@if($movie->cover == 1)
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check"></i> Yes
</span>
@else
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times"></i> No
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
@if($movie->backdrop == 1)
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check"></i> Yes
</span>
@else
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times"></i> No
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<a href="{{ url('admin/movie-edit?id=' . $movie->imdbid) }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-900 mr-3">
<i class="fa fa-edit"></i> Edit
</a>
<a href="{{ url('admin/movie-edit?id=' . $movie->imdbid . '&update=1') }}" class="text-green-600 dark:text-green-400 hover:text-green-900" title="Update from TMDB">
<i class="fa fa-refresh"></i> Update
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-film text-gray-400 text-5xl mb-4"></i>
<p class="text-gray-500 dark:text-gray-400 text-lg">
@if(!empty($lastSearch))
No movies found matching "{{ $lastSearch }}".
@else
No movies found in the database.
@endif
</p>
<a href="{{ url('admin/movie-add') }}" class="mt-4 inline-block px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add Your First Movie
</a>
</div>
@endif
</div>
</div>
@endsection
@@ -0,0 +1,158 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-film mr-2"></i>{{ $title }}
</h1>
<a href="{{ url('admin/movie-add') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add Movie
</a>
</div>
</div>
<!-- Success/Error/Warning Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg">
<p class="text-red-800 dark:text-red-200">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
@if(session('warning'))
<div class="mx-6 mt-4 p-4 bg-yellow-50 dark:bg-yellow-900 border border-yellow-200 dark:border-yellow-700 rounded-lg">
<p class="text-yellow-800 dark:text-yellow-200">
<i class="fa fa-exclamation-triangle mr-2"></i>{{ session('warning') }}
</p>
</div>
@endif
<!-- Search Form -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<form method="GET" action="{{ url('admin/movie-list') }}">
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-search text-gray-400"></i>
</div>
<input type="text"
name="moviesearch"
value="{{ $lastSearch ?? '' }}"
placeholder="Search by IMDB ID or movie title..."
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<button type="submit" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
Search
</button>
@if(!empty($lastSearch))
<a href="{{ url('admin/movie-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
Clear
</a>
@endif
</div>
</form>
</div>
<!-- Movie List Table -->
@if(!empty($movielist) && count($movielist) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">IMDB ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Title</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Year</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Rating</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Genre</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Cover</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Backdrop</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($movielist as $movie)
<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">
<a href="https://www.imdb.com/title/tt{{ $movie->imdbid }}" target="_blank" class="text-blue-600 dark:text-blue-400 hover:underline">
{{ $movie->imdbid }}
</a>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900 dark:text-gray-200">{{ $movie->title ?? 'N/A' }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{{ $movie->year ?? 'N/A' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{{ $movie->rating ?? 'N/A' }}
</td>
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{{ \Illuminate\Support\Str::limit($movie->genre ?? 'N/A', 30) }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
@if($movie->cover == 1)
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check"></i> Yes
</span>
@else
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times"></i> No
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
@if($movie->backdrop == 1)
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check"></i> Yes
</span>
@else
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times"></i> No
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<a href="{{ url('admin/movie-edit?id=' . $movie->imdbid) }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-900 mr-3">
<i class="fa fa-edit"></i> Edit
</a>
<a href="{{ url('admin/movie-edit?id=' . $movie->imdbid . '&update=1') }}" class="text-green-600 dark:text-green-400 hover:text-green-900" title="Update from TMDB">
<i class="fa fa-refresh"></i> Update
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-film text-gray-400 text-5xl mb-4"></i>
<p class="text-gray-500 dark:text-gray-400 text-lg">
@if(!empty($lastSearch))
No movies found matching "{{ $lastSearch }}".
@else
No movies found in the database.
@endif
</p>
<a href="{{ url('admin/movie-add') }}" class="mt-4 inline-block px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add Your First Movie
</a>
</div>
@endif
</div>
</div>
@endsection
+236
View File
@@ -0,0 +1,236 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-music mr-2"></i>{{ $title }}
</h1>
</div>
<!-- Success/Error/Warning Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg">
<p class="text-red-800 dark:text-red-200">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
@if(session('warning'))
<div class="mx-6 mt-4 p-4 bg-yellow-50 dark:bg-yellow-900 border border-yellow-200 dark:border-yellow-700 rounded-lg">
<p class="text-yellow-800 dark:text-yellow-200">
<i class="fa fa-exclamation-triangle mr-2"></i>{{ session('warning') }}
</p>
</div>
@endif
<!-- Edit Music Form -->
<form method="POST" action="{{ url('admin/music-edit') }}" enctype="multipart/form-data" class="p-6">
@csrf
<input type="hidden" name="id" value="{{ $mus['id'] }}">
<input type="hidden" name="action" value="submit">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Left Column -->
<div class="space-y-6">
<!-- Title -->
<div>
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Album Title <span class="text-red-500">*</span>
</label>
<input type="text"
id="title"
name="title"
value="{{ $mus['title'] ?? '' }}"
required
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- Artist -->
<div>
<label for="artist" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Artist
</label>
<input type="text"
id="artist"
name="artist"
value="{{ $mus['artist'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- Publisher -->
<div>
<label for="publisher" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Publisher/Label
</label>
<input type="text"
id="publisher"
name="publisher"
value="{{ $mus['publisher'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- ASIN -->
<div>
<label for="asin" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
ASIN
</label>
<input type="text"
id="asin"
name="asin"
value="{{ $mus['asin'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- URL -->
<div>
<label for="url" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
URL
</label>
<input type="url"
id="url"
name="url"
value="{{ $mus['url'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
</div>
<!-- Right Column -->
<div class="space-y-6">
<!-- Genre -->
<div>
<label for="genre" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Genre
</label>
<select id="genre"
name="genre"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
<option value="">-- Select Genre --</option>
@if(!empty($genres))
@foreach($genres as $genreItem)
<option value="{{ $genreItem['id'] }}"
{{ (isset($mus['genre_id']) && $mus['genre_id'] == $genreItem['id']) ? 'selected' : '' }}>
{{ $genreItem['title'] }}
</option>
@endforeach
@endif
</select>
</div>
<!-- Year -->
<div>
<label for="year" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Year
</label>
<input type="number"
id="year"
name="year"
min="1900"
max="2100"
value="{{ $mus['year'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- Release Date -->
<div>
<label for="releasedate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Release Date
</label>
<input type="date"
id="releasedate"
name="releasedate"
value="{{ !empty($mus['releasedate']) ? date('Y-m-d', $mus['releasedate']) : '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- Tracks -->
<div>
<label for="tracks" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Number of Tracks
</label>
<input type="number"
id="tracks"
name="tracks"
min="0"
value="{{ $mus['tracks'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- Sales Rank -->
<div>
<label for="salesrank" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Sales Rank
</label>
<input type="number"
id="salesrank"
name="salesrank"
min="0"
value="{{ $mus['salesrank'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<!-- Cover Image -->
<div>
<label for="cover" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Cover Image
</label>
@if(!empty($mus['cover']) && $mus['cover'] == 1)
<div class="mb-3">
<img src="{{ asset('storage/covers/music/' . $mus['id'] . '.jpg') }}"
alt="Album Cover"
class="max-w-xs rounded-lg shadow-md border border-gray-300 dark:border-gray-600">
</div>
@endif
<input type="file"
id="cover"
name="cover"
accept="image/jpeg,image/jpg"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
Upload a new cover image (JPG format)
</p>
</div>
<!-- Current Cover Status -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Current Cover Status
</label>
@if(!empty($mus['cover']) && $mus['cover'] == 1)
<span class="inline-flex items-center px-3 py-1 text-sm font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check mr-2"></i> Cover Available
</span>
@else
<span class="inline-flex items-center px-3 py-1 text-sm font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times mr-2"></i> No Cover
</span>
@endif
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="mt-8 flex gap-3 border-t border-gray-200 pt-6">
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-save mr-2"></i>Save Changes
</button>
<a href="{{ route('admin.music-list') }}" class="px-6 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
<i class="fa fa-arrow-left mr-2"></i>Back to Music List
</a>
</div>
</form>
</div>
</div>
@endsection
+145
View File
@@ -0,0 +1,145 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800 dark:text-gray-200">
<i class="fa fa-music mr-2"></i>{{ $title }}
</h1>
</div>
</div>
<!-- Success/Error/Warning Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg">
<p class="text-red-800 dark:text-red-200">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
@if(session('warning'))
<div class="mx-6 mt-4 p-4 bg-yellow-50 dark:bg-yellow-900 border border-yellow-200 dark:border-yellow-700 rounded-lg">
<p class="text-yellow-800 dark:text-yellow-200">
<i class="fa fa-exclamation-triangle mr-2"></i>{{ session('warning') }}
</p>
</div>
@endif
<!-- Search Form -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<form method="GET" action="{{ url('admin/music-list') }}">
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-search text-gray-400"></i>
</div>
<input type="text"
name="musicsearch"
value="{{ $lastSearch ?? '' }}"
placeholder="Search by artist, album title..."
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<button type="submit" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
Search
</button>
@if(!empty($lastSearch))
<a href="{{ url('admin/music-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
Clear
</a>
@endif
</div>
</form>
</div>
<!-- Music List Table -->
@if(!empty($musicList) && count($musicList) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Title</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Artist</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Publisher</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Year</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Genre</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Tracks</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Cover</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@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">
{{ $music['id'] ?? 'N/A' }}
</td>
<td class="px-6 py-4">
<div class="text-sm font-medium text-gray-900 dark:text-gray-200">
{{ $music['title'] ?? 'N/A' }}
</div>
</td>
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{{ $music['artist'] ?? 'N/A' }}
</td>
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{{ $music['publisher'] ?? 'N/A' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{{ $music['year'] ?? 'N/A' }}
</td>
<td class="px-6 py-4 text-sm text-gray-500 dark:text-gray-400">
{{ \Illuminate\Support\Str::limit($music['genre'] ?? 'N/A', 30) }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
{{ $music['tracks'] ?? 'N/A' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
@if(!empty($music['cover']) && $music['cover'] == 1)
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check"></i> Yes
</span>
@else
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times"></i> No
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<a href="{{ url('admin/music-edit?id=' . $music['id']) }}" class="text-blue-600 dark:text-blue-400 hover:text-blue-900">
<i class="fa fa-edit"></i> Edit
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-music text-gray-400 text-5xl mb-4"></i>
<p class="text-gray-500 dark:text-gray-400 text-lg">
@if(!empty($lastSearch))
No music found matching "{{ $lastSearch }}".
@else
No music found in the database.
@endif
</p>
</div>
@endif
</div>
</div>
@endsection
@@ -0,0 +1,207 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-edit mr-2"></i>{{ $title ?? 'Category Regex Edit' }}
</h1>
<a href="{{ url('/admin/category_regexes-list') }}" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200">
<i class="fa fa-arrow-left mr-2"></i>Back to List
</a>
</div>
</div>
<!-- Form -->
<form action="{{ url('/admin/category_regexes-edit?action=submit') }}" method="POST" id="regexForm" class="px-6 py-6">
@csrf
<input type="hidden" name="id" value="{{ $regex->id ?? '' }}"/>
<!-- Error Message -->
@if($error)
<div class="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
<div class="flex">
<i class="fa fa-exclamation-triangle text-red-500 mr-3"></i>
<p class="text-red-800">{{ $error }}</p>
</div>
</div>
@endif
<!-- Group -->
<div class="mb-6">
<label for="group_regex" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Group: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-users text-gray-400"></i>
</div>
<input type="text"
id="group_regex"
name="group_regex"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ htmlspecialchars($regex->group_regex ?? '') }}"
required>
</div>
<p class="mt-2 text-sm text-gray-500">
Regex to match against a group or multiple groups. Delimiters are already added, and PCRE_CASELESS is added after for case insensitivity.<br>
Example of matching a single group: <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">alt\.binaries\.example</code><br>
Example of matching multiple groups: <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">alt\.binaries.*</code>
</p>
</div>
<!-- Regex -->
<div class="mb-6">
<label for="regex" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Regex: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-code text-gray-400"></i>
</div>
<textarea id="regex"
name="regex"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
rows="4"
required>{{ htmlspecialchars($regex->regex ?? '') }}</textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Regex to use when categorizing releases.<br>
The regex delimiters are not added, you MUST add them. See <a href="http://php.net/manual/en/regexp.reference.delimiters" target="_blank" class="text-blue-600 dark:text-blue-400 hover:text-blue-800">this</a> page.<br>
To make the regex case insensitive, add <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">i</code> after the last delimiter.
</p>
</div>
<!-- Description -->
<div class="mb-6">
<label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Description:
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-align-left text-gray-400"></i>
</div>
<textarea id="description"
name="description"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
rows="3">{{ htmlspecialchars($regex->description ?? '') }}</textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Description for this regex. You can include an example usenet subject this regex would match on.
</p>
</div>
<!-- Ordinal -->
<div class="mb-6">
<label for="ordinal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Ordinal: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-sort-numeric-asc text-gray-400"></i>
</div>
<input type="number"
id="ordinal"
name="ordinal"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $regex->ordinal ?? 0 }}"
min="0"
required>
</div>
<p class="mt-2 text-sm text-gray-500">
The order to run this regex in. Must be a number, 0 or higher.<br>
If multiple regex have the same ordinal, MySQL will randomly sort them.
</p>
</div>
<!-- Active Status -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Active:
</label>
<div class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
@foreach($status_ids as $k => $id)
<div class="flex items-center {{ $loop->last ? '' : 'mb-3' }}">
<input type="radio"
name="status"
id="status{{ $id }}"
value="{{ $id }}"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ ($regex->status ?? 1) == $id ? 'checked' : '' }}>
<label for="status{{ $id }}" class="ml-3 text-sm text-gray-700">
{{ $status_names[$k] }}
</label>
</div>
@endforeach
</div>
<p class="mt-2 text-sm text-gray-500">
Only active regex are used during the collection matching process.
</p>
</div>
<!-- Category -->
<div class="mb-6">
<label for="categories_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Category: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-folder-open text-gray-400"></i>
</div>
<select id="categories_id"
name="categories_id"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
required>
@foreach($category_ids as $k => $catId)
<option value="{{ $catId }}" {{ ($regex->categories_id ?? '') == $catId ? 'selected' : '' }}>
{{ $category_names[$k] }}
</option>
@endforeach
</select>
</div>
<p class="mt-2 text-sm text-gray-500">
Select a category which releases matched to this regex will go into.
</p>
</div>
</form>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between">
<a href="{{ url('/admin/category_regexes-list') }}" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Cancel
</a>
<button type="submit" form="regexForm" class="px-4 py-2 bg-green-600 dark:bg-green-700 text-white rounded-lg hover:bg-green-700">
<i class="fa fa-save mr-2"></i>Save Changes
</button>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('regexForm');
if (form) {
form.addEventListener('submit', function(event) {
const groupRegex = document.getElementById('group_regex');
const regex = document.getElementById('regex');
const ordinal = document.getElementById('ordinal');
if (!groupRegex.value.trim() || !regex.value.trim() || !ordinal.value) {
event.preventDefault();
alert('Please fill in all required fields.');
return false;
}
});
}
});
</script>
@endpush
@endsection
@@ -0,0 +1,235 @@
@extends('layouts.admin')
@section('content')
<div class="max-w-full px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-folder-open mr-2"></i>{{ $title ?? 'Category Regex List' }}
</h1>
<a href="{{ url('/admin/category-regexes-edit') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add New Regex
</a>
</div>
</div>
<!-- Info Alert -->
<div class="px-6 py-4 bg-blue-50 border-b border-blue-100">
<div class="flex">
<div class="flex-shrink-0">
<i class="fa fa-info-circle text-blue-500 text-2xl"></i>
</div>
<div class="ml-3">
<p class="text-sm text-blue-700">
This page lists regular expressions used for categorizing releases.<br>
You can recategorize all releases by running <code class="bg-blue-100 px-2 py-1 rounded text-xs">misc/update/update_releases 6 true</code>
</p>
</div>
</div>
</div>
<!-- Messages -->
<div id="message" class="px-6"></div>
<!-- Search Form -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<form name="groupsearch" action="" method="get" class="max-w-md">
@csrf
<label for="group" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Search by Group:</label>
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-search text-gray-400"></i>
</div>
<input id="group"
type="text"
name="group"
value="{{ $group }}"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Search a group...">
</div>
<button class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700" type="submit">
Search
</button>
</div>
</form>
</div>
<!-- Table -->
@if($regex && count($regex) > 0)
<!-- Pagination Top -->
@if(method_exists($regex, 'links'))
<div class="px-6 py-3 border-b border-gray-200">
{{ $regex->onEachSide(5)->links() }}
</div>
@endif
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-20">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Group</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Regex</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Ordinal</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Status</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-28">Category</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-28">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($regex as $row)
<tr id="row-{{ $row->id }}" class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-semibold text-gray-900">{{ $row->id }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<code class="bg-blue-50 text-blue-700 px-2 py-1 rounded text-xs">{{ $row->group_regex }}</code>
</td>
<td class="px-6 py-4 text-sm text-gray-500">
<span class="inline-block max-w-[200px] truncate" title="{{ $row->description }}">
{{ $row->description }}
</span>
</td>
<td class="px-6 py-4 text-sm">
<div class="max-w-[200px] break-words">
<code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs break-all" title="{{ htmlspecialchars($row->regex) }}">
{{ htmlspecialchars($row->regex) }}
</code>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-900">{{ $row->ordinal }}</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
@if($row->status == 1)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check-circle mr-1"></i>Active
</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times-circle mr-1"></i>Disabled
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800">
<i class="fa fa-folder-open mr-1"></i>{{ $row->categories_id }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-medium">
<div class="flex gap-2 justify-center">
<a href="{{ url('/admin/category_regexes-edit?id=' . $row->id) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900"
title="Edit this regex">
<i class="fa fa-edit"></i>
</a>
<button type="button"
class="text-red-600 hover:text-red-900"
onclick="confirmDelete({{ $row->id }})"
title="Delete this regex">
<i class="fa fa-trash"></i>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Pagination Bottom -->
@if(method_exists($regex, 'links'))
<div class="px-6 py-4 border-t border-gray-200">
{{ $regex->onEachSide(5)->links() }}
</div>
@endif
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-exclamation-triangle text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No regex patterns found</h3>
<p class="text-gray-500 mb-4">Try a different search term or add a new regex.</p>
<a href="{{ url('/admin/category-regexes-edit') }}" 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-plus mr-2"></i>Add New Regex
</a>
</div>
@endif
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">
@if($regex && method_exists($regex, 'total'))
Total entries: {{ $regex->total() }}
@elseif($regex)
Total entries: {{ count($regex) }}
@else
No entries
@endif
</span>
<a href="{{ url('/admin/category_regexes-edit') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-800 text-sm">
<i class="fa fa-plus mr-2"></i>Add New Regex
</a>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
function confirmDelete(id) {
if (confirm('Are you sure you want to delete this regex? This action cannot be undone.')) {
deleteRegex(id);
}
}
function deleteRegex(id) {
fetch('{{ url("/admin/category_regexes-delete") }}?id=' + id, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const row = document.getElementById('row-' + id);
row.style.transition = 'opacity 0.3s';
row.style.opacity = '0';
setTimeout(() => row.remove(), 300);
showMessage('Regex deleted successfully', 'success');
} else {
showMessage('Error deleting regex', 'error');
}
})
.catch(error => {
showMessage('Error deleting regex', 'error');
});
}
function showMessage(message, type = 'success') {
const messageDiv = document.getElementById('message');
const bgColor = type === 'success' ? 'bg-green-50 border-green-200' : 'bg-red-50 border-red-200';
const textColor = type === 'success' ? 'text-green-800' : 'text-red-800';
const icon = type === 'success' ? 'check-circle' : 'exclamation-circle';
messageDiv.innerHTML = `
<div class="mt-4 p-4 ${bgColor} border rounded-lg">
<div class="flex items-center justify-between">
<p class="${textColor}">
<i class="fa fa-${icon} mr-2"></i>${message}
</p>
<button type="button" class="${textColor} hover:opacity-75" onclick="this.parentElement.parentElement.remove()">
<i class="fa fa-times"></i>
</button>
</div>
</div>
`;
setTimeout(() => {
messageDiv.innerHTML = '';
}, 5000);
}
</script>
@endpush
@endsection
@@ -0,0 +1,181 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-edit mr-2"></i>{{ $title ?? 'Collection Regex Edit' }}
</h1>
<a href="{{ url('/admin/collection_regexes-list') }}" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200">
<i class="fa fa-arrow-left mr-2"></i>Back to List
</a>
</div>
</div>
<!-- Form -->
<form action="{{ url('/admin/collection_regexes-edit?action=submit') }}" method="POST" id="regexForm" class="px-6 py-6">
@csrf
<input type="hidden" name="id" value="{{ $regex->id ?? '' }}"/>
<!-- Error Message -->
@if($error)
<div class="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
<div class="flex">
<i class="fa fa-exclamation-triangle text-red-500 mr-3"></i>
<p class="text-red-800">{{ $error }}</p>
</div>
</div>
@endif
<!-- Group -->
<div class="mb-6">
<label for="group_regex" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Group: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-users text-gray-400"></i>
</div>
<input type="text"
id="group_regex"
name="group_regex"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ htmlspecialchars($regex->group_regex ?? '') }}"
required>
</div>
<p class="mt-2 text-sm text-gray-500">
Regex to match against a group or multiple groups. Delimiters are already added, and PCRE_CASELESS is added after for case insensitivity.<br>
Example of matching a single group: <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">alt\.binaries\.example</code><br>
Example of matching multiple groups: <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">alt\.binaries.*</code>
</p>
</div>
<!-- Regex -->
<div class="mb-6">
<label for="regex" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Regex: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-code text-gray-400"></i>
</div>
<textarea id="regex"
name="regex"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
rows="4"
required>{{ htmlspecialchars($regex->regex ?? '') }}</textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Regex to use when grouping binaries into collections.<br>
The regex delimiters are not added, you MUST add them. See <a href="http://php.net/manual/en/regexp.reference.delimiters" target="_blank" class="text-blue-600 dark:text-blue-400 hover:text-blue-800">this</a> page.<br>
To make the regex case insensitive, add <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">i</code> after the last delimiter.
</p>
</div>
<!-- Description -->
<div class="mb-6">
<label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Description:
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-align-left text-gray-400"></i>
</div>
<textarea id="description"
name="description"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
rows="3">{{ htmlspecialchars($regex->description ?? '') }}</textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Description for this regex. You can include an example usenet subject this regex would match on.
</p>
</div>
<!-- Ordinal -->
<div class="mb-6">
<label for="ordinal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Ordinal: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-sort-numeric-asc text-gray-400"></i>
</div>
<input type="number"
id="ordinal"
name="ordinal"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $regex->ordinal ?? 0 }}"
min="0"
required>
</div>
<p class="mt-2 text-sm text-gray-500">
The order to run this regex in. Must be a number, 0 or higher.<br>
If multiple regex have the same ordinal, MySQL will randomly sort them.
</p>
</div>
<!-- Active Status -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Active:
</label>
<div class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
@foreach($status_ids as $k => $id)
<div class="flex items-center {{ $loop->last ? '' : 'mb-3' }}">
<input type="radio"
name="status"
id="status{{ $id }}"
value="{{ $id }}"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ ($regex->status ?? 1) == $id ? 'checked' : '' }}>
<label for="status{{ $id }}" class="ml-3 text-sm text-gray-700">
{{ $status_names[$k] }}
</label>
</div>
@endforeach
</div>
<p class="mt-2 text-sm text-gray-500">
Only active regex are used during the collection matching process.
</p>
</div>
</form>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between">
<a href="{{ url('/admin/collection_regexes-list') }}" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Cancel
</a>
<button type="submit" form="regexForm" class="px-4 py-2 bg-green-600 dark:bg-green-700 text-white rounded-lg hover:bg-green-700">
<i class="fa fa-save mr-2"></i>Save Changes
</button>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('regexForm');
if (form) {
form.addEventListener('submit', function(event) {
const groupRegex = document.getElementById('group_regex');
const regex = document.getElementById('regex');
const ordinal = document.getElementById('ordinal');
if (!groupRegex.value.trim() || !regex.value.trim() || !ordinal.value) {
event.preventDefault();
alert('Please fill in all required fields.');
return false;
}
});
}
});
</script>
@endpush
@endsection
@@ -0,0 +1,234 @@
@extends('layouts.admin')
@section('content')
<div class="max-w-full px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-layer-group mr-2"></i>{{ $title ?? 'Collection Regex List' }}
</h1>
<a href="{{ url('/admin/collection_regexes-edit') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add New Regex
</a>
</div>
</div>
<!-- Info Alert -->
<div class="px-6 py-4 bg-blue-50 border-b border-blue-100">
<div class="flex">
<div class="flex-shrink-0">
<i class="fa fa-info-circle text-blue-500 text-2xl"></i>
</div>
<div class="ml-3">
<p class="text-sm text-blue-700">
This page lists regular expressions used for grouping binaries into collections.<br>
You can test your regex patterns using the <a href="{{ url('/admin/collection_regexes-test') }}" class="font-semibold underline hover:text-blue-900">test feature</a>.
</p>
</div>
</div>
</div>
<!-- Messages -->
<div id="message" class="px-6"></div>
<!-- Search Form -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<form name="groupsearch" action="" method="get" class="max-w-md">
@csrf
<label for="group" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Search by Group:</label>
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-search text-gray-400"></i>
</div>
<input id="group"
type="text"
name="group"
value="{{ $group }}"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Search a group...">
</div>
<button class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700" type="submit">
Search
</button>
</div>
</form>
</div>
<!-- Table -->
@if($regex && count($regex) > 0)
<!-- Pagination Top -->
@if(method_exists($regex, 'links'))
<div class="px-6 py-3 border-b border-gray-200">
{{ $regex->onEachSide(5)->links() }}
</div>
@endif
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-20">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Group</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Regex</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Ordinal</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Status</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-28">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($regex as $row)
<tr id="row-{{ $row->id }}" class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-semibold text-gray-900">{{ $row->id }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<code class="bg-blue-50 text-blue-700 px-2 py-1 rounded text-xs">{{ $row->group_regex }}</code>
</td>
<td class="px-6 py-4 text-sm text-gray-500">
<span class="inline-block max-w-[200px] truncate" title="{{ $row->description }}">
{{ $row->description }}
</span>
</td>
<td class="px-6 py-4 text-sm">
<div class="max-w-[200px] break-words">
<code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs break-all" title="{{ htmlspecialchars($row->regex) }}">
{{ htmlspecialchars($row->regex) }}
</code>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-900">{{ $row->ordinal }}</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
@if($row->status == 1)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check-circle mr-1"></i>Active
</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times-circle mr-1"></i>Disabled
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-medium">
<div class="flex gap-2 justify-center">
<a href="{{ url('/admin/collection_regexes-edit?id=' . $row->id) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900"
title="Edit this regex">
<i class="fa fa-edit"></i>
</a>
<button type="button"
class="text-red-600 hover:text-red-900"
onclick="confirmDelete({{ $row->id }})"
title="Delete this regex">
<i class="fa fa-trash"></i>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Pagination Bottom -->
@if(method_exists($regex, 'links'))
<div class="px-6 py-4 border-t border-gray-200">
{{ $regex->onEachSide(5)->links() }}
</div>
@endif
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-exclamation-triangle text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No regex patterns found</h3>
<p class="text-gray-500 mb-4">Try a different search term or add a new regex.</p>
<a href="{{ url('/admin/collection_regexes-edit') }}" 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-plus mr-2"></i>Add New Regex
</a>
</div>
@endif
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">
@if($regex && method_exists($regex, 'total'))
Total entries: {{ $regex->total() }}
@elseif($regex)
Total entries: {{ count($regex) }}
@else
No entries
@endif
</span>
<div class="flex gap-2">
<a href="{{ url('/admin/collection_regexes-test') }}" class="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 text-sm">
<i class="fa fa-flask mr-2"></i>Test Regex
</a>
<a href="{{ url('/admin/collection_regexes-edit') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-800 text-sm">
<i class="fa fa-plus mr-2"></i>Add New Regex
</a>
</div>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
function confirmDelete(id) {
if (confirm('Are you sure you want to delete this regex? This action cannot be undone.')) {
deleteRegex(id);
}
}
function deleteRegex(id) {
fetch('{{ url("/admin/collection_regexes-delete") }}?id=' + id, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const row = document.getElementById('row-' + id);
row.style.transition = 'opacity 0.3s';
row.style.opacity = '0';
setTimeout(() => row.remove(), 300);
showMessage('Regex deleted successfully', 'success');
} else {
showMessage('Error deleting regex', 'error');
}
})
.catch(error => {
showMessage('Error deleting regex', 'error');
});
}
function showMessage(message, type = 'success') {
const messageDiv = document.getElementById('message');
const bgColor = type === 'success' ? 'bg-green-50 border-green-200' : 'bg-red-50 border-red-200';
const textColor = type === 'success' ? 'text-green-800' : 'text-red-800';
const icon = type === 'success' ? 'check-circle' : 'exclamation-circle';
messageDiv.innerHTML = `
<div class="mt-4 p-4 ${bgColor} border rounded-lg">
<div class="flex items-center justify-between">
<p class="${textColor}">
<i class="fa fa-${icon} mr-2"></i>${message}
</p>
<button type="button" class="${textColor} hover:opacity-75" onclick="this.parentElement.parentElement.remove()">
<i class="fa fa-times"></i>
</button>
</div>
</div>
`;
setTimeout(() => {
messageDiv.innerHTML = '';
}, 5000);
}
</script>
@endpush
@endsection
@@ -0,0 +1,170 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-flask mr-2"></i>{{ $title ?? 'Collection Regex Test' }}
</h1>
<a href="{{ url('/admin/collection_regexes-list') }}" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200">
<i class="fa fa-arrow-left mr-2"></i>Back to List
</a>
</div>
</div>
<!-- Info Alert -->
<div class="px-6 py-4 bg-blue-50 border-b border-blue-100">
<div class="flex">
<i class="fa fa-info-circle text-blue-500 text-xl mr-3"></i>
<p class="text-sm text-blue-700">
Test your collection regex patterns against actual binary data from your database.
</p>
</div>
</div>
<!-- Test Form -->
<form method="GET" action="{{ url('/admin/collection_regexes-test') }}" class="px-6 py-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<!-- Group Field -->
<div>
<label for="group" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Group: <span class="text-red-500">*</span>
</label>
<input type="text"
id="group"
name="group"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $group }}"
placeholder="alt.binaries.teevee"
required>
<p class="mt-2 text-sm text-gray-500">
Enter a newsgroup name to test against
</p>
</div>
<!-- Limit Field -->
<div>
<label for="limit" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Limit:
</label>
<input type="number"
id="limit"
name="limit"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $limit }}"
min="1"
max="1000">
<p class="mt-2 text-sm text-gray-500">
Number of binaries to test (max 1000)
</p>
</div>
</div>
<!-- Regex Field -->
<div class="mb-6">
<label for="regex" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Regex: <span class="text-red-500">*</span>
</label>
<textarea id="regex"
name="regex"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
rows="4"
required
placeholder="/^(?P<name>.*?)([\. ]S\d{1,3}[\. ]?E\d{1,3})/i">{{ $regex }}</textarea>
<p class="mt-2 text-sm text-gray-500">
Enter the regex pattern to test. Include delimiters and flags.
</p>
</div>
<!-- Submit Button -->
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-play mr-2"></i>Test Regex
</button>
</form>
<!-- Results Section -->
@if($data)
<div class="px-6 py-6 border-t border-gray-200">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200 mb-4">
<i class="fa fa-chart-bar mr-2"></i>Test Results:
</h2>
@if(count($data) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Binary ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Subject</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-64">Match</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($data as $row)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm font-semibold text-gray-900">
{{ $row['binaryID'] ?? $row['id'] ?? 'N/A' }}
</td>
<td class="px-6 py-4 text-sm text-gray-500">
<div class="max-w-2xl break-words">
{{ $row['subject'] ?? '' }}
</div>
</td>
<td class="px-6 py-4 text-sm">
@if(isset($row['match']) && $row['match'])
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">
<i class="fa fa-check mr-1"></i>Match
</span>
@if(isset($row['name']))
<div class="mt-2 text-xs text-gray-600">
<strong>Name:</strong> {{ $row['name'] }}
</div>
@endif
@else
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-red-100 text-red-800">
<i class="fa fa-times mr-1"></i>No Match
</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Success Summary -->
<div class="mt-6 p-4 bg-green-50 border border-green-200 rounded-lg">
<div class="flex">
<i class="fa fa-check-circle text-green-500 text-xl mr-3"></i>
<div>
<p class="text-green-800 font-medium">
Tested {{ count($data) }} binaries
</p>
<p class="text-sm text-green-700 mt-1">
Review the matches above to verify your regex pattern is working correctly.
</p>
</div>
</div>
</div>
@else
<!-- No Results Warning -->
<div class="p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
<div class="flex">
<i class="fa fa-exclamation-triangle text-yellow-500 text-xl mr-3"></i>
<div>
<p class="text-yellow-800 font-medium">No binaries found</p>
<p class="text-sm text-yellow-700 mt-1">
No binaries found for the specified group or no matches found. Try a different group or regex pattern.
</p>
</div>
</div>
</div>
@endif
</div>
@endif
</div>
</div>
@endsection
@@ -0,0 +1,181 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-edit mr-2"></i>{{ $title ?? 'Release Naming Regex Edit' }}
</h1>
<a href="{{ url('/admin/release_naming_regexes-list') }}" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200">
<i class="fa fa-arrow-left mr-2"></i>Back to List
</a>
</div>
</div>
<!-- Form -->
<form action="{{ url('/admin/release_naming_regexes-edit?action=submit') }}" method="POST" id="regexForm" class="px-6 py-6">
@csrf
<input type="hidden" name="id" value="{{ $regex->id ?? '' }}"/>
<!-- Error Message -->
@if($error)
<div class="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg">
<div class="flex">
<i class="fa fa-exclamation-triangle text-red-500 mr-3"></i>
<p class="text-red-800">{{ $error }}</p>
</div>
</div>
@endif
<!-- Group -->
<div class="mb-6">
<label for="group_regex" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Group: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-users text-gray-400"></i>
</div>
<input type="text"
id="group_regex"
name="group_regex"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ htmlspecialchars($regex->group_regex ?? '') }}"
required>
</div>
<p class="mt-2 text-sm text-gray-500">
Regex to match against a group or multiple groups. Delimiters are already added, and PCRE_CASELESS is added after for case insensitivity.<br>
Example of matching a single group: <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">alt\.binaries\.example</code><br>
Example of matching multiple groups: <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">alt\.binaries.*</code>
</p>
</div>
<!-- Regex -->
<div class="mb-6">
<label for="regex" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Regex: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-code text-gray-400"></i>
</div>
<textarea id="regex"
name="regex"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
rows="4"
required>{{ htmlspecialchars($regex->regex ?? '') }}</textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Regex to use when renaming releases.<br>
The regex delimiters are not added, you MUST add them. See <a href="http://php.net/manual/en/regexp.reference.delimiters" target="_blank" class="text-blue-600 dark:text-blue-400 hover:text-blue-800">this</a> page.<br>
To make the regex case insensitive, add <code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">i</code> after the last delimiter.
</p>
</div>
<!-- Description -->
<div class="mb-6">
<label for="description" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Description:
</label>
<div class="relative">
<div class="absolute top-3 left-3 pointer-events-none">
<i class="fa fa-align-left text-gray-400"></i>
</div>
<textarea id="description"
name="description"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
rows="3">{{ htmlspecialchars($regex->description ?? '') }}</textarea>
</div>
<p class="mt-2 text-sm text-gray-500">
Description for this regex. You can include an example release name this regex would match on.
</p>
</div>
<!-- Ordinal -->
<div class="mb-6">
<label for="ordinal" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Ordinal: <span class="text-red-500">*</span>
</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-sort-numeric-asc text-gray-400"></i>
</div>
<input type="number"
id="ordinal"
name="ordinal"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $regex->ordinal ?? 0 }}"
min="0"
required>
</div>
<p class="mt-2 text-sm text-gray-500">
The order to run this regex in. Must be a number, 0 or higher.<br>
If multiple regex have the same ordinal, MySQL will randomly sort them.
</p>
</div>
<!-- Active Status -->
<div class="mb-6">
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Active:
</label>
<div class="bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg p-4">
@foreach($status_ids as $k => $id)
<div class="flex items-center {{ $loop->last ? '' : 'mb-3' }}">
<input type="radio"
name="status"
id="status{{ $id }}"
value="{{ $id }}"
class="w-4 h-4 text-blue-600 dark:text-blue-400 border-gray-300 dark:border-gray-600 focus:ring-blue-500"
{{ ($regex->status ?? 1) == $id ? 'checked' : '' }}>
<label for="status{{ $id }}" class="ml-3 text-sm text-gray-700">
{{ $status_names[$k] }}
</label>
</div>
@endforeach
</div>
<p class="mt-2 text-sm text-gray-500">
Only active regex are used during the release naming process.
</p>
</div>
</form>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between">
<a href="{{ url('/admin/release_naming_regexes-list') }}" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Cancel
</a>
<button type="submit" form="regexForm" class="px-4 py-2 bg-green-600 dark:bg-green-700 text-white rounded-lg hover:bg-green-700">
<i class="fa fa-save mr-2"></i>Save Changes
</button>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('regexForm');
if (form) {
form.addEventListener('submit', function(event) {
const groupRegex = document.getElementById('group_regex');
const regex = document.getElementById('regex');
const ordinal = document.getElementById('ordinal');
if (!groupRegex.value.trim() || !regex.value.trim() || !ordinal.value) {
event.preventDefault();
alert('Please fill in all required fields.');
return false;
}
});
}
});
</script>
@endpush
@endsection
@@ -0,0 +1,234 @@
@extends('layouts.admin')
@section('content')
<div class="max-w-full px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-tag mr-2"></i>{{ $title ?? 'Release Naming Regex List' }}
</h1>
<a href="{{ url('/admin/release_naming_regexes-edit') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add New Regex
</a>
</div>
</div>
<!-- Info Alert -->
<div class="px-6 py-4 bg-blue-50 border-b border-blue-100">
<div class="flex">
<div class="flex-shrink-0">
<i class="fa fa-info-circle text-blue-500 text-2xl"></i>
</div>
<div class="ml-3">
<p class="text-sm text-blue-700">
This page lists regular expressions used for renaming releases based on their names.<br>
You can test your regex patterns using the <a href="{{ url('/admin/release_naming_regexes-test') }}" class="font-semibold underline hover:text-blue-900">test feature</a>.
</p>
</div>
</div>
</div>
<!-- Messages -->
<div id="message" class="px-6"></div>
<!-- Search Form -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<form name="groupsearch" action="" method="get" class="max-w-md">
@csrf
<label for="group" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Search by Group:</label>
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-search text-gray-400"></i>
</div>
<input id="group"
type="text"
name="group"
value="{{ $group }}"
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Search a group...">
</div>
<button class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700" type="submit">
Search
</button>
</div>
</form>
</div>
<!-- Table -->
@if($regex && count($regex) > 0)
<!-- Pagination Top -->
@if(method_exists($regex, 'links'))
<div class="px-6 py-3 border-b border-gray-200">
{{ $regex->onEachSide(5)->links() }}
</div>
@endif
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-20">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Group</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Regex</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Ordinal</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Status</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-28">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($regex as $row)
<tr id="row-{{ $row->id }}" class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-semibold text-gray-900">{{ $row->id }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<code class="bg-blue-50 text-blue-700 px-2 py-1 rounded text-xs">{{ $row->group_regex }}</code>
</td>
<td class="px-6 py-4 text-sm text-gray-500">
<span class="inline-block max-w-[200px] truncate" title="{{ $row->description }}">
{{ $row->description }}
</span>
</td>
<td class="px-6 py-4 text-sm">
<div class="max-w-[200px] break-words">
<code class="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs break-all" title="{{ htmlspecialchars($row->regex) }}">
{{ htmlspecialchars($row->regex) }}
</code>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-center text-sm text-gray-900">{{ $row->ordinal }}</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
@if($row->status == 1)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check-circle mr-1"></i>Active
</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
<i class="fa fa-times-circle mr-1"></i>Disabled
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-center text-sm font-medium">
<div class="flex gap-2 justify-center">
<a href="{{ url('/admin/release_naming_regexes-edit?id=' . $row->id) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900"
title="Edit this regex">
<i class="fa fa-edit"></i>
</a>
<button type="button"
class="text-red-600 hover:text-red-900"
onclick="confirmDelete({{ $row->id }})"
title="Delete this regex">
<i class="fa fa-trash"></i>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Pagination Bottom -->
@if(method_exists($regex, 'links'))
<div class="px-6 py-4 border-t border-gray-200">
{{ $regex->onEachSide(5)->links() }}
</div>
@endif
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-exclamation-triangle text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No regex patterns found</h3>
<p class="text-gray-500 mb-4">Try a different search term or add a new regex.</p>
<a href="{{ url('/admin/release_naming_regexes-edit') }}" 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-plus mr-2"></i>Add New Regex
</a>
</div>
@endif
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50">
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">
@if($regex && method_exists($regex, 'total'))
Total entries: {{ $regex->total() }}
@elseif($regex)
Total entries: {{ count($regex) }}
@else
No entries
@endif
</span>
<div class="flex gap-2">
<a href="{{ url('/admin/release_naming_regexes-test') }}" class="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 text-sm">
<i class="fa fa-flask mr-2"></i>Test Regex
</a>
<a href="{{ url('/admin/release_naming_regexes-edit') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-800 text-sm">
<i class="fa fa-plus mr-2"></i>Add New Regex
</a>
</div>
</div>
</div>
</div>
</div>
@push('scripts')
<script>
function confirmDelete(id) {
if (confirm('Are you sure you want to delete this regex? This action cannot be undone.')) {
deleteRegex(id);
}
}
function deleteRegex(id) {
fetch('{{ url("/admin/release_naming_regexes-delete") }}?id=' + id, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
const row = document.getElementById('row-' + id);
row.style.transition = 'opacity 0.3s';
row.style.opacity = '0';
setTimeout(() => row.remove(), 300);
showMessage('Regex deleted successfully', 'success');
} else {
showMessage('Error deleting regex', 'error');
}
})
.catch(error => {
showMessage('Error deleting regex', 'error');
});
}
function showMessage(message, type = 'success') {
const messageDiv = document.getElementById('message');
const bgColor = type === 'success' ? 'bg-green-50 border-green-200' : 'bg-red-50 border-red-200';
const textColor = type === 'success' ? 'text-green-800' : 'text-red-800';
const icon = type === 'success' ? 'check-circle' : 'exclamation-circle';
messageDiv.innerHTML = `
<div class="mt-4 p-4 ${bgColor} border rounded-lg">
<div class="flex items-center justify-between">
<p class="${textColor}">
<i class="fa fa-${icon} mr-2"></i>${message}
</p>
<button type="button" class="${textColor} hover:opacity-75" onclick="this.parentElement.parentElement.remove()">
<i class="fa fa-times"></i>
</button>
</div>
</div>
`;
setTimeout(() => {
messageDiv.innerHTML = '';
}, 5000);
}
</script>
@endpush
@endsection
@@ -0,0 +1,192 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-flask mr-2"></i>{{ $title ?? 'Release Naming Regex Test' }}
</h1>
<a href="{{ url('/admin/release_naming_regexes-list') }}" class="px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-200">
<i class="fa fa-arrow-left mr-2"></i>Back to List
</a>
</div>
</div>
<!-- Info Alert -->
<div class="px-6 py-4 bg-blue-50 border-b border-blue-100">
<div class="flex">
<i class="fa fa-info-circle text-blue-500 text-xl mr-3"></i>
<p class="text-sm text-blue-700">
Test your release naming regex patterns against actual release data from your database.
</p>
</div>
</div>
<!-- Test Form -->
<form method="GET" action="{{ url('/admin/release_naming_regexes-test') }}" class="px-6 py-6">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<!-- Group Field -->
<div class="md:col-span-1">
<label for="group" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Group: <span class="text-red-500">*</span>
</label>
<input type="text"
id="group"
name="group"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $group }}"
placeholder="alt.binaries.teevee"
required>
<p class="mt-2 text-sm text-gray-500">
Enter a newsgroup name to test against
</p>
</div>
<!-- Show Limit Field -->
<div>
<label for="showlimit" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Show Limit:
</label>
<input type="number"
id="showlimit"
name="showlimit"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $showlimit }}"
min="1"
max="1000">
<p class="mt-2 text-sm text-gray-500">
Results to display
</p>
</div>
<!-- Query Limit Field -->
<div>
<label for="querylimit" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Query Limit:
</label>
<input type="number"
id="querylimit"
name="querylimit"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
value="{{ $querylimit }}"
min="1"
max="500000">
<p class="mt-2 text-sm text-gray-500">
Max releases to query
</p>
</div>
</div>
<!-- Regex Field -->
<div class="mb-6">
<label for="regex" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Regex: <span class="text-red-500">*</span>
</label>
<textarea id="regex"
name="regex"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono text-sm"
rows="4"
required
placeholder="/^(?P<name>.*?)([\. ]S\d{1,3}[\. ]?E\d{1,3})/i">{{ $regex }}</textarea>
<p class="mt-2 text-sm text-gray-500">
Enter the regex pattern to test. Include delimiters and flags.
</p>
</div>
<!-- Submit Button -->
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-play mr-2"></i>Test Regex
</button>
</form>
<!-- Results Section -->
@if($data)
<div class="px-6 py-6 border-t border-gray-200">
<h2 class="text-xl font-semibold text-gray-800 dark:text-gray-200 mb-4">
<i class="fa fa-chart-bar mr-2"></i>Test Results:
</h2>
@if(count($data) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-24">Release ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Original Name</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">New Name</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider w-28">Match</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($data as $row)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm font-semibold text-gray-900">
{{ $row['releaseID'] ?? $row['id'] ?? 'N/A' }}
</td>
<td class="px-6 py-4 text-sm text-gray-500">
<div class="max-w-xl break-words">
{{ $row['oldName'] ?? $row['name'] ?? '' }}
</div>
</td>
<td class="px-6 py-4 text-sm">
@if(isset($row['newName']) && $row['newName'])
<div class="max-w-xl break-words font-semibold text-green-600">
{{ $row['newName'] }}
</div>
@else
<span class="text-gray-400"></span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
@if(isset($row['match']) && $row['match'])
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">
<i class="fa fa-check mr-1"></i>Match
</span>
@else
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold bg-red-100 text-red-800">
<i class="fa fa-times mr-1"></i>No Match
</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Success Summary -->
<div class="mt-6 p-4 bg-green-50 border border-green-200 rounded-lg">
<div class="flex">
<i class="fa fa-check-circle text-green-500 text-xl mr-3"></i>
<div>
<p class="text-green-800 font-medium">
Tested {{ count($data) }} releases
</p>
<p class="text-sm text-green-700 mt-1">
Review the matches above to verify your regex pattern is working correctly.
</p>
</div>
</div>
</div>
@else
<!-- No Results Warning -->
<div class="p-4 bg-yellow-50 border border-yellow-200 rounded-lg">
<div class="flex">
<i class="fa fa-exclamation-triangle text-yellow-500 text-xl mr-3"></i>
<div>
<p class="text-yellow-800 font-medium">No releases found</p>
<p class="text-sm text-yellow-700 mt-1">
No releases found for the specified group or no matches found. Try a different group or regex pattern.
</p>
</div>
</div>
</div>
@endif
</div>
@endif
</div>
</div>
@endsection
@@ -0,0 +1,246 @@
@extends('layouts.admin')
@section('title', 'Edit Release')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="mb-6">
<h1 class="text-3xl font-bold text-gray-800">Edit Release</h1>
<nav class="text-sm text-gray-600 dark:text-gray-400 mt-2">
<a href="{{ route('admin.index') }}" class="hover:text-blue-600">Dashboard</a>
<i class="fas fa-chevron-right mx-2 text-xs"></i>
<a href="{{ route('admin.release-list') }}" class="hover:text-blue-600">Releases</a>
<i class="fas fa-chevron-right mx-2 text-xs"></i>
<span>Edit</span>
</nav>
</div>
@if(session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
<i class="fas fa-check-circle mr-2"></i>{{ session('success') }}
</div>
@endif
@if(session('error'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
<i class="fas fa-exclamation-circle mr-2"></i>{{ session('error') }}
</div>
@endif
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<form action="{{ route('admin.release-edit') }}" method="POST">
@csrf
<input type="hidden" name="action" value="submit">
<input type="hidden" name="id" value="{{ $release->id ?? $release['id'] ?? '' }}">
<input type="hidden" name="guid" value="{{ $release->guid ?? $release['guid'] ?? '' }}">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Release Name -->
<div class="md:col-span-2">
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Release Name
</label>
<input type="text"
id="name"
name="name"
value="{{ $release->name ?? $release['name'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- Search Name -->
<div class="md:col-span-2">
<label for="searchname" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Search Name
</label>
<input type="text"
id="searchname"
name="searchname"
value="{{ $release->searchname ?? $release['searchname'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- From Name -->
<div class="md:col-span-2">
<label for="fromname" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
From Name
</label>
<input type="text"
id="fromname"
name="fromname"
value="{{ $release->fromname ?? $release['fromname'] ?? '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- Category -->
<div>
<label for="category" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Category
</label>
<select id="category"
name="category"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
@foreach($catlist as $catId => $catTitle)
<option value="{{ $catId }}" {{ ($release->categories_id ?? $release['categories_id'] ?? '') == $catId ? 'selected' : '' }}>
{{ $catTitle }}
</option>
@endforeach
</select>
</div>
<!-- Total Parts -->
<div>
<label for="totalpart" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Total Parts
</label>
<input type="number"
id="totalpart"
name="totalpart"
value="{{ $release->totalpart ?? $release['totalpart'] ?? 0 }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- Grabs -->
<div>
<label for="grabs" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Grabs
</label>
<input type="number"
id="grabs"
name="grabs"
value="{{ $release->grabs ?? $release['grabs'] ?? 0 }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- Size (in bytes) -->
<div>
<label for="size" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Size (bytes)
</label>
<input type="number"
id="size"
name="size"
value="{{ $release->size ?? $release['size'] ?? 0 }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<p class="text-xs text-gray-500 mt-1">
Current: {{ number_format(($release->size ?? $release['size'] ?? 0) / 1073741824, 2) }} GB
</p>
</div>
<!-- Post Date -->
<div>
<label for="postdate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Post Date
</label>
<input type="datetime-local"
id="postdate"
name="postdate"
value="{{ isset($release->postdate) || isset($release['postdate']) ? date('Y-m-d\TH:i', strtotime($release->postdate ?? $release['postdate'])) : '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- Add Date -->
<div>
<label for="adddate" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Add Date
</label>
<input type="datetime-local"
id="adddate"
name="adddate"
value="{{ isset($release->adddate) || isset($release['adddate']) ? date('Y-m-d\TH:i', strtotime($release->adddate ?? $release['adddate'])) : '' }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- Video ID -->
<div>
<label for="videos_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Video ID
</label>
<input type="number"
id="videos_id"
name="videos_id"
value="{{ $release->videos_id ?? $release['videos_id'] ?? 0 }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- TV Episode ID -->
<div>
<label for="tv_episodes_id" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
TV Episode ID
</label>
<input type="number"
id="tv_episodes_id"
name="tv_episodes_id"
value="{{ $release->tv_episodes_id ?? $release['tv_episodes_id'] ?? 0 }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- IMDB ID -->
<div>
<label for="imdbid" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
IMDB ID
</label>
<input type="text"
id="imdbid"
name="imdbid"
value="{{ $release->imdbid ?? $release['imdbid'] ?? '' }}"
placeholder="e.g., 0133093"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<!-- AniDB ID -->
<div>
<label for="anidbid" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
AniDB ID
</label>
<input type="number"
id="anidbid"
name="anidbid"
value="{{ $release->anidbid ?? $release['anidbid'] ?? 0 }}"
class="w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
</div>
<!-- Action Buttons -->
<div class="mt-6 flex gap-3">
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-800 transition inline-flex items-center">
<i class="fas fa-save mr-2"></i> Save Changes
</button>
<a href="{{ route('admin.release-list') }}" class="px-6 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 transition inline-flex items-center">
<i class="fas fa-times mr-2"></i> Cancel
</a>
<a href="{{ url('/details/' . ($release->guid ?? $release['guid'] ?? '')) }}" class="px-6 py-2 bg-green-600 dark:bg-green-700 text-white rounded-lg hover:bg-green-700 dark:hover:bg-green-800 transition inline-flex items-center">
<i class="fas fa-eye mr-2"></i> View Release
</a>
</div>
</form>
</div>
<!-- Additional Info -->
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 mt-6">
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-3">Release Information</h3>
<dl class="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
<div>
<dt class="font-medium text-gray-600">GUID</dt>
<dd class="text-gray-900 dark:text-gray-100 font-mono text-xs mt-1">{{ $release->guid ?? $release['guid'] ?? 'N/A' }}</dd>
</div>
<div>
<dt class="font-medium text-gray-600">Group</dt>
<dd class="text-gray-900 dark:text-gray-100 mt-1">{{ $release->group_name ?? $release['group_name'] ?? 'N/A' }}</dd>
</div>
<div>
<dt class="font-medium text-gray-600">Password Status</dt>
<dd class="text-gray-900 dark:text-gray-100 mt-1">
@if(($release->passwordstatus ?? $release['passwordstatus'] ?? 0) == 0)
<span class="text-green-600"><i class="fas fa-check-circle"></i> None</span>
@elseif(($release->passwordstatus ?? $release['passwordstatus'] ?? 0) == 1)
<span class="text-red-600"><i class="fas fa-lock"></i> Passworded</span>
@else
<span class="text-yellow-600"><i class="fas fa-question-circle"></i> Unknown</span>
@endif
</dd>
</div>
</dl>
</div>
</div>
@endsection
@@ -0,0 +1,188 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- 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="fa fa-exclamation-triangle mr-2"></i>{{ $title }}
</h1>
<a href="{{ url('/admin/release-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
<i class="fa fa-list mr-2"></i>View All Releases
</a>
</div>
</div>
<!-- Success/Error/Warning Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg">
<p class="text-red-800 dark:text-red-200">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
@if(session('warning'))
<div class="mx-6 mt-4 p-4 bg-yellow-50 dark:bg-yellow-900 border border-yellow-200 dark:border-yellow-700 rounded-lg">
<p class="text-yellow-800 dark:text-yellow-200">
<i class="fa fa-exclamation-triangle mr-2"></i>{{ session('warning') }}
</p>
</div>
@endif
<!-- Search Form -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
<form method="GET" action="{{ url('/admin/failrel-list') }}">
<div class="flex gap-2">
<div class="relative flex-1">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fa fa-search text-gray-400"></i>
</div>
<input type="text"
name="failrelsearch"
value="{{ request('failrelsearch') }}"
placeholder="Search by name or category..."
class="pl-10 w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:text-white">
</div>
<button type="submit" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
Search
</button>
@if(request('failrelsearch'))
<a href="{{ url('/admin/failrel-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
Clear
</a>
@endif
</div>
</form>
</div>
<!-- Failed Releases List Table -->
@if($releaselist && $releaselist->count() > 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-700">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Name</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Category</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Size</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Files</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Post Date</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Add Date</th>
<th class="px-6 py-3 text-center text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Grabs</th>
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700">
@foreach($releaselist as $release)
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700">
<td class="px-6 py-4">
<a href="{{ url('/admin/release-edit?id=' . $release->guid) }}"
class="text-sm font-medium text-blue-600 dark:text-blue-400 hover:underline truncate block max-w-xs"
title="{{ $release->searchname }}">
{{ \Illuminate\Support\Str::limit($release->searchname, 50) }}
</a>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200">
{{ $release->category_name ?? 'Unknown' }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200">
{{ human_filesize($release->size) }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
<a href="{{ url('/admin/release-files?id=' . $release->guid) }}"
class="inline-flex items-center px-2 py-1 text-xs font-semibold rounded-full bg-indigo-100 dark:bg-indigo-900 text-indigo-800 dark:text-indigo-200 hover:bg-indigo-200 dark:hover:bg-indigo-800">
<i class="fa fa-file mr-1"></i>{{ $release->totalpart ?? 0 }}
</a>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
<div class="flex items-center">
<i class="fa fa-calendar text-gray-400 mr-2"></i>
<span title="{{ $release->postdate }}">
{{ \Carbon\Carbon::parse($release->postdate)->format('Y-m-d H:i') }}
</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
<div class="flex items-center">
<i class="fa fa-calendar-plus-o text-gray-400 mr-2"></i>
<span title="{{ $release->adddate }}">
{{ \Carbon\Carbon::parse($release->adddate)->format('Y-m-d H:i') }}
</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-center">
<span class="px-2 py-1 text-xs font-semibold rounded-full bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200">
<i class="fa fa-download mr-1"></i>{{ $release->grabs ?? 0 }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<div class="flex justify-end gap-2">
<a href="{{ url('/admin/release-edit?id=' . $release->guid) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300"
title="Edit release">
<i class="fa fa-pencil"></i>
</a>
@if($release->guid)
<a href="{{ url('/details/' . $release->guid) }}"
class="text-indigo-600 dark:text-indigo-400 hover:text-indigo-900 dark:hover:text-indigo-300"
title="View release">
<i class="fa fa-eye"></i>
</a>
@endif
<a href="{{ url('/admin/release-delete/' . $release->guid) }}"
class="text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-300"
title="Delete release"
onclick="return confirm('Are you sure you want to delete this release?')">
<i class="fa fa-trash"></i>
</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700">
<div class="flex flex-col sm:flex-row justify-between items-center gap-4">
<div class="text-sm text-gray-700 dark:text-gray-300">
Showing <span class="font-semibold">{{ $releaselist->firstItem() }}</span> to
<span class="font-semibold">{{ $releaselist->lastItem() }}</span> of
<span class="font-semibold">{{ $releaselist->total() }}</span> failed releases
</div>
<div class="overflow-x-auto">
{{ $releaselist->onEachSide(2)->links() }}
</div>
</div>
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa 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>
@endif
</div>
</div>
@endsection
@@ -0,0 +1,122 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-list mr-2"></i>{{ $title }}
</h1>
</div>
</div>
<!-- Success/Error Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 border border-green-200 rounded-lg">
<p class="text-green-800">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
<p class="text-red-800">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
<!-- Release Table -->
@if(count($releaselist) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Category</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Size</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Files</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Posted</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Added</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Grabs</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($releaselist as $release)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ $release->id }}</td>
<td class="px-6 py-4 text-sm">
<div class="text-gray-900 dark:text-gray-100 font-medium max-w-md truncate" title="{{ $release->searchname }}">
{{ $release->searchname }}
</div>
<div class="text-gray-500 text-xs mt-1 max-w-md truncate" title="{{ $release->name }}">
{{ $release->name }}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 text-blue-800">
{{ $release->category_name ?? 'N/A' }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ number_format($release->size / 1073741824, 2) }} GB
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ $release->totalpart ?? 0 }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ \Carbon\Carbon::parse($release->postdate)->format('Y-m-d H:i') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ \Carbon\Carbon::parse($release->adddate)->format('Y-m-d H:i') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ $release->grabs ?? 0 }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div class="flex gap-2">
<a href="{{ url('/details/' . $release->guid) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900"
title="View Details"
target="_blank">
<i class="fa fa-eye"></i>
</a>
<a href="{{ url('admin/release-edit?id=' . $release->guid . '&action=view') }}"
class="text-green-600 hover:text-green-900"
title="Edit">
<i class="fa fa-edit"></i>
</a>
<a href="{{ url('admin/release-delete/' . $release->id) }}"
class="text-red-600 hover:text-red-900"
title="Delete"
onclick="return confirm('Are you sure you want to delete this release?')">
<i class="fa fa-trash"></i>
</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="px-6 py-4 border-t border-gray-200">
{{ $releaselist->links() }}
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-list text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No releases found</h3>
<p class="text-gray-500">No releases are currently available in the system.</p>
</div>
@endif
</div>
</div>
@endsection
+217
View File
@@ -0,0 +1,217 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-user-shield mr-2"></i>{{ $title }}
</h1>
</div>
<!-- Role Add Form -->
<form method="post" action="{{ url('admin/role-add') }}" class="p-6">
@csrf
<input type="hidden" name="action" value="submit">
<div class="space-y-6">
<!-- Role Name -->
<div>
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Role Name <span class="text-red-500">*</span>
</label>
<input type="text"
id="name"
name="name"
required
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">
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- API Requests -->
<div>
<label for="apirequests" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
API Requests per Day
</label>
<input type="number"
id="apirequests"
name="apirequests"
value="0"
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">
</div>
<!-- Download Requests -->
<div>
<label for="downloadrequests" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Download Requests per Day
</label>
<input type="number"
id="downloadrequests"
name="downloadrequests"
value="0"
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">
</div>
<!-- Default Invites -->
<div>
<label for="defaultinvites" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Default Invites
</label>
<input type="number"
id="defaultinvites"
name="defaultinvites"
value="0"
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">
</div>
<!-- Rate Limit -->
<div>
<label for="rate_limit" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Rate Limit (requests per minute)
</label>
<input type="number"
id="rate_limit"
name="rate_limit"
value="60"
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">
</div>
<!-- Donation -->
<div>
<label for="donation" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Donation Amount
</label>
<input type="number"
id="donation"
name="donation"
value="0"
step="0.01"
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">
</div>
<!-- Add Years -->
<div>
<label for="addyears" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Add Years (role duration)
</label>
<input type="number"
id="addyears"
name="addyears"
value="0"
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">
</div>
</div>
<!-- Permissions -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
Permissions
</label>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="flex items-center">
<input type="checkbox"
id="canpreview"
name="canpreview"
value="1"
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="canpreview" class="ml-2 text-sm text-gray-700">Can Preview</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="hideads"
name="hideads"
value="1"
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="hideads" class="ml-2 text-sm text-gray-700">Hide Ads</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="editrelease"
name="editrelease"
value="1"
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="editrelease" class="ml-2 text-sm text-gray-700">Edit Release</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewconsole"
name="viewconsole"
value="1"
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="viewconsole" class="ml-2 text-sm text-gray-700">View Console</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewmovies"
name="viewmovies"
value="1"
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="viewmovies" class="ml-2 text-sm text-gray-700">View Movies</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewaudio"
name="viewaudio"
value="1"
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="viewaudio" class="ml-2 text-sm text-gray-700">View Audio</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewpc"
name="viewpc"
value="1"
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="viewpc" class="ml-2 text-sm text-gray-700">View PC</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewtv"
name="viewtv"
value="1"
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="viewtv" class="ml-2 text-sm text-gray-700">View TV</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewadult"
name="viewadult"
value="1"
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="viewadult" class="ml-2 text-sm text-gray-700">View Adult</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewbooks"
name="viewbooks"
value="1"
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="viewbooks" class="ml-2 text-sm text-gray-700">View Books</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewother"
name="viewother"
value="1"
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="viewother" class="ml-2 text-sm text-gray-700">View Other</label>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="flex gap-3 pt-4 border-t border-gray-200">
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-save mr-2"></i>Create Role
</button>
<a href="{{ url('admin/role-list') }}" class="px-6 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Cancel
</a>
</div>
</div>
</form>
</div>
</div>
@endsection
+257
View File
@@ -0,0 +1,257 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-user-shield mr-2"></i>{{ $title }}
</h1>
</div>
<!-- Role Edit Form -->
@if($role)
<form method="post" action="{{ url('admin/role-edit') }}" class="p-6">
@csrf
<input type="hidden" name="action" value="submit">
<input type="hidden" name="id" value="{{ $role->id }}">
<div class="space-y-6">
<!-- Role Name -->
<div>
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Role Name <span class="text-red-500">*</span>
</label>
<input type="text"
id="name"
name="name"
value="{{ $role->name }}"
required
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">
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- API Requests -->
<div>
<label for="apirequests" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
API Requests per Day
</label>
<input type="number"
id="apirequests"
name="apirequests"
value="{{ $role->apirequests ?? 0 }}"
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">
</div>
<!-- Download Requests -->
<div>
<label for="downloadrequests" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Download Requests per Day
</label>
<input type="number"
id="downloadrequests"
name="downloadrequests"
value="{{ $role->downloadrequests ?? 0 }}"
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">
</div>
<!-- Default Invites -->
<div>
<label for="defaultinvites" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Default Invites
</label>
<input type="number"
id="defaultinvites"
name="defaultinvites"
value="{{ $role->defaultinvites ?? 0 }}"
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">
</div>
<!-- Rate Limit -->
<div>
<label for="rate_limit" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Rate Limit (requests per minute)
</label>
<input type="number"
id="rate_limit"
name="rate_limit"
value="{{ $role->rate_limit ?? 60 }}"
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">
</div>
<!-- Donation -->
<div>
<label for="donation" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Donation Amount
</label>
<input type="number"
id="donation"
name="donation"
value="{{ $role->donation ?? 0 }}"
step="0.01"
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">
</div>
<!-- Add Years -->
<div>
<label for="addyears" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Add Years (role duration)
</label>
<input type="number"
id="addyears"
name="addyears"
value="{{ $role->addyears ?? 0 }}"
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">
</div>
<!-- Is Default -->
<div>
<label for="isdefault" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Default Role
</label>
<select id="isdefault"
name="isdefault"
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">
@foreach($yesno_ids as $index => $value)
<option value="{{ $value }}" {{ ($role->isdefault ?? 0) == $value ? 'selected' : '' }}>
{{ $yesno_names[$index] }}
</option>
@endforeach
</select>
</div>
</div>
<!-- Permissions -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
Permissions
</label>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="flex items-center">
<input type="checkbox"
id="canpreview"
name="canpreview"
value="1"
{{ $role->hasPermissionTo('preview') ? '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="canpreview" class="ml-2 text-sm text-gray-700">Can Preview</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="hideads"
name="hideads"
value="1"
{{ $role->hasPermissionTo('hideads') ? '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="hideads" class="ml-2 text-sm text-gray-700">Hide Ads</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="editrelease"
name="editrelease"
value="1"
{{ $role->hasPermissionTo('edit release') ? '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="editrelease" class="ml-2 text-sm text-gray-700">Edit Release</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewconsole"
name="viewconsole"
value="1"
{{ $role->hasPermissionTo('view console') ? '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="viewconsole" class="ml-2 text-sm text-gray-700">View Console</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewmovies"
name="viewmovies"
value="1"
{{ $role->hasPermissionTo('view movies') ? '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="viewmovies" class="ml-2 text-sm text-gray-700">View Movies</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewaudio"
name="viewaudio"
value="1"
{{ $role->hasPermissionTo('view audio') ? '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="viewaudio" class="ml-2 text-sm text-gray-700">View Audio</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewpc"
name="viewpc"
value="1"
{{ $role->hasPermissionTo('view pc') ? '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="viewpc" class="ml-2 text-sm text-gray-700">View PC</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewtv"
name="viewtv"
value="1"
{{ $role->hasPermissionTo('view tv') ? '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="viewtv" class="ml-2 text-sm text-gray-700">View TV</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewadult"
name="viewadult"
value="1"
{{ $role->hasPermissionTo('view adult') ? '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="viewadult" class="ml-2 text-sm text-gray-700">View Adult</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewbooks"
name="viewbooks"
value="1"
{{ $role->hasPermissionTo('view books') ? '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="viewbooks" class="ml-2 text-sm text-gray-700">View Books</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="viewother"
name="viewother"
value="1"
{{ $role->hasPermissionTo('view other') ? '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="viewother" class="ml-2 text-sm text-gray-700">View Other</label>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="flex gap-3 pt-4 border-t border-gray-200">
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-save mr-2"></i>Update Role
</button>
<a href="{{ url('admin/role-list') }}" class="px-6 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Cancel
</a>
</div>
</div>
</form>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-exclamation-circle text-red-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">Role not found</h3>
<p class="text-gray-500 mb-4">The requested role could not be found.</p>
<a href="{{ url('admin/role-list') }}" 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-arrow-left mr-2"></i> Back to Role List
</a>
</div>
@endif
</div>
</div>
@endsection
@@ -0,0 +1,86 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-user-shield mr-2"></i>{{ $title }}
</h1>
<a href="{{ url('admin/role-add') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-plus mr-2"></i>Add New Role
</a>
</div>
</div>
<!-- Roles Table -->
@if(count($userroles) > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role Name</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">API Requests</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Download Requests</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Default Invites</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Rate Limit</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Default</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($userroles as $role)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ $role->id }}</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">{{ $role->name }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $role->apirequests ?? 'N/A' }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $role->downloadrequests ?? 'N/A' }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $role->defaultinvites ?? 0 }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $role->rate_limit ?? 60 }}</td>
<td class="px-6 py-4 whitespace-nowrap">
@if($role->isdefault)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
<i class="fa fa-check mr-1"></i>Yes
</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 dark:bg-gray-800 text-gray-800">
No
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div class="flex gap-2">
<a href="{{ url('admin/role-edit?id=' . $role->id) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900"
title="Edit">
<i class="fa fa-edit"></i>
</a>
<a href="{{ url('admin/role-delete?id=' . $role->id) }}"
class="text-red-600 hover:text-red-900"
title="Delete"
onclick="return confirm('Are you sure you want to delete role \'{{ $role->name }}\'?')">
<i class="fa fa-trash"></i>
</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-user-shield text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No roles found</h3>
<p class="text-gray-500">Create your first role to get started.</p>
</div>
@endif
</div>
</div>
@endsection
File diff suppressed because it is too large Load Diff
+150
View File
@@ -0,0 +1,150 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-chart-bar mr-2"></i>{{ $title }}
</h1>
</div>
<div class="p-6 space-y-8">
<!-- Top Grabbers -->
@if(!empty($topgrabs) && count($topgrabs) > 0)
<div>
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Top Grabbers</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Username</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Grabs</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($topgrabs as $grab)
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ $grab->username }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $grab->grabs }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
<!-- Top Downloads -->
@if(!empty($topdownloads) && count($topdownloads) > 0)
<div>
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Top Downloads</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Release</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Downloads</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($topdownloads as $download)
<tr>
<td class="px-6 py-4 text-sm font-medium text-gray-900">{{ $download->searchname }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $download->grabs }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
<!-- Recently Added -->
@if(!empty($recent) && count($recent) > 0)
<div>
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Recently Added Releases</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Releases</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($recent as $item)
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ $item->thedate }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $item->num }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
<!-- Users by Month -->
@if(!empty($usersbymonth) && count($usersbymonth) > 0)
<div>
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">User Signups by Month</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Month</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Signups</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($usersbymonth as $month)
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ $month->mth }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $month->num }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
<!-- Users by Role -->
@if(!empty($usersbyrole) && count($usersbyrole) > 0)
<div>
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Users by Role</h2>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Count</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($usersbyrole as $role)
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ $role->name }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $role->num }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@if(empty($topgrabs) && empty($topdownloads) && empty($recent) && empty($usersbymonth) && empty($usersbyrole))
<div class="text-center py-12">
<i class="fa fa-chart-bar text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No statistics available</h3>
<p class="text-gray-500">Statistics will appear here once data is collected.</p>
</div>
@endif
</div>
</div>
</div>
@endsection
@@ -0,0 +1,351 @@
@extends('layouts.admin')
@push('styles')
<style>
/* Select color states - Light mode */
#tmuxForm select.select-yes { background-color: #d1e7dd !important; border-color: #badbcc !important; color: #0f5132 !important; }
#tmuxForm select.select-no { background-color: #f8d7da !important; border-color: #f5c2c7 !important; color: #842029 !important; }
#tmuxForm select.select-other { background-color: #e2e3e5 !important; border-color: #d3d6d8 !important; color: #41464b !important; }
#tmuxForm select:focus { box-shadow: 0 0 0 .25rem rgba(13,110,253,.25); }
/* Select color states - Dark mode */
@media (prefers-color-scheme: dark) {
#tmuxForm select.select-yes { background-color: #1a3a2a !important; border-color: #2d5a3d !important; color: #7fc99b !important; }
#tmuxForm select.select-no { background-color: #3a1a1d !important; border-color: #5a2d31 !important; color: #f5a9b0 !important; }
#tmuxForm select.select-other { background-color: #2a2a2a !important; border-color: #3d3d3d !important; color: #b0b0b0 !important; }
}
</style>
@endpush
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-terminal mr-2"></i>{{ $title }}
</h1>
</div>
<!-- Success Message -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
<p class="text-green-800 dark:text-green-300">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
<!-- Tmux Settings Form -->
<form method="post" action="{{ url('admin/tmux-edit') }}" class="p-6" id="tmuxForm">
@csrf
<input type="hidden" name="action" value="submit">
<div class="space-y-8">
<!-- Tmux - How It Works -->
<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">Tmux - How It Works</h2>
<div class="bg-blue-50 dark:bg-gray-700 border border-blue-200 dark:border-gray-600 rounded-lg p-4">
<p class="text-sm text-gray-700 dark:text-gray-300 mb-2">Tmux is a screen multiplexer and at least version 1.6 is required. It is used here to allow multiple windows per session and multiple panes per window.</p>
<p class="text-sm text-gray-700 dark:text-gray-300 mb-2">Each script is run in its own shell environment. It is not looped, but allowed to run once and then exit. This notifies tmux that the pane is dead and can then be respawned with another iteration of the script in a new shell environment.</p>
<p class="text-sm text-gray-700 dark:text-gray-300 mb-2">This allows for scripts that crash to be restarted without user intervention.</p>
<div class="bg-yellow-50 dark:bg-gray-800 border border-yellow-300 dark:border-gray-600 rounded p-3 mt-3">
<p class="text-sm font-medium text-yellow-800 dark:text-gray-300"><i class="fa fa-exclamation-triangle mr-2"></i>NOTICE:</p>
<p class="text-sm text-yellow-700 dark:text-gray-400">If "Save Tmux Settings" is the last thing you did on this page, refreshing will save the current form values again, not reload from database.</p>
</div>
</div>
</div>
<!-- Monitor 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">Monitor Settings</h2>
<div class="space-y-4">
<x-form.group label="Tmux Scripts Running" for="running" help="Shutdown switch. When on, scripts run; when off, all scripts are terminated.">
<x-select id="running" name="running" class="w-full">
@foreach($yesno_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->running ?? '') == $val ? 'selected' : '' }}>
{{ $yesno_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Monitor Loop Timer" for="monitor_delay" help="Time between query refreshes. Lower = more frequent DB queries.">
<div class="flex gap-2">
<x-input id="monitor_delay" name="monitor_delay" type="number" value="{{ $site->monitor_delay ?? 300 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
<x-form.group label="Tmux Session Name" for="tmux_session" help="Session name for tmux. No spaces allowed. Can't be changed after scripts start.">
<x-input id="tmux_session" name="tmux_session" type="text" value="{{ $site->tmux_session ?? 'nntmux' }}" class="w-full" />
</x-form.group>
</div>
</div>
<!-- Sequential 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">Sequential Settings</h2>
<div class="space-y-4">
<x-form.group label="Run Sequential" for="sequential" help="Sequential runs update_binaries, backfill and update releases sequentially.">
<x-select id="sequential" name="sequential" class="w-full">
@foreach($sequential_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->sequential ?? '') == $val ? 'selected' : '' }}>
{{ $sequential_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Sequential Sleep Timer" for="seq_timer">
<div class="flex gap-2">
<x-input id="seq_timer" name="seq_timer" type="number" value="{{ $site->seq_timer ?? 60 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
<div class="bg-yellow-50 dark:bg-gray-800 border border-yellow-300 dark:border-gray-600 rounded p-3">
<p class="text-sm text-yellow-700 dark:text-gray-400"><i class="fa fa-exclamation-triangle mr-2"></i>Sequential mode is not recommended as it's not tested enough.</p>
</div>
</div>
</div>
<!-- Update Binaries 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">Update Binaries Settings</h2>
<div class="space-y-4">
<x-form.group label="Update Binaries" for="binaries" help="Gets from your last_record to now.">
<x-select id="binaries" name="binaries" class="w-full">
@foreach($binaries_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->binaries ?? '') == $val ? 'selected' : '' }}>
{{ $binaries_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Update Binaries Sleep Timer" for="bins_timer">
<div class="flex gap-2">
<x-input id="bins_timer" name="bins_timer" type="number" value="{{ $site->bins_timer ?? 10 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
<x-form.group label="Binaries Kill Timer" for="bins_kill_timer" help="Time allowed to run with no updates.">
<div class="flex gap-2">
<x-input id="bins_kill_timer" name="bins_kill_timer" type="number" value="{{ $site->bins_kill_timer ?? 30 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">minutes</span>
</div>
</x-form.group>
</div>
</div>
<!-- Backfill 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">Backfill Settings</h2>
<div class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<x-form.group label="Backfill Mode" for="backfill">
<x-select id="backfill" name="backfill" class="w-full">
@foreach($backfill_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->backfill ?? '') == $val ? 'selected' : '' }}>
{{ $backfill_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Backfill Order" for="backfill_order">
<x-select id="backfill_order" name="backfill_order" class="w-full">
@foreach($backfill_group_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->backfill_order ?? '') == $val ? 'selected' : '' }}>
{{ $backfill_group[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Backfill Days" for="backfill_days">
<x-select id="backfill_days" name="backfill_days" class="w-full">
@foreach($backfill_days_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->backfill_days ?? '') == $val ? 'selected' : '' }}>
{{ $backfill_days[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
</div>
<x-form.group label="Backfill Quantity" for="backfill_qty" help="Number of headers per group per thread to download.">
<x-input id="backfill_qty" name="backfill_qty" type="number" value="{{ $site->backfill_qty ?? 20000 }}" class="w-full" />
</x-form.group>
<x-form.group label="Backfill Groups" for="backfill_groups" help="Number of groups to backfill per loop.">
<x-input id="backfill_groups" name="backfill_groups" type="number" value="{{ $site->backfill_groups ?? 1 }}" class="w-full" />
</x-form.group>
<x-form.group label="Backfill Sleep Timer" for="back_timer">
<div class="flex gap-2">
<x-input id="back_timer" name="back_timer" type="number" value="{{ $site->back_timer ?? 300 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
<x-form.group label="Variable Sleep Timer" for="progressive" help="Vary backfill sleep depending on collection count.">
<x-select id="progressive" name="progressive" class="w-full">
@foreach($yesno_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->progressive ?? '') == $val ? 'selected' : '' }}>
{{ $yesno_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
</div>
</div>
<!-- Update Releases 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">Update Releases Settings</h2>
<div class="space-y-4">
<x-form.group label="Update Releases" for="releases" help="Create releases. Only turn off when you only want to post process.">
<x-select id="releases" name="releases" class="w-full">
@foreach($releases_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->releases ?? '') == $val ? 'selected' : '' }}>
{{ $releases_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Update Releases Sleep Timer" for="rel_timer">
<div class="flex gap-2">
<x-input id="rel_timer" name="rel_timer" type="number" value="{{ $site->rel_timer ?? 15 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
</div>
</div>
<!-- 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">Postprocessing Settings</h2>
<div class="space-y-4">
<x-form.group label="Postprocess Additional" for="post" help="Deep rar inspection, preview/sample creation, NFO processing.">
<x-select id="post" name="post" class="w-full">
@foreach($post_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->post ?? '') == $val ? 'selected' : '' }}>
{{ $post_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Postprocess Additional Sleep Timer" for="post_timer">
<div class="flex gap-2">
<x-input id="post_timer" name="post_timer" type="number" value="{{ $site->post_timer ?? 300 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
<x-form.group label="Postprocess Kill Timer" for="post_kill_timer" help="Time allowed with no screen updates.">
<div class="flex gap-2">
<x-input id="post_kill_timer" name="post_kill_timer" type="number" value="{{ $site->post_kill_timer ?? 300 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
<x-form.group label="Postprocess Amazon" for="post_amazon" help="Books, music and games lookups.">
<x-select id="post_amazon" name="post_amazon" class="w-full">
@foreach($yesno_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->post_amazon ?? '') == $val ? 'selected' : '' }}>
{{ $yesno_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Postprocess Amazon Sleep Timer" for="post_timer_amazon">
<div class="flex gap-2">
<x-input id="post_timer_amazon" name="post_timer_amazon" type="number" value="{{ $site->post_timer_amazon ?? 300 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
<x-form.group label="Postprocess Non-Amazon" for="post_non" help="Movies, anime and TV lookups.">
<x-select id="post_non" name="post_non" class="w-full">
@foreach($yesno_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->post_non ?? '') == $val ? 'selected' : '' }}>
{{ $yesno_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Postprocess Non-Amazon Sleep Timer" for="post_timer_non">
<div class="flex gap-2">
<x-input id="post_timer_non" name="post_timer_non" type="number" value="{{ $site->post_timer_non ?? 300 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
</div>
</div>
<!-- Fix Release Names -->
<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">Fix Release Names</h2>
<div class="space-y-4">
<x-form.group label="Fix Release Names" for="fix_names" help="Fix release names using NFOs, par2 files, filenames, md5 and sha1.">
<x-select id="fix_names" name="fix_names" class="w-full">
@foreach($yesno_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->fix_names ?? '') == $val ? 'selected' : '' }}>
{{ $yesno_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Fix Release Names Sleep Timer" for="fix_timer">
<div class="flex gap-2">
<x-input id="fix_timer" name="fix_timer" type="number" value="{{ $site->fix_timer ?? 60 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
</div>
</div>
<!-- Remove Crap Releases -->
<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">Remove Crap Releases</h2>
<div class="space-y-4">
<x-form.group label="Remove Crap Releases" for="fix_crap_opt" help="Remove passworded and other junk releases.">
<x-select id="fix_crap_opt" name="fix_crap_opt" class="w-full">
@foreach($yesno_ids as $index => $val)
<option value="{{ $val }}" {{ ($site->fix_crap_opt ?? '') == $val ? 'selected' : '' }}>
{{ $yesno_names[$index] }}
</option>
@endforeach
</x-select>
</x-form.group>
<x-form.group label="Remove Crap Sleep Timer" for="crap_timer">
<div class="flex gap-2">
<x-input id="crap_timer" name="crap_timer" type="number" value="{{ $site->crap_timer ?? 300 }}" class="flex-1" />
<span class="px-3 py-2 bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md text-sm">seconds</span>
</div>
</x-form.group>
</div>
</div>
<!-- Submit Button -->
<div class="flex justify-end">
<x-button type="submit" class="px-6 py-2">
<i class="fa fa-save mr-2"></i>Save Tmux Settings
</x-button>
</div>
</div>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,265 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-trash-restore mr-2"></i>{{ $title }}
</h1>
<a href="{{ url('admin/user-list') }}" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700">
<i class="fa fa-arrow-left mr-2"></i>Back to Active Users
</a>
</div>
</div>
<!-- Search Filters -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<form method="get" action="{{ url('admin/deleted-users') }}">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div>
<label for="username" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Username</label>
<input type="text"
id="username"
name="username"
value="{{ $username ?? '' }}"
placeholder="Filter by username"
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">
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email</label>
<input type="text"
id="email"
name="email"
value="{{ $email ?? '' }}"
placeholder="Filter by email"
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">
</div>
<div>
<label for="host" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Host/IP</label>
<input type="text"
id="host"
name="host"
value="{{ $host ?? '' }}"
placeholder="Filter by host"
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">
</div>
<div>
<label for="created_from" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Created From</label>
<input type="date"
id="created_from"
name="created_from"
value="{{ $created_from ?? '' }}"
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">
</div>
<div>
<label for="created_to" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Created To</label>
<input type="date"
id="created_to"
name="created_to"
value="{{ $created_to ?? '' }}"
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">
</div>
<div>
<label for="deleted_from" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Deleted From</label>
<input type="date"
id="deleted_from"
name="deleted_from"
value="{{ $deleted_from ?? '' }}"
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">
</div>
<div>
<label for="deleted_to" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Deleted To</label>
<input type="date"
id="deleted_to"
name="deleted_to"
value="{{ $deleted_to ?? '' }}"
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">
</div>
</div>
<div class="mt-4 flex gap-2">
<button type="submit" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-md hover:bg-blue-700">
<i class="fa fa-search mr-2"></i>Filter
</button>
<a href="{{ url('admin/deleted-users') }}" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-md hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Clear
</a>
</div>
</form>
</div>
<!-- Success/Error Messages -->
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 border border-green-200 rounded-lg">
<p class="text-green-800">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
<p class="text-red-800">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
<!-- Deleted Users Table -->
@if(count($deletedusers) > 0)
<form method="post" action="{{ url('admin/deleted-users/bulk') }}" id="bulkActionForm">
@csrf
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200">
<div class="flex items-center gap-4">
<label class="text-sm font-medium text-gray-700">Bulk Actions:</label>
<select name="action" id="bulkAction" class="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 focus:border-blue-500">
<option value="">Select Action</option>
<option value="restore">Restore Selected</option>
<option value="delete">Permanently Delete Selected</option>
</select>
<button type="submit"
class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-md hover:bg-blue-700"
onclick="return confirmBulkAction()">
<i class="fa fa-check mr-2"></i>Apply
</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left">
<input type="checkbox" id="selectAll" class="h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="{{ url('admin/deleted-users?ob=username_' . ($orderby == 'username_asc' ? 'desc' : 'asc') . ($queryString ? '&' . $queryString : '')) }}" class="hover:text-gray-700">
Username
@if(str_starts_with($orderby ?? '', 'username'))
<i class="fa fa-sort-{{ str_ends_with($orderby, 'desc') ? 'down' : 'up' }} ml-1"></i>
@endif
</a>
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="{{ url('admin/deleted-users?ob=email_' . ($orderby == 'email_asc' ? 'desc' : 'asc') . ($queryString ? '&' . $queryString : '')) }}" class="hover:text-gray-700">
Email
@if(str_starts_with($orderby ?? '', 'email'))
<i class="fa fa-sort-{{ str_ends_with($orderby, 'desc') ? 'down' : 'up' }} ml-1"></i>
@endif
</a>
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Host</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="{{ url('admin/deleted-users?ob=createdat_' . ($orderby == 'createdat_asc' ? 'desc' : 'asc') . ($queryString ? '&' . $queryString : '')) }}" class="hover:text-gray-700">
Created
@if(str_starts_with($orderby ?? '', 'createdat'))
<i class="fa fa-sort-{{ str_ends_with($orderby, 'desc') ? 'down' : 'up' }} ml-1"></i>
@endif
</a>
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a href="{{ url('admin/deleted-users?ob=deletedat_' . ($orderby == 'deletedat_asc' ? 'desc' : 'asc') . ($queryString ? '&' . $queryString : '')) }}" class="hover:text-gray-700">
Deleted
@if(str_starts_with($orderby ?? '', 'deletedat'))
<i class="fa fa-sort-{{ str_ends_with($orderby, 'desc') ? 'down' : 'up' }} ml-1"></i>
@endif
</a>
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-800 divide-y divide-gray-200">
@foreach($deletedusers as $user)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap">
<input type="checkbox" name="user_ids[]" value="{{ $user->id }}" class="user-checkbox h-4 w-4 text-blue-600 dark:text-blue-400 focus:ring-blue-500 border-gray-300 dark:border-gray-600 rounded">
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">{{ $user->username }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $user->email }}</td>
<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-800 text-gray-800">
{{ $user->rolename ?? 'N/A' }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $user->host ?? 'N/A' }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ $user->created_at ? $user->created_at->format('Y-m-d H:i') : 'N/A' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ $user->deleted_at ? $user->deleted_at->format('Y-m-d H:i') : 'N/A' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div class="flex gap-2">
<a href="{{ url('admin/deleted-users/restore/' . $user->id) }}"
class="text-green-600 hover:text-green-900"
title="Restore User"
onclick="return confirm('Are you sure you want to restore user \'{{ $user->username }}\'?')">
<i class="fa fa-undo"></i>
</a>
<a href="{{ url('admin/deleted-users/permanent-delete/' . $user->id) }}"
class="text-red-600 hover:text-red-900"
title="Permanently Delete"
onclick="return confirm('Are you sure you want to PERMANENTLY delete user \'{{ $user->username }}\'? This action cannot be undone!')">
<i class="fa fa-trash-alt"></i>
</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</form>
<!-- Pagination -->
<div class="px-6 py-4 border-t border-gray-200">
{{ $deletedusers->links() }}
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-trash-restore text-gray-400 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">There are no soft-deleted users matching your filters.</p>
</div>
@endif
</div>
</div>
<script>
// Select all checkbox functionality
document.getElementById('selectAll')?.addEventListener('change', function() {
const checkboxes = document.querySelectorAll('.user-checkbox');
checkboxes.forEach(cb => cb.checked = this.checked);
});
// Confirm bulk action
function confirmBulkAction() {
const action = document.getElementById('bulkAction').value;
const checkedBoxes = document.querySelectorAll('.user-checkbox:checked');
if (!action) {
alert('Please select an action.');
return false;
}
if (checkedBoxes.length === 0) {
alert('Please select at least one user.');
return false;
}
if (action === 'delete') {
return confirm(`Are you sure you want to PERMANENTLY delete ${checkedBoxes.length} user(s)? This action cannot be undone!`);
} else if (action === 'restore') {
return confirm(`Are you sure you want to restore ${checkedBoxes.length} user(s)?`);
}
return true;
}
</script>
@endsection
+207
View File
@@ -0,0 +1,207 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200">
<h1 class="text-2xl font-semibold text-gray-800">
<i class="fa fa-user mr-2"></i>{{ $title }}
</h1>
</div>
<!-- Error Messages -->
@if(!empty($error))
<div class="mx-6 mt-4 p-4 bg-red-50 border border-red-200 rounded-lg">
<p class="text-red-800">
<i class="fa fa-exclamation-circle mr-2"></i>{{ $error }}
</p>
</div>
@endif
<!-- User Form -->
<form method="post" action="{{ url('admin/user-edit') }}" class="p-6">
@csrf
<input type="hidden" name="action" value="submit">
@if(!empty($user['id']))
<input type="hidden" name="id" value="{{ $user['id'] }}">
@endif
<div class="space-y-6">
<!-- Username -->
<div>
<label for="username" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Username <span class="text-red-500">*</span>
</label>
<input type="text"
id="username"
name="username"
value="{{ is_array($user) ? ($user['username'] ?? '') : ($user->username ?? '') }}"
required
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">
</div>
<!-- Email -->
<div>
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Email <span class="text-red-500">*</span>
</label>
<input type="email"
id="email"
name="email"
value="{{ is_array($user) ? ($user['email'] ?? '') : ($user->email ?? '') }}"
required
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">
</div>
<!-- Password -->
<div>
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Password @if(empty($user['id']))<span class="text-red-500">*</span>@endif
</label>
<input type="password"
id="password"
name="password"
placeholder="{{ !empty($user['id']) ? 'Leave blank to keep current password' : '' }}"
@if(empty($user['id'])) required @endif
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">
@if(!empty($user['id']))
<p class="mt-1 text-sm text-gray-500">Leave blank to keep the current password</p>
@endif
</div>
<!-- Role -->
<div>
<label for="role" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Role <span class="text-red-500">*</span>
</label>
<select id="role"
name="role"
required
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">
@foreach($role_ids ?? [] as $index => $roleId)
<option value="{{ $roleId }}"
{{ (is_array($user) ? ($user['role'] ?? '') : ($user->roles->first()->id ?? '')) == $roleId ? 'selected' : '' }}>
{{ $role_names[$roleId] ?? '' }}
</option>
@endforeach
</select>
</div>
@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) }}"
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">
</div>
@endif
<!-- Invites -->
<div>
<label for="invites" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Invites
</label>
<input type="number"
id="invites"
name="invites"
value="{{ is_array($user) ? ($user['invites'] ?? 0) : ($user->invites ?? 0) }}"
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">
</div>
<!-- Notes -->
<div>
<label for="notes" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Notes
</label>
<textarea id="notes"
name="notes"
rows="4"
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">{{ 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">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">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">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">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">Books</label>
</div>
<div class="flex items-center">
<input type="checkbox"
id="xxxview"
name="xxxview"
value="1"
{{ (is_array($user) ? ($user['xxxview'] ?? 0) : ($user->xxxview ?? 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="xxxview" class="ml-2 text-sm text-gray-700">XXX</label>
</div>
</div>
</div>
@endif
<!-- Action Buttons -->
<div class="flex gap-3 pt-4 border-t border-gray-200">
<button type="submit" class="px-6 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700">
<i class="fa fa-save mr-2"></i>Save User
</button>
<a href="{{ url('admin/user-list') }}" class="px-6 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300">
<i class="fa fa-times mr-2"></i>Cancel
</a>
</div>
</div>
</form>
</div>
</div>
@endsection
+200
View File
@@ -0,0 +1,200 @@
@extends('layouts.admin')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
<!-- 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="fa fa-users mr-2"></i>{{ $title }}
</h1>
<a href="{{ url('admin/user-edit?action=add') }}" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-lg hover:bg-blue-700 dark:hover:bg-blue-800">
<i class="fa fa-plus mr-2"></i>Add New User
</a>
</div>
</div>
<!-- Search Filters -->
<div class="px-6 py-4 bg-gray-50 dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
<form method="get" action="{{ url('admin/user-list') }}">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div>
<label for="username" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Username</label>
<input type="text"
id="username"
name="username"
value="{{ $username ?? '' }}"
placeholder="Filter by username"
class="w-full px-3 py-2 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400">
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email</label>
<input type="text"
id="email"
name="email"
value="{{ $email ?? '' }}"
placeholder="Filter by email"
class="w-full px-3 py-2 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400">
</div>
<div>
<label for="host" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Host/IP</label>
<input type="text"
id="host"
name="host"
value="{{ $host ?? '' }}"
placeholder="Filter by host"
class="w-full px-3 py-2 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400">
</div>
<div>
<label for="role" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Role</label>
<select id="role"
name="role"
class="w-full px-3 py-2 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 border border-gray-300 dark:border-gray-600 rounded-md focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-blue-500 dark:focus:border-blue-400">
<option value="">All Roles</option>
@foreach($role_ids ?? [] as $index => $roleId)
<option value="{{ $roleId }}" {{ ($role ?? '') == $roleId ? 'selected' : '' }}>
{{ $role_names[$roleId] ?? '' }}
</option>
@endforeach
</select>
</div>
</div>
<div class="mt-4 flex gap-2">
<button type="submit" class="px-4 py-2 bg-blue-600 dark:bg-blue-700 text-white rounded-md hover:bg-blue-700 dark:hover:bg-blue-800">
<i class="fa fa-search mr-2"></i>Filter
</button>
<a href="{{ url('admin/user-list') }}" class="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-md hover:bg-gray-300 dark:hover:bg-gray-600">
<i class="fa fa-times mr-2"></i>Clear
</a>
</div>
</form>
</div>
<!-- Success/Error Messages -->
@if(request()->has('deleted') && request()->input('deleted') == 1)
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>
User "{{ request()->input('username') }}" has been deleted successfully.
</p>
</div>
@endif
@if(session('success'))
<div class="mx-6 mt-4 p-4 bg-green-50 dark:bg-green-900 border border-green-200 dark:border-green-700 rounded-lg">
<p class="text-green-800 dark:text-green-200">
<i class="fa fa-check-circle mr-2"></i>{{ session('success') }}
</p>
</div>
@endif
@if(session('error'))
<div class="mx-6 mt-4 p-4 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg">
<p class="text-red-800 dark:text-red-200">
<i class="fa fa-exclamation-circle mr-2"></i>{{ session('error') }}
</p>
</div>
@endif
<!-- User Table -->
@if(count($userlist) > 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">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">Role</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Host</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Country</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Verified</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">
@foreach($userlist as $user)
<tr class="hover:bg-gray-50 dark:hover:bg-gray-700 transition">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100">{{ $user->id }}</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">{{ $user->username }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ $user->email }}</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200">
{{ $user->roles->first()->name ?? 'N/A' }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{{ $user->host ?? 'N/A' }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
@if(!empty($user->country_code))
<span title="{{ $user->country_name }}">{{ $user->country_code }}</span>
@else
N/A
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap">
@if($user->verified)
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200">
<i class="fa fa-check mr-1"></i>Yes
</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-200">
<i class="fa fa-times mr-1"></i>No
</span>
@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' }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div class="flex gap-2">
<a href="{{ url('admin/user-edit?id=' . $user->id) }}"
class="text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300"
title="Edit">
<i class="fa fa-edit"></i>
</a>
@if(!$user->verified)
<a href="{{ url('admin/verify?id=' . $user->id) }}"
class="text-green-600 dark:text-green-400 hover:text-green-900 dark:hover:text-green-300"
title="Verify User"
onclick="return confirm('Are you sure you want to verify this user?')">
<i class="fa fa-check-circle"></i>
</a>
<a href="{{ url('admin/resendverification?id=' . $user->id) }}"
class="text-yellow-600 dark:text-yellow-400 hover:text-yellow-900 dark:hover:text-yellow-300"
title="Resend Verification">
<i class="fa fa-envelope"></i>
</a>
@endif
<a href="{{ url('admin/user-delete?id=' . $user->id) }}"
class="text-red-600 dark:text-red-400 hover:text-red-900 dark:hover:text-red-300"
title="Delete"
onclick="return confirm('Are you sure you want to delete user \'{{ $user->username }}\'?')">
<i class="fa fa-trash"></i>
</a>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="px-6 py-4 border-t border-gray-200">
{{ $userlist->links() }}
</div>
@else
<div class="px-6 py-12 text-center">
<i class="fa fa-users text-gray-400 text-5xl mb-4"></i>
<h3 class="text-lg font-medium text-gray-900 dark:text-gray-100 mb-2">No users found</h3>
<p class="text-gray-500">Try adjusting your search filters or add a new user.</p>
</div>
@endif
</div>
</div>
@endsection