refactor: revamp devtools page

This commit refactors the development tools page to make it look better. Additionally, it makes small adjustments in the notifications and corrects missing strings from the page.
This commit is contained in:
Miguel Nogueira 2022-04-04 09:59:18 +01:00
parent 2ddfb62f17
commit 21fdb349d9
9 changed files with 70 additions and 37 deletions

View File

@ -82,7 +82,7 @@ class AbsenceController extends Controller
{ {
$this->authorize('create', Absence::class); $this->authorize('create', Absence::class);
if ($this->hasActiveRequest(Auth::user())) { if ($this->absenceService->hasActiveRequest(Auth::user())) {
return redirect() return redirect()
->back() ->back()
->with('error', __('You already have an active request. Cancel it or let it expire first.')); ->with('error', __('You already have an active request. Cancel it or let it expire first.'));

View File

@ -24,10 +24,12 @@ namespace App\Http\Controllers;
use App\Application; use App\Application;
use App\Events\ApplicationApprovedEvent; use App\Events\ApplicationApprovedEvent;
use App\Events\ApplicationDeniedEvent; use App\Events\ApplicationDeniedEvent;
use App\Services\AbsenceService;
use App\Services\AccountSuspensionService; use App\Services\AccountSuspensionService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class DevToolsController extends Controller class DevToolsController extends Controller
{ {
@ -105,4 +107,16 @@ class DevToolsController extends Controller
->with('error', __('There were no expired suspensions (or no suspensions at all) to purge.')); ->with('error', __('There were no expired suspensions (or no suspensions at all) to purge.'));
} }
public function endAbsencesNow(AbsenceService $service)
{
$this->singleAuthorise();
$service->endExpired();
Log::alert('(absence cleaner) Forcefully started absence expiration check!');
return redirect()
->back()
->with('success', 'Cleaned up expired absences.');
}
} }

View File

@ -49,7 +49,6 @@ class AbsenceRequestCancelled extends Notification implements ShouldQueue
->subject(config('app.name').' - absence request cancelled') ->subject(config('app.name').' - absence request cancelled')
->line("This notification confirms that your recent Leave of Absence from {$this->absence->created_at} has just been cancelled by you.") ->line("This notification confirms that your recent Leave of Absence from {$this->absence->created_at} has just been cancelled by you.")
->line('Please note that any inactivity will be counted in our activity metrics. You may also make a new request if you wish.') ->line('Please note that any inactivity will be counted in our activity metrics. You may also make a new request if you wish.')
->action('View your request', url(route('absences.show', ['absence' => $this->absence->id])))
->action('Send new request', url(route('absences.create'))) ->action('Send new request', url(route('absences.create')))
->salutation('The team at ' . config('app.name')); ->salutation('The team at ' . config('app.name'));
} }

View File

@ -49,7 +49,6 @@ class AbsenceRequestDeclined extends Notification implements ShouldQueue
->subject(config('app.name').' - absence request declined') ->subject(config('app.name').' - absence request declined')
->line("Your recent Leave of Absence request from {$this->absence->created_at} has just been declined by an admin.") ->line("Your recent Leave of Absence request from {$this->absence->created_at} has just been declined by an admin.")
->line('Please note that any inactivity will be counted in our activity metrics. You may make a new request, but we recommend you ask your team lead regarding your declined request.') ->line('Please note that any inactivity will be counted in our activity metrics. You may make a new request, but we recommend you ask your team lead regarding your declined request.')
->action('View your request', url(route('absences.show', ['absence' => $this->absence->id])))
->action('Send new request', url(route('absences.create'))) ->action('Send new request', url(route('absences.create')))
->salutation('The team at ' . config('app.name')); ->salutation('The team at ' . config('app.name'));
} }

View File

@ -49,7 +49,6 @@ class AbsenceRequestEnded extends Notification implements ShouldQueue
->subject(config('app.name').' - absence request expired') ->subject(config('app.name').' - absence request expired')
->line("Your Leave of Absence request from {$this->absence->created_at} (until {$this->absence->predicted_end}) has expired today.") ->line("Your Leave of Absence request from {$this->absence->created_at} (until {$this->absence->predicted_end}) has expired today.")
->line('Please note that any inactivity will be counted in our activity metrics. You may now make a new request if you still need more time.') ->line('Please note that any inactivity will be counted in our activity metrics. You may now make a new request if you still need more time.')
->action('View expired request', url(route('absences.show', ['absence' => $this->absence->id])))
->action('Send new request', url(route('absences.create'))) ->action('Send new request', url(route('absences.create')))
->salutation('The team at ' . config('app.name')); ->salutation('The team at ' . config('app.name'));
} }

View File

@ -49,7 +49,7 @@ class AbsenceService
{ {
$absence = Absence::create([ $absence = Absence::create([
'requesterID' => $user->id, 'requesterID' => $requester->id,
'start' => $request->start_date, 'start' => $request->start_date,
'predicted_end' => $request->predicted_end, 'predicted_end' => $request->predicted_end,
'available_assist' => $request->available_assist == "on", 'available_assist' => $request->available_assist == "on",
@ -62,7 +62,7 @@ class AbsenceService
} }
Log::info('Processing new leave of absence request.', [ Log::info('Processing new leave of absence request.', [
'requesting_user' => $user->email, 'requesting_user' => $requester->email,
'absenceid' => $absence->id, 'absenceid' => $absence->id,
'reason' => $request->reason 'reason' => $request->reason
]); ]);
@ -78,7 +78,7 @@ class AbsenceService
* @return Absence The approved absence. * @return Absence The approved absence.
* @throws AbsenceNotActionableException * @throws AbsenceNotActionableException
*/ */
public function approveAbsence(Absence $absence): Absence public function approveAbsence(Absence $absence)
{ {
Log::info('An absence request has just been approved.', [ Log::info('An absence request has just been approved.', [
'absenceid' => $absence->id, 'absenceid' => $absence->id,
@ -99,7 +99,7 @@ class AbsenceService
* @return Absence The declined absence. * @return Absence The declined absence.
* @throws AbsenceNotActionableException * @throws AbsenceNotActionableException
*/ */
public function declineAbsence(Absence $absence): Absence public function declineAbsence(Absence $absence)
{ {
Log::warning('An absence request has just been declined.', [ Log::warning('An absence request has just been declined.', [
'absenceid' => $absence->id, 'absenceid' => $absence->id,
@ -138,7 +138,7 @@ class AbsenceService
* @param Absence $absence * @param Absence $absence
* @return bool * @return bool
*/ */
public function endAbsence(Absence $absence): Absence public function endAbsence(Absence $absence)
{ {
Log::info('An absence request has just expired.', [ Log::info('An absence request has just expired.', [
'absenceid' => $absence->id, 'absenceid' => $absence->id,

View File

@ -14,10 +14,10 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
{{ $modalFooter }} {{ $modalFooter ?? '' }}
@if ($includeCloseButton == true) @if ($includeCloseButton == true)
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{__('messages.modal_close')}}</button> <button type="button" class="btn btn-secondary" data-dismiss="modal">{{__('Close')}}</button>
@endif @endif
</div> </div>
</div> </div>

View File

@ -14,7 +14,7 @@
@section('content') @section('content')
<x-modal id="confirmForceEventDispatch" modal-label="confirmForceEventDispatch" modal-title="{{__('messages.choose_app')}}" include-close-button="true"> <x-modal id="confirmForceEventDispatch" modal-label="confirmForceEventDispatch" modal-title="{{__('Choose an application to override')}}" include-close-button="true">
<p>{{__('Please choose an application to force approve')}}</p> <p>{{__('Please choose an application to force approve')}}</p>
<p>{{ __('Note that this process overrides users\'s votes.') }}</p> <p>{{ __('Note that this process overrides users\'s votes.') }}</p>
@ -35,7 +35,7 @@
</form> </form>
<x-slot name="modalFooter"> <x-slot name="modalFooter">
<button type="button" class="btn btn-danger" onclick="document.getElementById('forceEval').submit()">{{__('messages.dispatch_event')}}</button> <button type="button" class="btn btn-danger" onclick="document.getElementById('forceEval').submit()">{{__('Override now')}}</button>
</x-slot> </x-slot>
</x-modal> </x-modal>
@ -70,45 +70,64 @@
<div class="col"> <div class="col">
<div class="alert alert-warning"> <div class="alert alert-warning">
<i class="fa fa-exclamation-triangle"></i> <b>{{__('Warning')}}</b> <i class="fa fa-exclamation-triangle"></i> <b>{{__('Warning')}}</b>
<p>{{__('These tools were intended for development purposes. Unless you know exactly what each command does, we recommend you don\'t use any of them.')}}</p>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row mt-5">
<div class="col">
<div class="col text-center"> <x-card id="appCommands" card-title="{{ __('Application-specific commands') }}" footer-style="text-muted">
<x-card id="tools" card-title="Commands & Actions" footer-style="text-center">
<x-slot name="cardHeader"> <x-slot name="cardHeader">
</x-slot> </x-slot>
<button data-toggle="tooltip" data-placement="top" title="{{ __('Dispatches an approval event for the selected application') }}" type="button" class="btn btn-primary" onclick="$('#confirmForceEventDispatch').modal('show')"><i class="fas fa-bullhorn"></i> {{ __('Dispatch approval event') }}</button>
<button data-toggle="tooltip" data-placement="top" title="{{ __('Dispatches a rejection event for the selected application') }}" type="button" class="btn btn-primary ml-2" onclick="$('#confirmDispatchRejection').modal('show')"><i class="fas fa-bullhorn"></i> {{ __('Dispatch rejection event') }}</button> <div class="form-group d-block">
<button type="button" class="mb-3 btn btn-info" onclick="$('#confirmForceEventDispatch').modal('show')"><i class="fas fa-check-circle"></i> {{ __('Application Override: Approve') }}</button>
<button type="button" class="mt-3 btn btn-info" onclick="$('#confirmDispatchRejection').modal('show')"><i class="fas fa-ban"></i> {{ __('Application Override: Decline') }}</button>
<form name="evalvotes" method="post" action="{{ route('devForceEvaluateVotes') }}" class="d-inline"> </div>
@csrf
<button data-toggle="tooltip" data-placement="top" title="{{ __('Counts and processes all backlogged votes, for all applications.') }}" type="submit" class="btn btn-primary ml-3"><i class="fas fa-redo"></i> {{ __('Count all votes now') }}</button>
</form>
<form name="purgebans" method="post" action="{{ route('devPurgeExpired') }}" class="d-inline">
@csrf
@method('DELETE')
<button data-toggle="tooltip" data-placement="top" title="{{ __('Cleans the database of old, expired suspensions, therefore unbanning certain users.') }}" type="submit" class="btn btn-primary ml-3"><i class="far fa-trash-alt"></i> {{ __('Purge expired bans') }}</button>
</form>
<x-slot name="cardFooter"> <x-slot name="cardFooter">
<p class="text-muted"> .</p> <p><i class="fas fa-info-circle"></i> {{ __('This panel allows you to override statuses for specific applications. Overriding them will trigger the correct events as well. Note that this system entirely ignores the voting system because these statuses ignore all other logic.') }}</p>
</x-slot> </x-slot>
</x-card>
</x-card>
</div> </div>
<div class="col">
<x-card id="appCleaning" card-title="{{ __('Housekeeping') }}" footer-style="text-muted">
<x-slot name="cardHeader">
</x-slot>
<div class="form-group d-block">
<form method="post" action="{{ route('devForceEvaluateVotes') }}">
@csrf
<button type="submit" class="mb-3 btn btn-info"><i class="fas fa-vote-yea"></i> {{ __('Run task: process pending votes') }}</button>
</form>
<form method="post" action="{{ route('devPurgeExpiredSuspensions') }}">
@csrf
@method('DELETE')
<button type="submit" class="mb-3 btn btn-info"><i class="fas fa-users-cog"></i> {{ __('Run task: lift expired suspensions') }}</button>
</form>
<form method="post" action="{{ route('devPurgeExpiredAbsences') }}">
@csrf
@method('DELETE')
<button type="submit" class="mb-3 d-block btn btn-info"><i class="fas fa-calendar-minus"></i> {{ __('Run task: end expired absence requests') }}</button>
</form>
</div>
<x-slot name="cardFooter">
<p><i class="fas fa-info-circle"></i> {{ __('Housekeeping jobs usually run once every day, but if one of them has failed for some reason, you can manually run them here.') }}</p>
</x-slot>
</x-card>
</div>
</div> </div>
@stop @stop

View File

@ -351,7 +351,10 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['lo
->name('devForceEvaluateVotes'); ->name('devForceEvaluateVotes');
Route::delete('/suspensions/purge-expired', [DevToolsController::class, 'purgeSuspensions']) Route::delete('/suspensions/purge-expired', [DevToolsController::class, 'purgeSuspensions'])
->name('devPurgeExpired'); ->name('devPurgeExpiredSuspensions');
Route::delete('/absences/purge-expired', [DevToolsController::class, 'endAbsencesNow'])
->name('devPurgeExpiredAbsences');
}); });