Update admin area

This commit is contained in:
DariusIII
2025-10-05 13:02:31 +02:00
parent f70d77c620
commit d6f9a2ca15
7 changed files with 951 additions and 88 deletions
@@ -10,18 +10,11 @@ class AdminFailedReleasesController extends BasePageController
/**
* @throws \Exception
*/
public function index(): void
public function index()
{
$this->setAdminPrefs();
$title = 'Failed Releases List';
$releaselist = Release::getFailedRange();
$meta_title = $title = 'Failed Releases List';
$frellist = Release::getFailedRange();
$this->smarty->assign('releaselist', $frellist);
$content = $this->smarty->fetch('failrel-list.tpl');
$this->smarty->assign(compact('title', 'meta_title', 'content'));
$this->adminrender();
return view('admin.failrel-list', compact('title', 'releaselist'));
}
}
@@ -11,66 +11,42 @@ class AdminGroupController extends BasePageController
/**
* @throws \Exception
*/
public function index(Request $request): void
public function index(Request $request)
{
$this->setAdminPrefs();
$groupname = $request->input('groupname') ?? '';
$grouplist = UsenetGroup::getGroupsRange($groupname);
$title = 'Group List';
$groupName = $request->input('groupname') ?? '';
$this->smarty->assign(
[
'groupname' => $groupName,
'grouplist' => UsenetGroup::getGroupsRange($groupName),
]
);
$meta_title = $title = 'Group List';
$content = $this->smarty->fetch('group-list.tpl');
$this->smarty->assign(compact('title', 'meta_title', 'content'));
$this->adminrender();
return view('admin.group-list', compact('title', 'groupname', 'grouplist'));
}
/**
* @throws \Exception
*/
public function createBulk(Request $request): void
public function createBulk(Request $request)
{
$this->setAdminPrefs();
// set the current action
$action = $request->input('action') ?? 'view';
$groupmsglist = '';
if ($action === 'submit') {
if ($request->has('groupfilter') && ! empty($request->input('groupfilter'))) {
$msgs = UsenetGroup::addBulk($request->input('groupfilter'), $request->input('active'), $request->input('backfill'));
$groupmsglist = UsenetGroup::addBulk($request->input('groupfilter'), $request->input('active'), $request->input('backfill'));
}
} else {
$msgs = '';
}
$this->smarty->assign('groupmsglist', $msgs);
$this->smarty->assign('yesno_ids', [1, 0]);
$this->smarty->assign('yesno_names', ['Yes', 'No']);
$title = 'Bulk Add Newsgroups';
$meta_title = $title = 'Bulk Add Newsgroups';
$content = $this->smarty->fetch('group-bulk.tpl');
$this->smarty->assign(compact('title', 'meta_title', 'content'));
$this->adminrender();
return view('admin.group-bulk', compact('title', 'groupmsglist'));
}
/**
* @return \Illuminate\Http\RedirectResponse|void
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*
* @throws \Exception
*/
public function edit(Request $request)
{
$this->setAdminPrefs();
// Set the current action.
$action = $request->input('action') ?? 'view';
@@ -105,82 +81,51 @@ class AdminGroupController extends BasePageController
case 'view':
default:
$meta_title = $title = 'Group Edit';
$title = 'Group Edit';
if ($request->has('id')) {
$meta_title = $title = 'Newsgroup Edit';
$title = 'Newsgroup Edit';
$id = $request->input('id');
$group = UsenetGroup::getGroupByID($id);
} else {
$meta_title = $title = 'Newsgroup Add';
$title = 'Newsgroup Add';
}
break;
}
$this->smarty->assign('yesno_ids', [1, 0]);
$this->smarty->assign('yesno_names', ['Yes', 'No']);
$this->smarty->assign('group', $group);
$content = $this->smarty->fetch('group-edit.tpl');
$this->smarty->assign(compact('title', 'meta_title', 'content'));
$this->adminrender();
return view('admin.group-edit', compact('title', 'group'));
}
/**
* @throws \Exception
*/
public function active(Request $request): void
public function active(Request $request)
{
$this->setAdminPrefs();
$gname = '';
if (! empty($request->input('groupname'))) {
$gname = $request->input('groupname');
}
$groupname = ! empty($request->input('groupname')) ? $request->input('groupname') : '';
$this->smarty->assign('groupname', $groupname);
$grouplist = UsenetGroup::getGroupsRange($gname, true);
$title = 'Active Groups';
$this->smarty->assign('grouplist', $grouplist);
$meta_title = $title = 'Group List';
$content = $this->smarty->fetch('group-list.tpl');
$this->smarty->assign(compact('title', 'meta_title', 'content'));
$this->adminrender();
return view('admin.group-list', compact('title', 'groupname', 'grouplist'));
}
/**
* @throws \Exception
*/
public function inactive(Request $request): void
public function inactive(Request $request)
{
$this->setAdminPrefs();
$gname = '';
if (! empty($request->input('groupname'))) {
$gname = $request->input('groupname');
}
$groupname = ! empty($request->input('groupname')) ? $request->input('groupname') : '';
$this->smarty->assign('groupname', $groupname);
$grouplist = UsenetGroup::getGroupsRange($gname, false);
$title = 'Inactive Groups';
$this->smarty->assign('grouplist', $grouplist);
$meta_title = $title = 'Group List';
$content = $this->smarty->fetch('group-list.tpl');
$this->smarty->assign(compact('title', 'meta_title', 'content'));
$this->adminrender();
return view('admin.group-list', compact('title', 'groupname', 'grouplist'));
}
}
@@ -0,0 +1,160 @@
@extends('layouts.admin')
@section('title', $title ?? 'Failed Releases 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/release-list') }}" class="btn btn-outline-secondary">
<i class="fa fa-list me-2"></i>View All Releases
</a>
</div>
</div>
<div class="card-body">
<form name="failrelsearch" method="get" action="{{ url('/admin/failrel-list') }}" id="failrel-search-form" class="mb-4">
<div class="row">
<div class="col-md-6 offset-md-6">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search by name or category"
id="failrelsearch" name="failrelsearch" value="{{ request('failrelsearch') }}">
<button type="submit" class="btn btn-primary">
<i class="fa fa-search me-2"></i>Search
</button>
</div>
</div>
</div>
</form>
@if($releaselist && $releaselist->count() > 0)
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead class="thead-light">
<tr>
<th>Name</th>
<th>Category</th>
<th class="text-end">Size</th>
<th class="text-center">Files</th>
<th>Post Date</th>
<th>Add Date</th>
<th class="text-center">Grabs</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
@foreach($releaselist as $release)
<tr>
<td>
<a href="{{ url('/admin/release-edit?id=' . $release->guid) }}" class="title fw-semibold text-truncate d-inline-block" style="max-width: 300px;" title="{{ $release->name }}">
{{ $release->searchname }}
</a>
</td>
<td>
<span class="badge bg-secondary">{{ $release->category_name ?? 'Unknown' }}</span>
</td>
<td class="text-end">
<span class="badge bg-info">{{ human_filesize($release->size) }}</span>
</td>
<td class="text-center">
<a href="{{ url('/admin/release-files?id=' . $release->guid) }}" class="badge bg-primary">
<i class="fa fa-file me-1"></i>{{ $release->totalpart ?? 0 }}
</a>
</td>
<td>
<div class="d-flex align-items-center">
<i class="fa fa-calendar text-muted me-2"></i>
<span title="{{ $release->postdate }}">{{ \Carbon\Carbon::parse($release->postdate)->format('Y-m-d H:i') }}</span>
</div>
</td>
<td>
<div class="d-flex align-items-center">
<i class="fa fa-calendar-plus-o text-muted me-2"></i>
<span title="{{ $release->adddate }}">{{ \Carbon\Carbon::parse($release->adddate)->format('Y-m-d H:i') }}</span>
</div>
</td>
<td class="text-center">
<span class="badge bg-success">
<i class="fa fa-download me-1"></i>{{ $release->grabs ?? 0 }}
</span>
</td>
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<a href="{{ url('/admin/release-edit?id=' . $release->guid) }}" class="btn btn-primary" data-bs-toggle="tooltip" title="Edit release">
<i class="fa fa-pencil"></i>
</a>
@if($release->guid)
<a href="{{ url('/details/' . $release->guid) }}" class="btn btn-info" data-bs-toggle="tooltip" title="View release">
<i class="fa fa-eye"></i>
</a>
@endif
<a href="{{ url('/admin/release-delete/' . $release->guid) }}" class="btn btn-danger" data-bs-toggle="tooltip" 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>
@else
<div class="alert alert-info">
<i class="fa fa-info-circle me-2"></i>No failed releases available.
</div>
@endif
</div>
@if($releaselist && $releaselist->count() > 0)
<div class="card-footer">
<div class="d-flex justify-content-between align-items-center">
<div>
Showing {{ $releaselist->firstItem() }} to {{ $releaselist->lastItem() }} of {{ $releaselist->total() }} failed releases
</div>
<div class="pagination-container overflow-auto">
{{ $releaselist->onEachSide(5)->links() }}
</div>
</div>
</div>
@endif
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
});
</script>
<style>
/* Improve table responsiveness */
.table-responsive {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
/* Badge styling */
.badge {
font-weight: 500;
padding: 0.4em 0.6em;
}
/* Pagination container */
.pagination-container {
max-width: 100%;
}
/* Improve action buttons on small screens */
@media (max-width: 767.98px) {
.btn-group-sm .btn {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
}
}
</style>
@endsection
+186
View File
@@ -0,0 +1,186 @@
@extends('layouts.admin')
@section('title', $title ?? 'Bulk Add Newsgroups')
@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/group-list') }}" class="btn btn-outline-secondary">
<i class="fa fa-list me-2"></i>View All Groups
</a>
</div>
</div>
<div class="card-body">
@if(!empty($groupmsglist))
<div class="alert alert-info mb-4">
<i class="fa fa-info-circle me-2"></i>
The following groups have been processed. You can now view them in the group list.
</div>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead>
<tr>
<th>Group</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach($groupmsglist as $group)
<tr>
<td>
<div class="d-flex align-items-center">
<i class="fa fa-users text-muted me-2"></i>
<span class="fw-medium">{{ $group['group'] }}</span>
</div>
</td>
<td>
@if(strpos($group['msg'], 'Error') !== false)
<span class="badge bg-danger rounded-pill">
<i class="fa fa-exclamation-circle me-1"></i>{{ $group['msg'] }}
</span>
@elseif(strpos($group['msg'], 'exists') !== false)
<span class="badge bg-warning text-dark rounded-pill">
<i class="fa fa-exclamation-triangle me-1"></i>{{ $group['msg'] }}
</span>
@else
<span class="badge bg-success rounded-pill">
<i class="fa fa-check-circle me-1"></i>{{ $group['msg'] }}
</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="alert alert-info mb-4">
<i class="fa fa-info-circle me-2"></i>
Enter a regular expression to match multiple groups for bulk addition to the system.
</div>
<form action="{{ url('/admin/group-bulk?action=submit') }}" method="POST" id="groupBulkForm">
@csrf
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="groupfilter" class="form-label fw-bold">Group Pattern:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-filter"></i></span>
<textarea id="groupfilter" name="groupfilter" class="form-control" rows="5" placeholder="e.g. alt.binaries.cd.image.linux|alt.binaries.warez.linux"></textarea>
</div>
<small class="text-muted">
A regular expression to match against group names. Separate multiple patterns with the pipe symbol (|).
<br>Example: <code>alt.binaries.cd.image.linux|alt.binaries.warez.linux</code>
</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label class="form-label fw-bold">Active:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="active" id="active_yes" value="1" checked>
<label class="form-check-label" for="active_yes">Yes</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="active" id="active_no" value="0">
<label class="form-check-label" for="active_no">No</label>
</div>
<div class="mt-2">
<small class="text-muted">Inactive groups will not have headers downloaded for them.</small>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label class="form-label fw-bold">Backfill:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="backfill" id="backfill_yes" value="1">
<label class="form-check-label" for="backfill_yes">Yes</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="backfill" id="backfill_no" value="0" checked>
<label class="form-check-label" for="backfill_no">No</label>
</div>
<div class="mt-2">
<small class="text-muted">Inactive groups will not have backfill headers downloaded for them.</small>
</div>
</div>
</div>
</form>
@endif
</div>
<div class="card-footer">
<div class="d-flex justify-content-between">
<a href="{{ url('/admin/group-list') }}" class="btn btn-outline-secondary">
<i class="fa fa-arrow-left me-2"></i>Back to Groups
</a>
@if(empty($groupmsglist))
<button type="submit" form="groupBulkForm" class="btn btn-success">
<i class="fa fa-plus-circle me-2"></i>Add Groups
</button>
@else
<a href="{{ url('/admin/group-bulk') }}" class="btn btn-primary">
<i class="fa fa-plus-circle me-2"></i>Add More Groups
</a>
@endif
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Form validation
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 - could be expanded
try {
new RegExp(groupfilter);
} catch(e) {
event.preventDefault();
alert('Invalid regex pattern: ' + e.message);
return false;
}
});
}
});
</script>
<style>
/* Textarea styling */
textarea.form-control {
font-family: monospace;
font-size: 0.9rem;
}
/* Code blocks in help text */
code {
background-color: #f8f9fa;
padding: 0.2rem 0.4rem;
border-radius: 0.2rem;
color: #d63384;
}
</style>
@endsection
+189
View File
@@ -0,0 +1,189 @@
@extends('layouts.admin')
@section('title', $title ?? 'Group 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/group-list') }}" class="btn btn-outline-secondary">
<i class="fa fa-arrow-left me-2"></i>Back to Groups
</a>
</div>
</div>
@if(isset($error) && $error != '')
<div class="alert alert-danger m-3">
<i class="fa fa-exclamation-circle me-2"></i>{{ $error }}
</div>
@endif
<div class="card-body">
<form action="{{ url('/admin/group-edit?action=submit') }}" method="POST" id="groupForm">
@csrf
<input type="hidden" name="id" value="{{ $group['id'] ?? '' }}"/>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="name" class="form-label fw-bold">Group Name:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-users"></i></span>
<input type="text" id="name" name="name" class="form-control" value="{{ $group['name'] ?? '' }}"/>
</div>
<small class="text-muted">Changing the name to an invalid group will break things.</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>
<textarea id="description" name="description" class="form-control" rows="3">{{ $group['description'] ?? '' }}</textarea>
</div>
<small class="text-muted">Brief explanation of this group's content</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="backfill_target" class="form-label fw-bold">Backfill Days:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-calendar"></i></span>
<input type="number" id="backfill_target" name="backfill_target" class="form-control" value="{{ $group['backfill_target'] ?? 0 }}"/>
</div>
<small class="text-muted">Number of days to attempt to backfill this group. Adjust as necessary.</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="minfilestoformrelease" class="form-label fw-bold">Minimum Files To Form Release:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-file"></i></span>
<input type="number" id="minfilestoformrelease" name="minfilestoformrelease" class="form-control" value="{{ $group['minfilestoformrelease'] ?? 0 }}"/>
</div>
<small class="text-muted">The minimum number of files to make a release. If left blank, will use the site wide setting.</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 File 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="{{ $group['minsizetoformrelease'] ?? 0 }}"/>
</div>
<small class="text-muted">The minimum total size in bytes to make a release. If left blank, will use the site wide setting.</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="first_record" class="form-label fw-bold">First Record ID:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-angle-double-left"></i></span>
<input type="number" id="first_record" name="first_record" class="form-control" value="{{ $group['first_record'] ?? 0 }}"/>
</div>
<small class="text-muted">The oldest record number for the group.</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label for="last_record" class="form-label fw-bold">Last Record ID:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-angle-double-right"></i></span>
<input type="number" id="last_record" name="last_record" class="form-control" value="{{ $group['last_record'] ?? 0 }}"/>
</div>
<small class="text-muted">The newest record number for the group.</small>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label class="form-label fw-bold">Active:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="form-check form-check-inline">
<input type="radio" id="active_1" name="active" value="1" class="form-check-input" {{ (isset($group['active']) && $group['active'] == 1) ? 'checked' : '' }}>
<label for="active_1" class="form-check-label">Yes</label>
</div>
<div class="form-check form-check-inline">
<input type="radio" id="active_0" name="active" value="0" class="form-check-input" {{ (isset($group['active']) && $group['active'] == 0) ? 'checked' : '' }}>
<label for="active_0" class="form-check-label">No</label>
</div>
<div class="mt-2">
<small class="text-muted">Inactive groups will not have headers downloaded for them.</small>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-lg-3 col-md-4">
<label class="form-label fw-bold">Backfill:</label>
</div>
<div class="col-lg-9 col-md-8">
<div class="form-check form-check-inline">
<input type="radio" id="backfill_1" name="backfill" value="1" class="form-check-input" {{ (isset($group['backfill']) && $group['backfill'] == 1) ? 'checked' : '' }}>
<label for="backfill_1" class="form-check-label">Yes</label>
</div>
<div class="form-check form-check-inline">
<input type="radio" id="backfill_0" name="backfill" value="0" class="form-check-input" {{ (isset($group['backfill']) && $group['backfill'] == 0) ? 'checked' : '' }}>
<label for="backfill_0" class="form-check-label">No</label>
</div>
<div class="mt-2">
<small class="text-muted">If set to No, backfill will ignore this group. This works even if the above setting is No.</small>
</div>
</div>
</div>
</form>
</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/group-list') }}'">
<i class="fa fa-times me-2"></i>Cancel
</button>
<button type="submit" form="groupForm" class="btn btn-success">
<i class="fa fa-save me-2"></i>Save Changes
</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Form validation
const form = document.getElementById('groupForm');
form.addEventListener('submit', function(event) {
// Basic validation example - can be expanded
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>
@endsection
+373
View File
@@ -0,0 +1,373 @@
@extends('layouts.admin')
@section('title', $title ?? 'Group 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>
<div class="d-flex gap-2">
<a href="{{ url('/admin/group-list-active') }}" class="btn btn-sm btn-outline-primary">
<i class="fa fa-check-circle me-2"></i>Active Groups
</a>
<a href="{{ url('/admin/group-list-inactive') }}" class="btn btn-sm btn-outline-secondary">
<i class="fa fa-times-circle me-2"></i>Inactive Groups
</a>
<a href="{{ url('/admin/group-list') }}" class="btn btn-sm btn-outline-info">
<i class="fa fa-list me-2"></i>All Groups
</a>
<a href="{{ url('/admin/group-bulk') }}" class="btn btn-sm btn-success">
<i class="fa fa-plus-circle me-2"></i>Bulk Add
</a>
</div>
</div>
</div>
<div class="card-body">
<div class="alert alert-info mb-4">
<i class="fa fa-info-circle me-2"></i>
Below is a list of all usenet groups available to be indexed. Click 'Activate' to start indexing a group.
Backfill works independently of active.
</div>
@if(isset($msg) && $msg != '')
<div class="alert alert-success" id="message">{{ $msg }}</div>
@endif
@if($grouplist && $grouplist->count() > 0)
<div class="row mb-4">
<div class="col-lg-4 col-md-6">
<form name="groupsearch" method="GET" class="mb-0">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-search"></i></span>
<input id="groupname" type="text" name="groupname" value="{{ $groupname ?? '' }}" class="form-control" placeholder="Search for group...">
<button type="submit" class="btn btn-primary">Go</button>
</div>
</form>
</div>
<div class="col-lg-4 col-md-6 d-flex justify-content-center align-items-center">
<div class="pagination-container overflow-auto w-100 d-flex justify-content-center">
{{ $grouplist->onEachSide(5)->links() }}
</div>
</div>
<div class="col-lg-4 col-md-12 d-flex flex-column justify-content-center align-items-lg-end align-items-center mt-3 mt-lg-0">
<div class="btn-group" role="group">
<button type="button" class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#resetAllModal">
<i class="fa fa-refresh me-1"></i> Reset All
</button>
<button type="button" class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#purgeAllModal">
<i class="fa fa-trash me-1"></i> Purge All
</button>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover align-middle">
<thead class="thead-light">
<tr>
<th>Group</th>
<th>First Post</th>
<th>Last Post</th>
<th>Last Updated</th>
<th class="text-center">Status</th>
<th class="text-center">Backfill</th>
<th class="text-center">Releases</th>
<th class="text-center">Min Files</th>
<th class="text-center">Min Size</th>
<th class="text-center">Backfill Days</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
@foreach($grouplist as $group)
<tr id="grouprow-{{ $group->id }}">
<td>
<a href="{{ url('/admin/group-edit?id=' . $group->id) }}" class="text-decoration-none fw-semibold">{{ str_replace('alt.binaries', 'a.b', $group->name) }}</a>
@if($group->description)
<div class="text-muted small">{{ $group->description }}</div>
@endif
</td>
<td>
<div class="d-flex flex-column">
<span>{{ $group->first_record_postdate }}</span>
<small class="text-muted">{{ \Carbon\Carbon::parse($group->first_record_postdate)->diffForHumans() }}</small>
</div>
</td>
<td>{{ $group->last_record_postdate }}</td>
<td>
<span data-bs-toggle="tooltip" title="{{ $group->last_updated }}">{{ \Carbon\Carbon::parse($group->last_updated)->diffForHumans() }}</span>
</td>
<td class="text-center" id="group-{{ $group->id }}">
@if($group->active == 1)
<button type="button" onclick="ajax_group_status({{ $group->id }}, 0)" class="btn btn-sm btn-success">
<i class="fa fa-check-circle me-1"></i>Active
</button>
@else
<button type="button" onclick="ajax_group_status({{ $group->id }}, 1)" class="btn btn-sm btn-outline-secondary">
<i class="fa fa-times-circle me-1"></i>Inactive
</button>
@endif
</td>
<td class="text-center" id="backfill-{{ $group->id }}">
@if($group->backfill == 1)
<button type="button" onclick="ajax_backfill_status({{ $group->id }}, 0)" class="btn btn-sm btn-info">
<i class="fa fa-check-circle me-1"></i>Enabled
</button>
@else
<button type="button" onclick="ajax_backfill_status({{ $group->id }}, 1)" class="btn btn-sm btn-outline-secondary">
<i class="fa fa-times-circle me-1"></i>Disabled
</button>
@endif
</td>
<td class="text-center">
<span class="badge bg-secondary">{{ $group->num_releases ?? 0 }}</span>
</td>
<td class="text-center">
@if(empty($group->minfilestoformrelease))
<span class="text-muted">n/a</span>
@else
<span class="badge bg-secondary">{{ $group->minfilestoformrelease }}</span>
@endif
</td>
<td class="text-center">
@if(empty($group->minsizetoformrelease))
<span class="text-muted">n/a</span>
@else
<span class="badge bg-secondary">{{ human_filesize($group->minsizetoformrelease) }}</span>
@endif
</td>
<td class="text-center">
<span class="badge bg-secondary">{{ $group->backfill_target }}</span>
</td>
<td class="text-center" id="groupdel-{{ $group->id }}">
<div class="btn-group btn-group-sm" role="group">
<a href="{{ url('/admin/group-edit?id=' . $group->id) }}" class="btn btn-primary" data-bs-toggle="tooltip" title="Edit this group">
<i class="fa fa-pencil"></i>
</a>
<button type="button" onclick="ajax_group_reset({{ $group->id }})" class="btn btn-warning" data-bs-toggle="tooltip" title="Reset this group">
<i class="fa fa-refresh"></i>
</button>
<button type="button" onclick="confirmGroupDelete({{ $group->id }})" class="btn btn-danger" data-bs-toggle="tooltip" title="Delete this group">
<i class="fa fa-trash"></i>
</button>
<button type="button" onclick="confirmGroupPurge({{ $group->id }})" class="btn btn-danger" data-bs-toggle="tooltip" title="Purge this group">
<i class="fa fa-eraser"></i>
</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@else
<div class="alert alert-warning">
<i class="fa fa-exclamation-triangle me-2"></i>No groups available (eg. none have been added).
</div>
@endif
</div>
@if($grouplist && $grouplist->count() > 0)
<div class="card-footer">
<div class="row">
<div class="col-lg-4 col-md-6">
<form name="groupsearch" method="GET" class="mb-0">
<div class="input-group">
<span class="input-group-text"><i class="fa fa-search"></i></span>
<input id="groupname" type="text" name="groupname" value="{{ $groupname ?? '' }}" class="form-control" placeholder="Search for group...">
<button type="submit" class="btn btn-primary">Go</button>
</div>
</form>
</div>
<div class="col-lg-4 col-md-6 d-flex justify-content-center align-items-center mt-3 mt-lg-0">
<div class="pagination-container overflow-auto w-100 d-flex justify-content-center">
{{ $grouplist->onEachSide(5)->links() }}
</div>
</div>
<div class="col-lg-4 col-md-12 d-flex justify-content-lg-end justify-content-center mt-3 mt-lg-0">
<div class="text-muted">
Showing {{ $grouplist->count() }} of {{ $grouplist->total() }} groups
</div>
</div>
</div>
</div>
@endif
</div>
<!-- Reset All Confirmation Modal -->
<div class="modal fade" id="resetAllModal" tabindex="-1" aria-labelledby="resetAllModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="resetAllModalLabel">Confirm Reset All Groups</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="text-danger"><i class="fa fa-exclamation-triangle me-2"></i>Are you sure you want to reset all groups?</p>
<p>This will reset the article pointers for all groups back to their current state.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-warning" onclick="ajax_group_reset_all()">Reset All</button>
</div>
</div>
</div>
</div>
<!-- Purge All Confirmation Modal -->
<div class="modal fade" id="purgeAllModal" tabindex="-1" aria-labelledby="purgeAllModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="purgeAllModalLabel">Confirm Purge All Groups</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p class="text-danger"><i class="fa fa-exclamation-triangle me-2"></i>Are you sure you want to purge all groups?</p>
<p>This will delete all releases and binaries for all groups. This action cannot be undone!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" onclick="ajax_group_purge_all()">Purge All</button>
</div>
</div>
</div>
</div>
<script>
function ajax_group_status(id, status) {
$.ajax({
type: 'POST',
url: '{{ url('/admin/ajax') }}',
data: {
_token: '{{ csrf_token() }}',
action: 'toggle_group_active_status',
group_id: id,
group_status: status
},
success: function(data) {
if (data.success) {
$('#group-' + id).html(data.html);
}
}
});
}
function ajax_backfill_status(id, status) {
$.ajax({
type: 'POST',
url: '{{ url('/admin/ajax') }}',
data: {
_token: '{{ csrf_token() }}',
action: 'toggle_group_backfill',
group_id: id,
backfill: status
},
success: function(data) {
if (data.success) {
$('#backfill-' + id).html(data.html);
}
}
});
}
function ajax_group_reset(id) {
if (confirm('Are you sure you want to reset this group?')) {
$.ajax({
type: 'POST',
url: '{{ url('/admin/ajax') }}',
data: {
_token: '{{ csrf_token() }}',
action: 'reset_group',
group_id: id
},
success: function(data) {
if (data.success) {
location.reload();
}
}
});
}
}
function confirmGroupDelete(id) {
if (confirm('Are you sure you want to delete this group?')) {
$.ajax({
type: 'POST',
url: '{{ url('/admin/ajax') }}',
data: {
_token: '{{ csrf_token() }}',
action: 'delete_group',
group_id: id
},
success: function(data) {
if (data.success) {
$('#grouprow-' + id).fadeOut();
}
}
});
}
}
function confirmGroupPurge(id) {
if (confirm('Are you sure you want to purge this group? This will delete all releases and binaries!')) {
$.ajax({
type: 'POST',
url: '{{ url('/admin/ajax') }}',
data: {
_token: '{{ csrf_token() }}',
action: 'purge_group',
group_id: id
},
success: function(data) {
if (data.success) {
location.reload();
}
}
});
}
}
function ajax_group_reset_all() {
$.ajax({
type: 'POST',
url: '{{ url('/admin/ajax') }}',
data: {
_token: '{{ csrf_token() }}',
action: 'reset_all_groups'
},
success: function(data) {
if (data.success) {
$('#resetAllModal').modal('hide');
location.reload();
}
}
});
}
function ajax_group_purge_all() {
$.ajax({
type: 'POST',
url: '{{ url('/admin/ajax') }}',
data: {
_token: '{{ csrf_token() }}',
action: 'purge_all_groups'
},
success: function(data) {
if (data.success) {
$('#purgeAllModal').modal('hide');
location.reload();
}
}
});
}
// Initialize tooltips
$(function () {
$('[data-bs-toggle="tooltip"]').tooltip();
});
</script>
@endsection
+18 -1
View File
@@ -47,7 +47,7 @@
</button>
<div id="releases-menu" class="hidden mt-2 ml-6 space-y-1">
<a href="{{ url('/admin/release-list') }}" class="block py-2 px-3 text-gray-400 hover:text-white hover:bg-gray-800 rounded transition">Release List</a>
<a href="{{ url('/admin/failed-releases') }}" class="block py-2 px-3 text-gray-400 hover:text-white hover:bg-gray-800 rounded transition">Failed Releases</a>
<a href="{{ url('/admin/failrel-list') }}" class="block py-2 px-3 text-gray-400 hover:text-white hover:bg-gray-800 rounded transition">Failed Releases</a>
<a href="{{ url('/admin/category-list') }}" class="block py-2 px-3 text-gray-400 hover:text-white hover:bg-gray-800 rounded transition">Categories</a>
</div>
</div>
@@ -82,6 +82,23 @@
</div>
</div>
<!-- Groups -->
<div class="mb-4">
<button type="button" class="flex items-center justify-between w-full text-left text-gray-300 hover:text-white transition" onclick="toggleAdminSubmenu('groups-menu')">
<div class="flex items-center space-x-3">
<i class="fas fa-server"></i>
<span>Groups Management</span>
</div>
<i class="fas fa-chevron-down text-sm transform transition-transform" id="groups-menu-icon"></i>
</button>
<div id="groups-menu" class="hidden mt-2 ml-6 space-y-1">
<a href="{{ url('/admin/group-list') }}" class="block py-2 px-3 text-gray-400 hover:text-white hover:bg-gray-800 rounded transition">All Groups</a>
<a href="{{ url('/admin/group-list-active') }}" class="block py-2 px-3 text-gray-400 hover:text-white hover:bg-gray-800 rounded transition">Active Groups</a>
<a href="{{ url('/admin/group-list-inactive') }}" class="block py-2 px-3 text-gray-400 hover:text-white hover:bg-gray-800 rounded transition">Inactive Groups</a>
<a href="{{ url('/admin/group-bulk') }}" class="block py-2 px-3 text-gray-400 hover:text-white hover:bg-gray-800 rounded transition">Bulk Add Groups</a>
</div>
</div>
<!-- System -->
<div class="mb-4">
<button type="button" class="flex items-center justify-between w-full text-left text-gray-300 hover:text-white transition" onclick="toggleAdminSubmenu('system-menu')">