Major changes - Vote system now finished

This commit is contained in:
Miguel Nogueira 2020-05-30 00:20:39 +01:00
parent cc8c293cc6
commit d15c0cb12f
32 changed files with 1791 additions and 17 deletions

View File

@ -49,3 +49,5 @@ PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
SENTRY_LARAVEL_DSN=

View File

@ -9,6 +9,7 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/asm89/stack-cors" />
<excludeFolder url="file://$MODULE_DIR$/vendor/barryvdh/laravel-debugbar" />
<excludeFolder url="file://$MODULE_DIR$/vendor/brick/math" />
<excludeFolder url="file://$MODULE_DIR$/vendor/clue/stream-filter" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/dnoegel/php-xdg-base-dir" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/cache" />
@ -30,6 +31,8 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
<excludeFolder url="file://$MODULE_DIR$/vendor/hamcrest/hamcrest-php" />
<excludeFolder url="file://$MODULE_DIR$/vendor/http-interop/http-factory-guzzle" />
<excludeFolder url="file://$MODULE_DIR$/vendor/jean85/pretty-package-versions" />
<excludeFolder url="file://$MODULE_DIR$/vendor/jeroennoten/laravel-adminlte" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/framework" />
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/tinker" />
@ -44,8 +47,16 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/nikic/php-parser" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nunomaduro/collision" />
<excludeFolder url="file://$MODULE_DIR$/vendor/opis/closure" />
<excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/client-common" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/discovery" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/guzzle6-adapter" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/httplug" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/message-factory" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/promise" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-common" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-docblock" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/type-resolver" />
@ -59,6 +70,8 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/event-dispatcher" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-client" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-factory" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/simple-cache" />
@ -79,6 +92,8 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/resource-operations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/type" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sentry/sentry" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sentry/sentry-laravel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/swiftmailer/swiftmailer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/css-selector" />
@ -90,12 +105,14 @@
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-kernel" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/options-resolver" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-iconv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php73" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-uuid" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/process" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/routing" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />

View File

@ -104,6 +104,23 @@
<path value="$PROJECT_DIR$/vendor/barryvdh/laravel-debugbar" />
<path value="$PROJECT_DIR$/vendor/maximebf/debugbar" />
<path value="$PROJECT_DIR$/vendor/symfony/debug" />
<path value="$PROJECT_DIR$/vendor/sentry/sentry-laravel" />
<path value="$PROJECT_DIR$/vendor/sentry/sentry" />
<path value="$PROJECT_DIR$/vendor/symfony/polyfill-uuid" />
<path value="$PROJECT_DIR$/vendor/symfony/options-resolver" />
<path value="$PROJECT_DIR$/vendor/psr/http-client" />
<path value="$PROJECT_DIR$/vendor/psr/http-factory" />
<path value="$PROJECT_DIR$/vendor/php-http/message-factory" />
<path value="$PROJECT_DIR$/vendor/php-http/httplug" />
<path value="$PROJECT_DIR$/vendor/php-http/client-common" />
<path value="$PROJECT_DIR$/vendor/php-http/discovery" />
<path value="$PROJECT_DIR$/vendor/php-http/message" />
<path value="$PROJECT_DIR$/vendor/php-http/promise" />
<path value="$PROJECT_DIR$/vendor/php-http/guzzle6-adapter" />
<path value="$PROJECT_DIR$/vendor/paragonie/random_compat" />
<path value="$PROJECT_DIR$/vendor/clue/stream-filter" />
<path value="$PROJECT_DIR$/vendor/jean85/pretty-package-versions" />
<path value="$PROJECT_DIR$/vendor/http-interop/http-factory-guzzle" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.2" />

View File

@ -14,6 +14,7 @@ class Application extends Model
];
public function user()
{
return $this->belongsTo('App\User', 'applicantUserID', 'id');
@ -29,10 +30,16 @@ class Application extends Model
return $this->hasOne('App\Appointment', 'applicationID', 'id');
}
public function votes()
{
return $this->belongsToMany('App\Vote', 'votes_has_application');
}
public function setStatus($status)
{
return $this->update([
'applicationStatus' => $status
]);
}
}

View File

@ -0,0 +1,133 @@
<?php
namespace App\Console\Commands;
use App\Application;
use App\Events\ApplicationApprovedEvent;
use App\Events\ApplicationDeniedEvent;
use Illuminate\Console\Command;
class CountVotes extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'votes:evaluate {--d|dryrun : Controls whether passing applicants should be promoted (e.g. only show results)}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Iterates through eligible applications and determines if they should be approved based on the number of votes';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$eligibleApps = Application::where('applicationStatus', 'STAGE_PEERAPPROVAL')->get();
$pbar = $this->output->createProgressBar($eligibleApps->count());
if($eligibleApps->isEmpty())
{
$this->error('𐄂 There are no applications that need to be processed.');
return false;
}
foreach ($eligibleApps as $application)
{
$votes = $application->votes;
$voteCount = $application->votes->count();
$positiveVotes = 0;
$negativeVotes = 0;
if ($voteCount > 5)
{
$this->info('Counting votes for application ID ' . $application->id);
foreach ($votes as $vote)
{
switch ($vote->allowedVoteType)
{
case 'VOTE_APPROVE':
$positiveVotes++;
break;
case 'VOTE_DENY':
$negativeVotes++;
break;
}
}
$this->info('Total votes for application ID ' . $application->id . ': ' . $voteCount);
$this->info('Calculating criteria...');
$negativeVotePercent = floor(($negativeVotes / $voteCount) * 100);
$positiveVotePercent = floor(($positiveVotes / $voteCount) * 100);
$pollResult = $positiveVotePercent > $negativeVotePercent;
$this->table([
'% of approval votes',
'% of denial votes'
], [ // array of arrays, e.g. rows
[
$positiveVotePercent . "%",
$negativeVotePercent . "%"
]
]);
if ($pollResult)
{
$this->info('✓ Dispatched promotion event for applicant ' . $application->user->name);
if (!$this->option('dryrun'))
{
event(new ApplicationApprovedEvent(Application::find($application->id)));
}
else
{
$this->warn('Dry run: Event won\'t be dispatched');
}
$pbar->advance();
}
else {
if (!$this->option('dryrun'))
{
event(new ApplicationDeniedEvent(Application::find($application->id)));
}
else {
$this->warn('Dry run: Event won\'t be dispatched');
}
$pbar->advance();
$this->error('𐄂 Applicant ' . $application->user->name . ' does not meet vote criteria (Majority)');
}
}
else
{
$this->warn("Application ID" . $application->id . " did not have enough votes for processing (min 5)");
}
}
$pbar->finish();
return true;
}
}

View File

@ -25,6 +25,10 @@ class Kernel extends ConsoleKernel
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')->hourly();
$schedule->command('vote:evaluate')
->everyFiveMinutes();
// Production value: Every day
}
/**

View File

@ -0,0 +1,31 @@
<?php
namespace App\Events;
use App\Application;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ApplicationApprovedEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $application;
/**
* Create a new event instance.
*
* @param Application $application
*/
public function __construct(Application $application)
{
$this->application = $application;
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Events;
use App\Application;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ApplicationDeniedEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $application;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Application $application)
{
$this->application = $application;
}
}

View File

@ -36,6 +36,10 @@ class Handler extends ExceptionHandler
*/
public function report(Throwable $exception)
{
if (app()->bound('sentry') && $this->shouldReport($exception)) {
app('sentry')->captureException($exception);
}
parent::report($exception);
}

View File

@ -13,6 +13,22 @@ use Illuminate\Support\Facades\Validator;
class ApplicationController extends Controller
{
private function canVote($votes)
{
$allvotes = collect([]);
foreach ($votes as $vote)
{
if ($vote->userID == Auth::user()->id)
{
Log::debug('Match');
$allvotes->push($vote);
}
}
return $allvotes->count() == 1;
}
public function showUserApps()
{
@ -33,7 +49,8 @@ class ApplicationController extends Controller
'application' => $application,
'structuredResponses' => json_decode($application->response->responseData, true),
'formStructure' => $application->response->form,
'vacancy' => $application->response->vacancy
'vacancy' => $application->response->vacancy,
'canVote' => $this->canVote($application->votes)
]
);
}
@ -96,6 +113,7 @@ class ApplicationController extends Controller
{
return view('dashboard.appmanagement.peerreview')
->with('applications', Application::where('applicationStatus', 'STAGE_PEERAPPROVAL')->get());
}
public function renderApplicationForm(Request $request, $vacancySlug)

View File

@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Application;
use App\Http\Requests\SaveNotesRequest;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Appointment;
@ -84,5 +85,24 @@ class AppointmentController extends Controller
return redirect()->back();
}
// also updates
public function saveNotes(SaveNotesRequest $request, $applicationID)
{
$application = Application::find($applicationID);
if (!is_null($application))
{
$application->appointment->meetingNotes = $request->noteText;
$application->appointment->save();
$request->session()->flash('success', 'Meeting notes have been saved.');
}
else
{
$request->session()->flash('error', 'Sanity check failed: There\'s no appointment to save notes to!');
}
return redirect()->back();
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Http\Controllers;
use App\Application;
use App\Events\ApplicationApprovedEvent;
use Illuminate\Http\Request;
class DevToolsController extends Controller
{
public function index()
{
return view('dashboard.administration.devtools')
->with('applications', Application::where('applicationStatus', 'STAGE_PEERAPPROVAL')->get());
}
public function forceVoteCount(Request $request)
{
$application = Application::find($request->application);
if (!is_null($application))
{
event(new ApplicationApprovedEvent($application));
$request->session()->flash('success', 'Event dispatched! Please check the debug logs for more info');
}
else
{
$request->session()->flash('error', 'Application doesn\'t exist!');
}
return redirect()->back();
}
}

View File

@ -2,9 +2,43 @@
namespace App\Http\Controllers;
use App\Application;
use App\Http\Requests\VoteRequest;
use App\Jobs\ProcessVoteList;
use App\Vote;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class VoteController extends Controller
{
//
public function vote(VoteRequest $voteRequest, $applicationID)
{
$application = Application::find($applicationID);
if (!is_null($application))
{
$vote = Vote::create([
'userID' => Auth::user()->id,
'allowedVoteType' => $voteRequest->voteType,
]);
$vote->application()->attach($applicationID);
Log::info('User ' . Auth::user()->name . ' has voted in applicant ' . $application->user->name . '\'s application', [
'voteType' => $voteRequest->voteType
]);
$voteRequest->session()->flash('success', 'Your vote has been registered! You will now be notified about the outcome of this application.');
}
else
{
$voteRequest->session()->flash('error', 'Can\t vote a non existant application!');
}
// Cron job will run command that processes votes
return redirect()->back();
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class SaveNotesRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'noteText' => 'required|string'
];
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class VoteRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'voteType' => 'required|string|in:VOTE_DENY,VOTE_APPROVE'
];
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace App\Listeners;
use App\Events\ApplicationDeniedEvent;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
class DenyUser
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param ApplicationDeniedEvent $event
* @return void
*/
public function handle(ApplicationDeniedEvent $event)
{
$event->application->setStatus('DENIED');
Log::info('User ' . $event->application->user->name . ' just had their application denied.');
// Also dispatch other notifications
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace App\Listeners;
use App\Events\ApplicationApprovedEvent;
use App\StaffProfile;
use Carbon\Carbon;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;
class PromoteUser
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param ApplicationApprovedEvent $event
* @return void
*/
public function handle(ApplicationApprovedEvent $event)
{
$event->application->setStatus('APPROVED');
$staffProfile = StaffProfile::create([
'userID' => $event->application->user->id,
'approvalDate' => now()->toDateTimeString(),
'memberNotes' => 'Approved by staff members. Welcome them to the team!'
]);
Log::info('User ' . $event->application->user->name . ' has just been promoted!', [
'newRank' => $event->application->response->vacancy->permissionGroupName,
'staffProfileID' => $staffProfile->id
]);
// TODO: Dispatch alert email and notifications for the user and staff members
// TODO: Also assign new app role based on the permission group name
}
}

View File

@ -20,6 +20,12 @@ class EventServiceProvider extends ServiceProvider
SendEmailVerificationNotification::class,
OnUserRegistration::class
],
'App\Events\ApplicationApprovedEvent' => [
'App\Listeners\PromoteUser'
],
'App\Events\ApplicationDeniedEvent' => [
'App\Listeners\DenyUser'
]
];
/**
@ -29,6 +35,7 @@ class EventServiceProvider extends ServiceProvider
*/
public function boot()
{
parent::boot();
//

View File

@ -2,6 +2,7 @@
namespace App\Providers;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
@ -32,8 +33,17 @@ class MojangStatusProvider extends ServiceProvider
{
Log::info("Mojang Status Provider: Mojang Status not found in the cache; Sending new request.");
try
{
$mcstatus = Http::get(config('general.urls.mojang.statuscheck'));
Cache::put('mojang_status', base64_encode($mcstatus->body()), now()->addMinutes(60));
Cache::put('mojang_status', base64_encode($mcstatus->body()), now()->addDays(3));
}
catch(ConnectException $connectException)
{
Log::critical('Could not connect to Mojang servers: Cannot check/refresh status', [
'message' => $connectException->getMessage()
]);
}
}
View::share('mcstatus', json_decode(base64_decode(Cache::get('mojang_status')), true));

View File

@ -6,5 +6,13 @@ use Illuminate\Database\Eloquent\Model;
class StaffProfile extends Model
{
//
public $fillable = [
'userID',
'approvalDate',
'terminationDate',
'resignationDate',
'memberNotes'
];
}

View File

@ -42,8 +42,14 @@ class User extends Authenticatable
return $this->hasMany('App\Application', 'applicantUserID', 'id');
}
public function votes()
{
return $this->hasMany('App\Vote', 'userID', 'id');
}
public function profile()
{
return $this->hasOne('App\Profile', 'userID', 'id');
}
}

View File

@ -6,5 +6,24 @@ use Illuminate\Database\Eloquent\Model;
class Vote extends Model
{
//
public $fillable = [
'userID',
'allowedVoteType',
];
public $touches = [
'application'
];
public function user()
{
return $this->belongsTo('App\User', 'id', 'userID');
}
public function application()
{
return $this->belongsToMany('App\Application', 'votes_has_application');
}
}

View File

@ -17,7 +17,8 @@
"jeroennoten/laravel-adminlte": "^3.2",
"laravel/framework": "^7.0",
"laravel/tinker": "^2.0",
"laravel/ui": "^2.0"
"laravel/ui": "^2.0",
"sentry/sentry-laravel": "1.7.1"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.3",

1081
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -294,6 +294,11 @@ return [
'text' => 'Global Notification Settings',
'icon' => 'far fa-bell',
'url' => '/admin/notifications'
],
[
'text' => 'Developer Tools',
'icon' => 'fas fa-code',
'url' => '/admin/devtools'
]
]
],

24
config/sentry.php Normal file
View File

@ -0,0 +1,24 @@
<?php
return [
'dsn' => env('SENTRY_LARAVEL_DSN', env('SENTRY_DSN')),
// capture release as git sha
// 'release' => trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')),
'breadcrumbs' => [
// Capture Laravel logs in breadcrumbs
'logs' => true,
// Capture SQL queries in breadcrumbs
'sql_queries' => true,
// Capture bindings on SQL queries logged in breadcrumbs
'sql_bindings' => true,
// Capture queue job information in breadcrumbs
'queue_info' => true,
],
];

View File

@ -1,6 +1,5 @@
.footer-button {
.footer-buttons {
display: inline-block;
white-space: nowrap;
margin-right: 10px;
}

View File

@ -0,0 +1,75 @@
@extends('adminlte::page')
@section('title', 'Raspberry Network | Developer Options')
@section('content_header')
<h4>Administration / Developer Tools</h4>
@stop
@section('js')
<x-global-errors></x-global-errors>
@stop
@section('content')
<x-modal id="confirmForceEventDispatch" modal-label="confirmForceEventDispatch" modal-title="Choose an application" include-close-button="true">
<p>Please choose an application to force re-evaluation</p>
<form method="POST" id="forceEval" action="{{route('devToolsForceVoteCount')}}">
@csrf
<select name="application" class="custom-select">
@if(!$applications->isEmpty())
@foreach($applications as $application)
<option value="{{$application->id}}">Application ID {{$application->id}} ({{$application->user->name}})</option>
@endforeach
@else
<option value="null" disabled>There are no valid applications</option>
@endif
</select>
</form>
<x-slot name="modalFooter">
<button type="button" class="btn btn-danger" onclick="document.getElementById('forceEval').submit()">Dispatch event now</button>
</x-slot>
</x-modal>
<div class="row">
<div class="col">
<div class="alert alert-warning">
<i class="fa fa-exclamation-triangle"></i> <b>Warning: Do not use these options if you don't know what you're doing, even if you have access to this page.</b>
</div>
</div>
</div>
<div class="row">
<div class="col">
<x-card id="tools" card-title="Event Management" footer-style="text-center">
<x-slot name="cardHeader">
</x-slot>
<button type="button" class="btn btn-danger" onclick="$('#confirmForceEventDispatch').modal('show')">Force Vote Evaluation</button>
<x-slot name="cardFooter">
</x-slot>
</x-card>
</div>
</div>
@stop

View File

@ -67,8 +67,6 @@
<td><span class="badge badge-warning">{{($application->applicationStatus == 'STAGE_PEERAPPROVAL') ? 'Peer Review' : 'Unknown'}}</span></td>
<td>
<button type="button" class="btn btn-info btn-sm" onclick="window.location.href='{{route('showUserApp', ['id' => $application->id])}}'"><i class="far fa-clipboard"></i> Review</button>
<button type="button" class="btn btn-success btn-sm"><i class="fas fa-user-check"></i> Vote: Approve</button>
<button type="button" class="btn btn-danger btn-sm"><i class="fas fa-user-times"></i> Vote: Deny</button>
</td>
@endforeach

View File

@ -22,10 +22,30 @@
<script type="text/javascript" src="/js/app.js"></script>
<x-global-errors></x-global-errors>
@if (!$canVote)
<script>
toastr.info('You cannot vote on this application anymore.', 'Warning')
</script>
@endif
@stop
@section('content')
<x-modal id="notes" modal-label="notes" modal-title="Shared Notepad" include-close-button="true">
<form id="meetingNotes" method="POST" action="{{route('saveNotes', ['applicationID' => $application->id])}}">
@csrf
@method('PATCH')
<textarea name="noteText" rows="5" class="form-control">{{$application->appointment->meetingNotes ?? 'There are no notes yet. Add some!'}}</textarea>
</form>
<p class="text-muted text-sm">Last updated @ {{$application->appointment->updated_at}}</p>
<x-slot name="modalFooter">
<button type="button" class="btn btn-success" onclick="document.getElementById('meetingNotes').submit()"><i class="far fa-paper-plane"></i> Save & Close</button>
</x-slot>
</x-modal>
<x-modal id="denyApplication" modal-label="denyApplicationLabel" modal-title="Please confirm" include-close-button="true">
<p>Are you sure you want to deny this application? Please keep in mind that this user will only be allowed to apply 30 days after their first application.</p>
@ -246,7 +266,8 @@
</form>
<button class="btn btn-warning mr-3">View Meeting Notes</button>
<button class="btn btn-danger mr-3">Cancel Interview</button>
<!-- Show to users only -->
<button class="btn btn-success mr-3">Accept Meeting</button>
</x-slot>
</x-card>
@ -270,10 +291,22 @@
<x-slot name="cardFooter">
<button type="button" class="btn btn-sm btn-warning">Vote: Approve Applicant</button>
<button type="button" class="btn btn-sm btn-warning">Vote: Deny Applicant</button>
@if($canVote)
<button type="button" class="btn btn-sm btn-warning ml-5">Meeting Notes</button>
<form class="d-inline-block" method="POST" action="{{route('voteApplication', ['id' => $application->id])}}">
@csrf
<input type="hidden" name="voteType" value="VOTE_APPROVE">
<button type="submit" class="btn btn-sm btn-warning">Vote: Approve Applicant</button>
</form>
<form class="d-inline-block" method="POST" action="{{route('voteApplication', ['id' => $application->id])}}">
@csrf
<input type="hidden" name="voteType" value="VOTE_DENY">
<button type="submit" class="btn btn-sm btn-warning">Vote: Deny Applicant</button>
</form>
@endif
<button type="button" class="btn btn-sm btn-warning {{($canVote) ? 'ml-5' : ''}}" onclick="$('#notes').modal('show')">Meeting Notes</button>
</x-slot>
</x-card>

View File

@ -12,7 +12,6 @@ use Illuminate\Support\Facades\Route;
| contains the "web" middleware group. Now create something great!
|
*/
Route::group(['prefix' => 'auth', 'middleware' => ['usernameUUID']], function (){
Auth::routes();
@ -38,9 +37,14 @@ Route::group(['middleware' => 'auth'], function(){
->name('showUserApps')
->middleware('eligibility');
Route::get('/view/{id}', 'ApplicationController@showUserApp')
->name('showUserApp');
Route::patch('/notes/save/{applicationID}', 'AppointmentController@saveNotes')
->name('saveNotes');
Route::patch('/update/{id}/{newStatus}', 'ApplicationController@updateApplicationStatus')
->name('updateApplicationStatus');
@ -54,6 +58,10 @@ Route::group(['middleware' => 'auth'], function(){
->name('pendingInterview');
Route::post('{id}/staff/vote', 'VoteController@vote')
->name('voteApplication');
});
Route::group(['prefix' => 'appointments'], function (){
@ -137,6 +145,14 @@ Route::group(['middleware' => 'auth'], function(){
Route::get('forms', 'FormController@index')
->name('showForms');
Route::get('devtools', 'DevToolsController@index')
->name('devTools');
// we could use route model binding
Route::post('devtools/vote-evaluation/force', 'DevToolsController@forceVoteCount')
->name('devToolsForceVoteCount');
});
});

0
storage/debugbar/.gitignore vendored Normal file → Executable file
View File