add: watchlist staff tool

- https://streamable.com/gdm4wr
This commit is contained in:
HDVinnie
2021-03-08 18:49:55 -05:00
parent 553b034ae4
commit 48aef0768a
11 changed files with 429 additions and 1 deletions
@@ -0,0 +1,83 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Controllers\Staff;
use App\Models\Watchlist;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
/**
* @see \Tests\Todo\Feature\Http\Controllers\WatchlistControllerTest
*/
class WatchlistController extends Controller
{
/**
* Watchlist.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
final public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
{
return \view('Staff.watchlist.index');
}
/**
* Store A New Watched User.
*
* @param \Illuminate\Http\Request $request
* @param int $id
*
* @return \Illuminate\Http\RedirectResponse
*/
final public function store(Request $request, int $id): \Illuminate\Http\RedirectResponse
{
$watchedUser = new Watchlist();
$watchedUser->user_id = $id;
$watchedUser->staff_id = $request->user()->id;
$watchedUser->message = $request->input('message');
$v = \validator($watchedUser->toArray(), [
'user_id' => 'required|exists:users,id',
'staff_id' => 'required|exists:users,id',
'message' => 'required|min:3',
]);
if ($v->fails()) {
return \redirect()->route('staff.watchlist.index')
->withErrors($v->errors());
}
$watchedUser->save();
return \redirect()->route('staff.watchlist.index')
->withSuccess('User Successfully Being Watched');
}
/**
* Delete A Watched User.
*
* @param $id
*
* @throws \Exception
*
* @return \Illuminate\Http\RedirectResponse
*/
final public function destroy(int $id): \Illuminate\Http\RedirectResponse
{
$watchedUser = Watchlist::findOrFail($id);
$watchedUser->delete();
return \redirect()->route('staff.watchlist.index')
->withSuccess('Successfully Stopped Watching User');
}
}
+72
View File
@@ -0,0 +1,72 @@
<?php
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Watchlist;
use Livewire\WithPagination;
class WatchlistSearch extends Component
{
use WithPagination;
public $user;
public $perPage = 25;
public $searchTerm = '';
public $sortField = 'created_at';
public $sortDirection = 'desc';
final public function paginationView()
{
return 'vendor.pagination.livewire-pagination';
}
final public function updatingSearchTerm()
{
$this->resetPage();
}
final public function mount()
{
$this->user = \auth()->user();
}
final public function getUsersProperty()
{
return Watchlist::query()
->with(['user', 'author'])
->when($this->searchTerm, function ($query) {
return $query->where('message', 'LIKE', '%'.$this->searchTerm.'%');
})
->orderBy($this->sortField, $this->sortDirection)
->paginate($this->perPage);
}
final public function sortBy($field)
{
if ($this->sortField === $field) {
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
} else {
$this->sortDirection = 'asc';
}
$this->sortField = $field;
}
final public function render(): \Illuminate\Contracts\View\Factory | \Illuminate\Contracts\View\View | \Illuminate\Contracts\Foundation\Application
{
return \view('livewire.watchlist-search', [
'watchedUsers' => $this->users
]);
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Watchlist extends Model
{
use HasFactory;
/**
* Belongs To A User.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class, 'user_id', 'id')->withDefault([
'username' => 'System',
'id' => '1',
]);
}
/**
* Belongs To A Uploader.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function author()
{
// Not needed yet but may use this soon.
return $this->belongsTo(User::class, 'staff_id', 'id')->withDefault([
'username' => 'System',
'id' => '1',
]);
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWatchlistsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('watchlists', function (Blueprint $table) {
$table->id();
$table->integer('user_id')->unique()->index();
$table->integer('staff_id')->index();
$table->text('message');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('watchlists');
}
}
@@ -0,0 +1,44 @@
@extends('layout.default')
@section('title')
<title>Watchlist @lang('common.search') - @lang('staff.staff-dashboard') - {{ config('other.title') }}</title>
@endsection
@section('meta')
<meta name="description" content="Watchlist Search - @lang('staff.staff-dashboard')">
@endsection
@section('breadcrumb')
<li>
<a href="{{ route('staff.dashboard.index') }}" itemprop="url" class="l-breadcrumb-item-link">
<span itemprop="title" class="l-breadcrumb-item-link-title">@lang('staff.staff-dashboard')</span>
</a>
</li>
<li>
<a href="{{ route('staff.watchlist.index') }}" itemprop="url" class="l-breadcrumb-item-link">
<span itemprop="title" class="l-breadcrumb-item-link-title">Watchlist @lang('common.search')</span>
</a>
</li>
@endsection
@section('content')
<style>
td {
vertical-align: middle !important;
}
</style>
<div class="container">
<div class="block">
<div class="header gradient silver">
<div class="inner_content">
<div class="page-title">
<h1 style="margin: 0;">Watched Users</h1>
</div>
</div>
</div>
</div>
</div>
<div class="box container">
@livewire('watchlist-search')
</div>
@endsection
@@ -0,0 +1,88 @@
<div>
<div class="mb-10 form-inline pull-right">
<div class="form-group">
@lang('common.quantity')
<select wire:model="perPage" class="form-control">
<option>25</option>
<option>50</option>
<option>100</option>
</select>
</div>
<div class="form-group">
<input type="text" wire:model="searchTerm" class="form-control" style="width: 275px;" placeholder="Search by message"/>
</div>
</div>
<div class="box-body no-padding">
<table class="table vertical-align table-hover">
<tbody>
<tr>
<th style="width: 15%;">
<div sortable wire:click="sortBy('user_id')" :direction="$sortField === 'user_id' ? $sortDirection : null" role="button">
Watching
@include('livewire.includes._sort-icon', ['field' => 'user_id'])
</div>
</th>
<th style="width: 15%;">
<div sortable wire:click="sortBy('staff_id')" :direction="$sortField === 'staff_id' ? $sortDirection : null" role="button">
Watched By
@include('livewire.includes._sort-icon', ['field' => 'staff_id'])
</div>
</th>
<th style="width: 40%;" class="hidden-sm hidden-xs">
<div sortable wire:click="sortBy('message')" :direction="$sortField === 'message' ? $sortDirection : null"
role="button">
Message
@include('livewire.includes._sort-icon', ['field' => 'message'])
</div>
</th>
<th style="width: 15%;">
<div sortable wire:click="sortBy('created_at')" :direction="$sortField === 'created_at' ? $sortDirection : null" role="button">
Created At
@include('livewire.includes._sort-icon', ['field' => 'created_at'])
</div>
</th>
<th style="width: 15%;">@lang('common.action')</th>
</tr>
@foreach ($watchedUsers as $watching)
<tr>
<td>
<a href="{{ route('users.show', ['username' => $watching->user->username]) }}">
<span class="badge-user text-bold">
{{ $watching->user->username }}
</span>
</a>
</td>
<td>
<a href="{{ route('users.show', ['username' => $watching->author->username]) }}">
<span class="badge-user text-bold">
{{ $watching->author->username }}
</span>
</a>
</td>
<td class="hidden-sm hidden-xs">{{ $watching->message }}</td>
<td class="hidden-sm hidden-xs">{{ $watching->created_at }}</td>
<td>
<form action="{{ route('staff.watchlist.destroy', ['id' => $watching->id]) }}" method="POST">
@csrf
@method('DELETE')
<button class="btn btn-xs btn-info" type="submit">
<i class="{{ config('other.font-awesome') }} fa-eye-slash"></i> Unwatch
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
@if (! $watchedUsers->count())
<div class="margin-10">
@lang('common.no-result')
</div>
@endif
<br>
<div class="text-center">
{{ $watchedUsers->links() }}
</div>
</div>
</div>
@@ -136,6 +136,11 @@
<i class="{{ config('other.font-awesome') }} fa-users"></i> @lang('staff.user-search')
</a>
</li>
<li>
<a href="{{ route('staff.watchlist.index') }}">
<i class="{{ config('other.font-awesome') }} fa-eye"></i> Watchlist
</a>
</li>
<li>
<a href="{{ route('staff.gifts.index') }}">
<i class="{{ config('other.font-awesome') }} fa-gift"></i> @lang('staff.user-gifting')
+1 -1
View File
@@ -338,7 +338,7 @@
@if ($torrent->anon !== 1 && $uploader->private_profile !== 1)
@if (auth()->user()->isFollowing($uploader->id))
<form class="form-inline" role="form"action="{{ route('follow.destroy', ['username' => $uploader->username]) }}"
<form class="form-inline" role="form" action="{{ route('follow.destroy', ['username' => $uploader->username]) }}"
style="display: inline-block;" method="POST">
@csrf
@method('DELETE')
+18
View File
@@ -79,6 +79,12 @@
</i>
</a>
@endif
@php $watched = App\Models\Watchlist::whereUserId($user->id)->first(); @endphp
@if ($watched && auth()->user()->group->is_modo)
<i class="{{ config('other.font-awesome') }} fa-eye fa-beat text-danger" aria-hidden="true" data-toggle="tooltip"
title="" data-original-title="User is being watched!">
</i>
@endif
</h2>
<h4>@lang('common.group'): <span class="badge-user text-bold"
style="color:{{ $user->group->color }}; background-image:{{ $user->group->effect }};"><i
@@ -92,6 +98,18 @@
<button class="btn btn-xs btn-warning" data-toggle="modal"
data-target="#modal_user_note"><span
class="{{ config('other.font-awesome') }} fa-sticky-note"></span> @lang('user.note') </button>
@if(! $watched)
<button class="btn btn-xs btn-danger" data-toggle="modal" data-target="#modal_user_watch">
<span class="{{ config('other.font-awesome') }} fa-eye"></span> Watch </button>
@else
<form style="display: inline;" action="{{ route('staff.watchlist.destroy', ['id' => $watched->id]) }}" method="POST">
@csrf
@method('DELETE')
<button class="btn btn-xs btn-warning" type="submit">
<i class="{{ config('other.font-awesome') }} fa-eye-slash"></i> Unwatch
</button>
</form>
@endif
@if ($user->group->id == 5)
<button class="btn btn-xs btn-warning" data-toggle="modal"
data-target="#modal_user_unban"><span
@@ -225,3 +225,39 @@
</div>
</div>
</div>
<div class="modal fade" id="modal_user_watch" tabindex="-1" role="dialog" aria-labelledby="modal_user_gift"
aria-hidden="true">
<div class="modal-dialog modal-dark modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header mx-auto">
<div class="text-center">
<p style="font-size: 27px;">Watch User: {{ $user->username }}</p>
</div>
</div>
<div class="modal-body">
<div class="py-3">
<form role="form" method="POST"
action="{{ route('staff.watchlist.store', ['id' => $user->id]) }}">
@csrf
<div class="form-group">
<label for="report_reason">Reason</label>
</div>
<div class="form-group">
<label for="message"></label>
<textarea class="form-control" rows="5" name="message" cols="50" id="message"></textarea>
</div>
<div class="form-group">
<input class="btn btn-danger" type="submit" value="Submit">
</div>
</form>
</div>
</div>
<div class="modal-footer">
<button type="button" class="close ml-auto" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
</div>
+9
View File
@@ -907,5 +907,14 @@ Route::group(['middleware' => 'language'], function () {
Route::get('/', 'WarningController@index')->name('index');
});
});
// Watchlist
Route::group(['prefix' => 'watchlist'], function () {
Route::name('staff.watchlist.')->group(function () {
Route::get('/', 'WatchlistController@index')->name('index');
Route::post('/{id}/store', 'WatchlistController@store')->name('store');
Route::delete('/{id}/destroy', 'WatchlistController@destroy')->name('destroy');
});
});
});
});