Type Ratings | New Feature (#1360)

* Type Ratings

Adding "Type Rating" definition and assignment possibility to v7 core.

* Update ProfileController.php

* StyleFix 1

* Update settings.yml

Change description text as requested
This commit is contained in:
B.Fatih KOZ 2021-11-30 22:36:17 +03:00 committed by GitHub
parent 66d83c0ce6
commit 52e716d6ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1275 additions and 181 deletions

View File

@ -0,0 +1,53 @@
<?php
use App\Contracts\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddTypeRatingTables extends Migration
{
public function up()
{
if (!Schema::hasTable('typeratings')) {
Schema::create('typeratings', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('type');
$table->string('description')->nullable();
$table->string('image_url')->nullable();
$table->boolean('active')->default(true);
$table->timestamps();
$table->unique('id');
$table->unique('name');
});
}
if (!Schema::hasTable('typerating_user')) {
Schema::create('typerating_user', function (Blueprint $table) {
$table->unsignedInteger('typerating_id');
$table->unsignedInteger('user_id');
$table->primary(['typerating_id', 'user_id']);
$table->index(['typerating_id', 'user_id']);
});
}
if (!Schema::hasTable('typerating_subfleet')) {
Schema::create('typerating_subfleet', function (Blueprint $table) {
$table->unsignedInteger('typerating_id');
$table->unsignedInteger('subfleet_id');
$table->primary(['typerating_id', 'subfleet_id']);
$table->index(['typerating_id', 'subfleet_id']);
});
}
}
public function down()
{
Schema::dropIfExists('typeratings');
Schema::dropIfExists('typerating_user');
Schema::dropIfExists('typerating_subfleet');
}
}

View File

@ -42,6 +42,9 @@
- name: ranks - name: ranks
display_name: Ranks display_name: Ranks
description: Create/edit ranks description: Create/edit ranks
- name: typeratings
display_name: Type Ratings
description: Create/edit type ratings
- name: users - name: users
display_name: Users display_name: Users
description: Create/edit users description: Create/edit users

View File

@ -283,7 +283,14 @@
value: true value: true
options: '' options: ''
type: boolean type: boolean
description: 'Aircraft that can be flown are restricted to a user''s rank' description: 'Aircraft restricted to user''s rank'
- key: pireps.restrict_aircraft_to_typerating
name: 'Restrict Aircraft by Type Ratings'
group: pireps
value: false
options: ''
type: boolean
description: 'Aircraft restricted to user type ratings'
- key: pireps.only_aircraft_at_dpt_airport - key: pireps.only_aircraft_at_dpt_airport
name: 'Restrict Aircraft At Departure' name: 'Restrict Aircraft At Departure'
group: pireps group: pireps

View File

@ -17,6 +17,7 @@ use App\Repositories\AircraftRepository;
use App\Repositories\FareRepository; use App\Repositories\FareRepository;
use App\Repositories\RankRepository; use App\Repositories\RankRepository;
use App\Repositories\SubfleetRepository; use App\Repositories\SubfleetRepository;
use App\Repositories\TypeRatingRepository;
use App\Services\ExportService; use App\Services\ExportService;
use App\Services\FareService; use App\Services\FareService;
use App\Services\FleetService; use App\Services\FleetService;
@ -36,26 +37,29 @@ class SubfleetController extends Controller
private $importSvc; private $importSvc;
private $rankRepo; private $rankRepo;
private $subfleetRepo; private $subfleetRepo;
private $typeratingRepo;
/** /**
* SubfleetController constructor. * SubfleetController constructor.
* *
* @param AircraftRepository $aircraftRepo * @param AircraftRepository $aircraftRepo
* @param FleetService $fleetSvc * @param FareRepository $fareRepo
* @param FareRepository $fareRepo * @param FareService $fareSvc
* @param FareService $fareSvc * @param FleetService $fleetSvc
* @param ImportService $importSvc * @param ImportService $importSvc
* @param RankRepository $rankRepo * @param RankRepository $rankRepo
* @param SubfleetRepository $subfleetRepo * @param SubfleetRepository $subfleetRepo
* @param TypeRatingRepository $typeratingRepo
*/ */
public function __construct( public function __construct(
AircraftRepository $aircraftRepo, AircraftRepository $aircraftRepo,
FleetService $fleetSvc,
FareRepository $fareRepo, FareRepository $fareRepo,
FareService $fareSvc, FareService $fareSvc,
FleetService $fleetSvc,
ImportService $importSvc, ImportService $importSvc,
RankRepository $rankRepo, RankRepository $rankRepo,
SubfleetRepository $subfleetRepo SubfleetRepository $subfleetRepo,
TypeRatingRepository $typeratingRepo
) { ) {
$this->aircraftRepo = $aircraftRepo; $this->aircraftRepo = $aircraftRepo;
$this->fareRepo = $fareRepo; $this->fareRepo = $fareRepo;
@ -64,48 +68,7 @@ class SubfleetController extends Controller
$this->importSvc = $importSvc; $this->importSvc = $importSvc;
$this->rankRepo = $rankRepo; $this->rankRepo = $rankRepo;
$this->subfleetRepo = $subfleetRepo; $this->subfleetRepo = $subfleetRepo;
} $this->typeratingRepo = $typeratingRepo;
/**
* Get the ranks that are available to the subfleet
*
* @param $subfleet
*
* @return array
*/
protected function getAvailRanks($subfleet)
{
$retval = [];
$all_ranks = $this->rankRepo->all();
$avail_ranks = $all_ranks->except($subfleet->ranks->modelKeys());
foreach ($avail_ranks as $rank) {
$retval[$rank->id] = $rank->name;
}
return $retval;
}
/**
* Get all the fares that haven't been assigned to a given subfleet
*
* @param mixed $subfleet
*
* @return array
*/
protected function getAvailFares($subfleet)
{
$retval = [];
$all_fares = $this->fareRepo->all();
$avail_fares = $all_fares->except($subfleet->fares->modelKeys());
foreach ($avail_fares as $fare) {
$retval[$fare->id] = $fare->name.
' (price: '.$fare->price.
', type: '.FareType::label($fare->type).
', cost: '.$fare->cost.
', capacity: '.$fare->capacity.')';
}
return $retval;
} }
/** /**
@ -152,8 +115,8 @@ class SubfleetController extends Controller
{ {
$input = $request->all(); $input = $request->all();
$subfleet = $this->subfleetRepo->create($input); $subfleet = $this->subfleetRepo->create($input);
Flash::success('Subfleet saved successfully.'); Flash::success('Subfleet saved successfully.');
return redirect(route('admin.subfleets.edit', [$subfleet->id])); return redirect(route('admin.subfleets.edit', [$subfleet->id]));
} }
@ -172,10 +135,12 @@ class SubfleetController extends Controller
if (empty($subfleet)) { if (empty($subfleet)) {
Flash::error('Subfleet not found'); Flash::error('Subfleet not found');
return redirect(route('admin.subfleets.index')); return redirect(route('admin.subfleets.index'));
} }
$avail_fares = $this->getAvailFares($subfleet); $avail_fares = $this->getAvailFares($subfleet);
return view('admin.subfleets.show', [ return view('admin.subfleets.show', [
'subfleet' => $subfleet, 'subfleet' => $subfleet,
'avail_fares' => $avail_fares, 'avail_fares' => $avail_fares,
@ -192,24 +157,27 @@ class SubfleetController extends Controller
public function edit($id) public function edit($id)
{ {
$subfleet = $this->subfleetRepo $subfleet = $this->subfleetRepo
->with(['fares', 'ranks']) ->with(['fares', 'ranks', 'typeratings'])
->findWithoutFail($id); ->findWithoutFail($id);
if (empty($subfleet)) { if (empty($subfleet)) {
Flash::error('Subfleet not found'); Flash::error('Subfleet not found');
return redirect(route('admin.subfleets.index')); return redirect(route('admin.subfleets.index'));
} }
$avail_fares = $this->getAvailFares($subfleet); $avail_fares = $this->getAvailFares($subfleet);
$avail_ranks = $this->getAvailRanks($subfleet); $avail_ranks = $this->getAvailRanks($subfleet);
$avail_ratings = $this->getAvailTypeRatings($subfleet);
return view('admin.subfleets.edit', [ return view('admin.subfleets.edit', [
'airlines' => Airline::all()->pluck('name', 'id'), 'airlines' => Airline::all()->pluck('name', 'id'),
'hubs' => Airport::where('hub', 1)->pluck('name', 'id'), 'hubs' => Airport::where('hub', 1)->pluck('name', 'id'),
'fuel_types' => FuelType::labels(), 'fuel_types' => FuelType::labels(),
'avail_fares' => $avail_fares, 'avail_fares' => $avail_fares,
'avail_ranks' => $avail_ranks, 'avail_ranks' => $avail_ranks,
'subfleet' => $subfleet, 'avail_ratings' => $avail_ratings,
'subfleet' => $subfleet,
]); ]);
} }
@ -229,12 +197,13 @@ class SubfleetController extends Controller
if (empty($subfleet)) { if (empty($subfleet)) {
Flash::error('Subfleet not found'); Flash::error('Subfleet not found');
return redirect(route('admin.subfleets.index')); return redirect(route('admin.subfleets.index'));
} }
$this->subfleetRepo->update($request->all(), $id); $this->subfleetRepo->update($request->all(), $id);
Flash::success('Subfleet updated successfully.'); Flash::success('Subfleet updated successfully.');
return redirect(route('admin.subfleets.index')); return redirect(route('admin.subfleets.index'));
} }
@ -251,6 +220,7 @@ class SubfleetController extends Controller
if (empty($subfleet)) { if (empty($subfleet)) {
Flash::error('Subfleet not found'); Flash::error('Subfleet not found');
return redirect(route('admin.subfleets.index')); return redirect(route('admin.subfleets.index'));
} }
@ -259,12 +229,13 @@ class SubfleetController extends Controller
$aircraft = $this->aircraftRepo->findWhere(['subfleet_id' => $id], ['id']); $aircraft = $this->aircraftRepo->findWhere(['subfleet_id' => $id], ['id']);
if ($aircraft->count() > 0) { if ($aircraft->count() > 0) {
Flash::error('There are aircraft still assigned to this subfleet, you can\'t delete it!')->important(); Flash::error('There are aircraft still assigned to this subfleet, you can\'t delete it!')->important();
return redirect(route('admin.subfleets.index')); return redirect(route('admin.subfleets.index'));
} }
$this->subfleetRepo->delete($id); $this->subfleetRepo->delete($id);
Flash::success('Subfleet deleted successfully.'); Flash::success('Subfleet deleted successfully.');
return redirect(route('admin.subfleets.index')); return redirect(route('admin.subfleets.index'));
} }
@ -281,11 +252,8 @@ class SubfleetController extends Controller
$subfleets = $this->subfleetRepo->all(); $subfleets = $this->subfleetRepo->all();
$path = $exporter->exportSubfleets($subfleets); $path = $exporter->exportSubfleets($subfleets);
return response()
->download($path, 'subfleets.csv', [ return response()->download($path, 'subfleets.csv', ['content-type' => 'text/csv'])->deleteFileAfterSend(true);
'content-type' => 'text/csv',
])
->deleteFileAfterSend(true);
} }
/** /**
@ -312,77 +280,64 @@ class SubfleetController extends Controller
} }
/** /**
* @param Subfleet $subfleet * Get all the fares that haven't been assigned to a given subfleet
* *
* @return mixed * @param mixed $subfleet
*
* @return array
*/ */
protected function return_ranks_view(?Subfleet $subfleet) protected function getAvailFares($subfleet)
{ {
$subfleet->refresh(); $retval = [];
$all_fares = $this->fareRepo->all();
$avail_fares = $all_fares->except($subfleet->fares->modelKeys());
foreach ($avail_fares as $fare) {
$retval[$fare->id] = $fare->name.
' (price: '.$fare->price.
', type: '.FareType::label($fare->type).
', cost: '.$fare->cost.
', capacity: '.$fare->capacity.')';
}
$avail_ranks = $this->getAvailRanks($subfleet); return $retval;
return view('admin.subfleets.ranks', [
'subfleet' => $subfleet,
'avail_ranks' => $avail_ranks,
]);
} }
/** /**
* @param Subfleet $subfleet * Get the ranks that are available to the subfleet
* *
* @return mixed * @param $subfleet
*
* @return array
*/ */
protected function return_fares_view(?Subfleet $subfleet) protected function getAvailRanks($subfleet)
{ {
$subfleet->refresh(); $retval = [];
$all_ranks = $this->rankRepo->all();
$avail_ranks = $all_ranks->except($subfleet->ranks->modelKeys());
foreach ($avail_ranks as $rank) {
$retval[$rank->id] = $rank->name;
}
$avail_fares = $this->getAvailFares($subfleet); return $retval;
return view('admin.subfleets.fares', [
'subfleet' => $subfleet,
'avail_fares' => $avail_fares,
]);
} }
/** /**
* Operations for associating ranks to the subfleet * Get the type ratings that are available to the subfleet
* *
* @param $id * @param $subfleet
* @param Request $request
* *
* @return mixed * @return array
*/ */
public function ranks($id, Request $request) protected function getAvailTypeRatings($subfleet)
{ {
$subfleet = $this->subfleetRepo->findWithoutFail($id); $retval = [];
if (empty($subfleet)) { $all_ratings = $this->typeratingRepo->all();
return $this->return_ranks_view($subfleet); $avail_ratings = $all_ratings->except($subfleet->typeratings->modelKeys());
foreach ($avail_ratings as $tr) {
$retval[$tr->id] = $tr->name.' ('.$tr->type.')';
} }
if ($request->isMethod('get')) { return $retval;
return $this->return_ranks_view($subfleet);
}
/*
* update specific rank data
*/
if ($request->isMethod('post')) {
$rank = $this->rankRepo->find($request->input('rank_id'));
$this->fleetSvc->addSubfleetToRank($subfleet, $rank);
} elseif ($request->isMethod('put')) {
$override = [];
$rank = $this->rankRepo->find($request->input('rank_id'));
$override[$request->name] = $request->value;
$this->fleetSvc->addSubfleetToRank($subfleet, $rank, $override);
} // dissassociate fare from teh aircraft
elseif ($request->isMethod('delete')) {
$rank = $this->rankRepo->find($request->input('rank_id'));
$this->fleetSvc->removeSubfleetFromRank($subfleet, $rank);
}
$subfleet->save();
return $this->return_ranks_view($subfleet);
} }
/** /**
@ -398,6 +353,54 @@ class SubfleetController extends Controller
]); ]);
} }
/**
* @param Subfleet $subfleet
*
* @return mixed
*/
protected function return_fares_view(?Subfleet $subfleet)
{
$subfleet->refresh();
$avail_fares = $this->getAvailFares($subfleet);
return view('admin.subfleets.fares', [
'subfleet' => $subfleet,
'avail_fares' => $avail_fares,
]);
}
/**
* @param Subfleet $subfleet
*
* @return mixed
*/
protected function return_ranks_view(?Subfleet $subfleet)
{
$subfleet->refresh();
$avail_ranks = $this->getAvailRanks($subfleet);
return view('admin.subfleets.ranks', [
'subfleet' => $subfleet,
'avail_ranks' => $avail_ranks,
]);
}
/**
* @param Subfleet $subfleet
*
* @return mixed
*/
protected function return_typeratings_view(?Subfleet $subfleet)
{
$subfleet->refresh();
$avail_ratings = $this->getAvailTypeRatings($subfleet);
return view('admin.subfleets.type_ratings', [
'subfleet' => $subfleet,
'avail_ratings' => $avail_ratings,
]);
}
/** /**
* Operations for associating ranks to the subfleet * Operations for associating ranks to the subfleet
* *
@ -479,4 +482,79 @@ class SubfleetController extends Controller
return $this->return_fares_view($subfleet); return $this->return_fares_view($subfleet);
} }
/**
* Operations for associating ranks to the subfleet
*
* @param $id
* @param Request $request
*
* @return mixed
*/
public function ranks($id, Request $request)
{
$subfleet = $this->subfleetRepo->findWithoutFail($id);
if (empty($subfleet)) {
return $this->return_ranks_view($subfleet);
}
if ($request->isMethod('get')) {
return $this->return_ranks_view($subfleet);
}
// associate rank with the subfleet
if ($request->isMethod('post')) {
$rank = $this->rankRepo->find($request->input('rank_id'));
$this->fleetSvc->addSubfleetToRank($subfleet, $rank);
} // override definitions
elseif ($request->isMethod('put')) {
$override = [];
$rank = $this->rankRepo->find($request->input('rank_id'));
$override[$request->name] = $request->value;
$this->fleetSvc->addSubfleetToRank($subfleet, $rank, $override);
} // dissassociate rank from the subfleet
elseif ($request->isMethod('delete')) {
$rank = $this->rankRepo->find($request->input('rank_id'));
$this->fleetSvc->removeSubfleetFromRank($subfleet, $rank);
}
$subfleet->save();
return $this->return_ranks_view($subfleet);
}
/**
* Operations for associating type ratings to the subfleet
*
* @param $id
* @param Request $request
*
* @return mixed
*/
public function typeratings($id, Request $request)
{
$subfleet = $this->subfleetRepo->findWithoutFail($id);
if (empty($subfleet)) {
return $this->return_typeratings_view($subfleet);
}
if ($request->isMethod('get')) {
return $this->return_typeratings_view($subfleet);
}
// associate subfleet with type rating
if ($request->isMethod('post')) {
$typerating = $this->typeratingRepo->find($request->input('typerating_id'));
$this->fleetSvc->addSubfleetToTypeRating($subfleet, $typerating);
} // dissassociate subfleet from the type rating
elseif ($request->isMethod('delete')) {
$typerating = $this->typeratingRepo->find($request->input('typerating_id'));
$this->fleetSvc->removeSubfleetFromTypeRating($subfleet, $typerating);
}
$subfleet->save();
return $this->return_typeratings_view($subfleet);
}
} }

View File

@ -0,0 +1,168 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Contracts\Controller;
use App\Http\Requests\CreateTypeRatingRequest;
use App\Http\Requests\UpdateTypeRatingRequest;
use App\Repositories\SubfleetRepository;
use App\Repositories\TypeRatingRepository;
use App\Services\FleetService;
use Cache;
use Illuminate\Http\Request;
use Laracasts\Flash\Flash;
use Prettus\Repository\Criteria\RequestCriteria;
class TypeRatingController extends Controller
{
private $fleetSvc;
private $subfleetRepo;
private $typeratingRepo;
public function __construct(
FleetService $fleetSvc,
SubfleetRepository $subfleetRepo,
TypeRatingRepository $typeratingRepo
) {
$this->fleetSvc = $fleetSvc;
$this->subfleetRepo = $subfleetRepo;
$this->typeratingRepo = $typeratingRepo;
}
public function index(Request $request)
{
$this->typeratingRepo->pushCriteria(new RequestCriteria($request));
$typeratings = $this->typeratingRepo->all();
return view('admin.typeratings.index', [
'typeratings' => $typeratings,
]);
}
public function create()
{
return view('admin.typeratings.create');
}
public function store(CreateTypeRatingRequest $request)
{
$input = $request->all();
$model = $this->typeratingRepo->create($input);
Flash::success('Type Rating saved successfully.');
// Cache::forget(config('cache.keys.RANKS_PILOT_LIST.key'));
return redirect(route('admin.typeratings.edit', [$model->id]));
}
public function show($id)
{
$typerating = $this->typeratingRepo->findWithoutFail($id);
if (empty($typerating)) {
Flash::error('Type Rating not found');
return redirect(route('admin.typeratings.index'));
}
return view('admin.typeratings.show', [
'typerating' => $typerating,
]);
}
public function edit($id)
{
$typerating = $this->typeratingRepo->findWithoutFail($id);
if (empty($typerating)) {
Flash::error('Type Rating not found');
return redirect(route('admin.typeratings.index'));
}
$avail_subfleets = $this->getAvailSubfleets($typerating);
return view('admin.typeratings.edit', [
'typerating' => $typerating,
'avail_subfleets' => $avail_subfleets,
]);
}
public function update($id, UpdateTypeRatingRequest $request)
{
$typerating = $this->typeratingRepo->findWithoutFail($id);
if (empty($typerating)) {
Flash::error('Type Rating not found');
return redirect(route('admin.typeratings.index'));
}
$typerating = $this->typeratingRepo->update($request->all(), $id);
// Cache::forget(config('cache.keys.RANKS_PILOT_LIST.key'));
Flash::success('Type Rating updated successfully.');
return redirect(route('admin.typeratings.index'));
}
public function destroy($id)
{
$typerating = $this->typeratingRepo->findWithoutFail($id);
if (empty($typerating)) {
Flash::error('Type Rating not found');
return redirect(route('admin.typeratings.index'));
}
$this->typeratingRepo->delete($id);
Flash::success('Type Rating deleted successfully.');
return redirect(route('admin.typeratings.index'));
}
protected function getAvailSubfleets($typerating)
{
$retval = [];
$all_subfleets = $this->subfleetRepo->all();
$avail_subfleets = $all_subfleets->except($typerating->subfleets->modelKeys());
foreach ($avail_subfleets as $subfleet) {
$retval[$subfleet->id] = $subfleet->name.' (Airline: '.$subfleet->airline->code.')';
}
return $retval;
}
protected function return_subfleet_view($typerating)
{
$avail_subfleets = $this->getAvailSubfleets($typerating);
return view('admin.typeratings.subfleets', [
'typerating' => $typerating,
'avail_subfleets' => $avail_subfleets,
]);
}
public function subfleets($id, Request $request)
{
$typerating = $this->typeratingRepo->findWithoutFail($id);
if (empty($typerating)) {
Flash::error('Type Rating not found!');
return redirect(route('admin.typeratings.index'));
}
// add subfleet to type rating
if ($request->isMethod('post')) {
$subfleet = $this->subfleetRepo->find($request->input('subfleet_id'));
$this->fleetSvc->addSubfleetToTypeRating($subfleet, $typerating);
}
// remove subfleet from type rating
elseif ($request->isMethod('delete')) {
$subfleet = $this->subfleetRepo->find($request->input('subfleet_id'));
$this->fleetSvc->removeSubfleetFromTypeRating($subfleet, $typerating);
}
return $this->return_subfleet_view($typerating);
}
}

View File

@ -12,6 +12,7 @@ use App\Repositories\AirlineRepository;
use App\Repositories\AirportRepository; use App\Repositories\AirportRepository;
use App\Repositories\PirepRepository; use App\Repositories\PirepRepository;
use App\Repositories\RoleRepository; use App\Repositories\RoleRepository;
use App\Repositories\TypeRatingRepository;
use App\Repositories\UserRepository; use App\Repositories\UserRepository;
use App\Services\UserService; use App\Services\UserService;
use App\Support\Timezonelist; use App\Support\Timezonelist;
@ -30,24 +31,27 @@ class UserController extends Controller
private $airportRepo; private $airportRepo;
private $pirepRepo; private $pirepRepo;
private $roleRepo; private $roleRepo;
private $typeratingRepo;
private $userRepo; private $userRepo;
private $userSvc; private $userSvc;
/** /**
* UserController constructor. * UserController constructor.
* *
* @param AirlineRepository $airlineRepo * @param AirlineRepository $airlineRepo
* @param AirportRepository $airportRepo * @param AirportRepository $airportRepo
* @param PirepRepository $pirepRepo * @param PirepRepository $pirepRepo
* @param RoleRepository $roleRepo * @param RoleRepository $roleRepo
* @param UserRepository $userRepo * @param TypeRatingRepository $typeratingRepo
* @param UserService $userSvc * @param UserRepository $userRepo
* @param UserService $userSvc
*/ */
public function __construct( public function __construct(
AirlineRepository $airlineRepo, AirlineRepository $airlineRepo,
AirportRepository $airportRepo, AirportRepository $airportRepo,
PirepRepository $pirepRepo, PirepRepository $pirepRepo,
RoleRepository $roleRepo, RoleRepository $roleRepo,
TypeRatingRepository $typeratingRepo,
UserRepository $userRepo, UserRepository $userRepo,
UserService $userSvc UserService $userSvc
) { ) {
@ -55,6 +59,7 @@ class UserController extends Controller
$this->airportRepo = $airportRepo; $this->airportRepo = $airportRepo;
$this->pirepRepo = $pirepRepo; $this->pirepRepo = $pirepRepo;
$this->roleRepo = $roleRepo; $this->roleRepo = $roleRepo;
$this->typeratingRepo = $typeratingRepo;
$this->userSvc = $userSvc; $this->userSvc = $userSvc;
$this->userRepo = $userRepo; $this->userRepo = $userRepo;
} }
@ -149,7 +154,7 @@ class UserController extends Controller
public function edit($id) public function edit($id)
{ {
$user = $this->userRepo $user = $this->userRepo
->with(['awards', 'fields', 'rank']) ->with(['awards', 'fields', 'rank', 'typeratings'])
->findWithoutFail($id); ->findWithoutFail($id);
if (empty($user)) { if (empty($user)) {
@ -169,17 +174,19 @@ class UserController extends Controller
$airlines = $this->airlineRepo->selectBoxList(); $airlines = $this->airlineRepo->selectBoxList();
$airports = $this->airportRepo->selectBoxList(false); $airports = $this->airportRepo->selectBoxList(false);
$roles = $this->roleRepo->selectBoxList(false, true); $roles = $this->roleRepo->selectBoxList(false, true);
$avail_ratings = $this->getAvailTypeRatings($user);
return view('admin.users.edit', [ return view('admin.users.edit', [
'user' => $user, 'user' => $user,
'pireps' => $pireps, 'pireps' => $pireps,
'country' => new ISO3166(), 'country' => new ISO3166(),
'countries' => $countries, 'countries' => $countries,
'timezones' => Timezonelist::toArray(), 'timezones' => Timezonelist::toArray(),
'airports' => $airports, 'airports' => $airports,
'airlines' => $airlines, 'airlines' => $airlines,
'ranks' => Rank::all()->pluck('name', 'id'), 'ranks' => Rank::all()->pluck('name', 'id'),
'roles' => $roles, 'roles' => $roles,
'avail_ratings' => $avail_ratings,
]); ]);
} }
@ -260,6 +267,7 @@ class UserController extends Controller
$user = $this->userRepo->findWithoutFail($id); $user = $this->userRepo->findWithoutFail($id);
if (empty($user)) { if (empty($user)) {
Flash::error('User not found'); Flash::error('User not found');
return redirect(route('admin.users.index')); return redirect(route('admin.users.index'));
} }
@ -283,6 +291,7 @@ class UserController extends Controller
$userAward = UserAward::where(['user_id' => $id, 'award_id' => $award_id]); $userAward = UserAward::where(['user_id' => $id, 'award_id' => $award_id]);
if (empty($userAward)) { if (empty($userAward)) {
Flash::error('The user award could not be found'); Flash::error('The user award could not be found');
return redirect()->back(); return redirect()->back();
} }
@ -311,4 +320,73 @@ class UserController extends Controller
return redirect(route('admin.users.edit', [$id])); return redirect(route('admin.users.edit', [$id]));
} }
/**
* Get the type ratings that are available to the user
*
* @param $user
*
* @return array
*/
protected function getAvailTypeRatings($user)
{
$retval = [];
$all_ratings = $this->typeratingRepo->all();
$avail_ratings = $all_ratings->except($user->typeratings->modelKeys());
foreach ($avail_ratings as $tr) {
$retval[$tr->id] = $tr->name.' ('.$tr->type.')';
}
return $retval;
}
/**
* @param User $user
*
* @return mixed
*/
protected function return_typeratings_view(?User $user)
{
$user->refresh();
$avail_ratings = $this->getAvailTypeRatings($user);
return view('admin.users.type_ratings', [
'user' => $user,
'avail_ratings' => $avail_ratings,
]);
}
/**
* Operations for associating type ratings to the user
*
* @param $id
* @param Request $request
*
* @return mixed
*/
public function typeratings($id, Request $request)
{
$user = $this->userRepo->findWithoutFail($id);
if (empty($user)) {
return $this->return_typeratings_view($user);
}
if ($request->isMethod('get')) {
return $this->return_typeratings_view($user);
}
// associate user with type rating
if ($request->isMethod('post')) {
$typerating = $this->typeratingRepo->find($request->input('typerating_id'));
$this->userSvc->addUserToTypeRating($user, $typerating);
} // dissassociate user from the type rating
elseif ($request->isMethod('delete')) {
$typerating = $this->typeratingRepo->find($request->input('typerating_id'));
$this->userSvc->removeUserFromTypeRating($user, $typerating);
}
$user->save();
return $this->return_typeratings_view($user);
}
} }

View File

@ -80,7 +80,7 @@ class ProfileController extends Controller
public function show($id) public function show($id)
{ {
/** @var \App\Models\User $user */ /** @var \App\Models\User $user */
$with = ['airline', 'awards', 'current_airport', 'fields.field', 'home_airport', 'last_pirep', 'rank']; $with = ['airline', 'awards', 'current_airport', 'fields.field', 'home_airport', 'last_pirep', 'rank', 'typeratings'];
$user = User::with($with)->where('id', $id)->first(); $user = User::with($with)->where('id', $id)->first();
if (empty($user)) { if (empty($user)) {

View File

@ -80,46 +80,39 @@ class SimBriefController
// No aircraft selected, show selection form // No aircraft selected, show selection form
if (!$aircraft_id) { if (!$aircraft_id) {
// If no subfleets defined for flight get them from user
if ($flight->subfleets->count() > 0) {
$subfleets = $flight->subfleets;
} else {
$subfleets = $this->userSvc->getAllowableSubfleets($user);
}
// Build an array of subfleet id's from the subfleets collection // Get user's allowed subfleets and intersect it with flight subfleets
$sf_ids = $subfleets->map(function ($subfleets) { // so we will have a proper list which the user is allowed to fly
return collect($subfleets->toArray()) $user_subfleets = $this->userSvc->getAllowableSubfleets($user)->pluck('id')->toArray();
->only(['id']) $flight_subfleets = $flight->subfleets->pluck('id')->toArray();
->all();
});
// Now we can build a proper aircrafts collection $subfleet_ids = filled($flight_subfleets) ? array_intersect($user_subfleets, $flight_subfleets) : $user_subfleets;
// Contents will be either members of flight->subfleets
// or members of user's allowable subfleets // Prepare variables for single aircraft query
$aircrafts = Aircraft::whereIn('subfleet_id', $sf_ids) $where = [];
->where('state', AircraftState::PARKED) $where['state'] = AircraftState::PARKED;
->where('status', AircraftStatus::ACTIVE) $where['status'] = AircraftStatus::ACTIVE;
->orderby('icao')
->orderby('registration')
->get();
if (setting('pireps.only_aircraft_at_dpt_airport')) { if (setting('pireps.only_aircraft_at_dpt_airport')) {
$aircrafts = $aircrafts->where('airport_id', $flight->dpt_airport_id); $where['airport_id'] = $flight->dpt_airport_id;
} }
if (setting('simbrief.block_aircraft')) { $withCount = ['simbriefs' => function ($query) {
// Build a list of aircraft_id's being used for active sb packs $query->whereNull('pirep_id');
$sb_aircraft = SimBrief::whereNotNull('flight_id')->pluck('aircraft_id'); }];
// Filter aircraft list to non used/blocked ones // Build proper aircraft collection considering all possible settings
$aircrafts = $aircrafts->whereNotIn('id', $sb_aircraft); // Flight subfleets, user subfleet restrictions, pirep restrictions, simbrief blocking etc
} $aircraft = Aircraft::withCount($withCount)->where($where)
->when(setting('simbrief.block_aircraft'), function ($query) {
return $query->having('simbriefs_count', 0);
})->whereIn('subfleet_id', $subfleet_ids)
->orderby('icao')->orderby('registration')
->get();
return view('flights.simbrief_aircraft', [ return view('flights.simbrief_aircraft', [
'flight' => $flight, 'flight' => $flight,
'aircrafts' => $aircrafts, 'aircrafts' => $aircraft,
'subfleets' => $subfleets,
]); ]);
} }

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Requests;
use App\Contracts\FormRequest;
use App\Models\Typerating;
class CreateTypeRatingRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return Typerating::$rules;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Http\Requests;
use App\Contracts\FormRequest;
use App\Models\Typerating;
class UpdateTypeRatingRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return Typerating::$rules;
}
}

View File

@ -107,4 +107,9 @@ class Subfleet extends Model
return $this->belongsToMany(Rank::class, 'subfleet_rank') return $this->belongsToMany(Rank::class, 'subfleet_rank')
->withPivot('acars_pay', 'manual_pay'); ->withPivot('acars_pay', 'manual_pay');
} }
public function typeratings()
{
return $this->belongsToMany(Typerating::class, 'typerating_subfleet', 'subfleet_id', 'typerating_id');
}
} }

36
app/Models/Typerating.php Normal file
View File

@ -0,0 +1,36 @@
<?php
namespace App\Models;
use App\Contracts\Model;
class Typerating extends Model
{
public $table = 'typeratings';
protected $fillable = [
'name',
'type',
'description',
'image_url',
];
// Validation
public static $rules = [
'name' => 'required',
'type' => 'required',
'description' => 'nullable',
'image_url' => 'nullable',
];
// Relationships
public function subfleets()
{
return $this->belongsToMany(Subfleet::class, 'typerating_subfleet', 'typerating_id', 'subfleet_id');
}
public function users()
{
return $this->belongsToMany(User::class, 'typerating_user', 'typerating_id', 'user_id');
}
}

View File

@ -7,6 +7,7 @@ use App\Models\Traits\JournalTrait;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Laratrust\Traits\LaratrustUserTrait; use Laratrust\Traits\LaratrustUserTrait;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
/** /**
* @property int id * @property int id
@ -42,6 +43,7 @@ use Laratrust\Traits\LaratrustUserTrait;
* @property UserFieldValue[] fields * @property UserFieldValue[] fields
* @property Role[] roles * @property Role[] roles
* @property Subfleet[] subfleets * @property Subfleet[] subfleets
* @property TypeRating[] typeratings
* *
* @mixin \Illuminate\Database\Eloquent\Builder * @mixin \Illuminate\Database\Eloquent\Builder
* @mixin \Illuminate\Notifications\Notifiable * @mixin \Illuminate\Notifications\Notifiable
@ -52,6 +54,7 @@ class User extends Authenticatable
use JournalTrait; use JournalTrait;
use LaratrustUserTrait; use LaratrustUserTrait;
use Notifiable; use Notifiable;
use HasRelationships;
public $table = 'users'; public $table = 'users';
@ -191,8 +194,7 @@ class User extends Authenticatable
{ {
$default = config('gravatar.default'); $default = config('gravatar.default');
$uri = config('gravatar.url') $uri = config('gravatar.url').md5(strtolower(trim($this->email))).'?d='.urlencode($default);
.md5(strtolower(trim($this->email))).'?d='.urlencode($default);
if ($size !== null) { if ($size !== null) {
$uri .= '&s='.$size; $uri .= '&s='.$size;
@ -265,4 +267,14 @@ class User extends Authenticatable
{ {
return $this->belongsTo(Rank::class, 'rank_id'); return $this->belongsTo(Rank::class, 'rank_id');
} }
public function typeratings()
{
return $this->belongsToMany(Typerating::class, 'typerating_user', 'user_id', 'typerating_id');
}
public function rated_subfleets()
{
return $this->hasManyDeep(Subfleet::class, ['typerating_user', Typerating::class, 'typerating_subfleet']);
}
} }

View File

@ -376,6 +376,21 @@ class RouteServiceProvider extends ServiceProvider
], 'settings', 'SettingsController@update') ], 'settings', 'SettingsController@update')
->name('settings.update')->middleware('ability:admin,settings'); ->name('settings.update')->middleware('ability:admin,settings');
// Type Ratings
Route::resource('typeratings', 'TypeRatingController')->middleware('ability:admin,typeratings');
Route::match([
'get',
'post',
'put',
'delete',
], 'typeratings/{id}/subfleets', 'TypeRatingController@subfleets')->middleware('ability:admin,typeratings');
Route::match([
'get',
'post',
'put',
'delete',
], 'typeratings/{id}/users', 'TypeRatingController@users')->middleware('ability:admin,typeratings');
// maintenance // maintenance
Route::match(['get'], 'maintenance', 'MaintenanceController@index') Route::match(['get'], 'maintenance', 'MaintenanceController@index')
->name('maintenance.index')->middleware('ability:admin,maintenance'); ->name('maintenance.index')->middleware('ability:admin,maintenance');
@ -426,6 +441,13 @@ class RouteServiceProvider extends ServiceProvider
'delete', 'delete',
], 'subfleets/{id}/ranks', 'SubfleetController@ranks')->middleware('ability:admin,fleet'); ], 'subfleets/{id}/ranks', 'SubfleetController@ranks')->middleware('ability:admin,fleet');
Route::match([
'get',
'post',
'put',
'delete',
], 'subfleets/{id}/typeratings', 'SubfleetController@typeratings')->middleware('ability:admin,fleet');
Route::resource('subfleets', 'SubfleetController')->middleware('ability:admin,fleet'); Route::resource('subfleets', 'SubfleetController')->middleware('ability:admin,fleet');
/** /**
@ -439,6 +461,13 @@ class RouteServiceProvider extends ServiceProvider
Route::resource('users', 'UserController')->middleware('ability:admin,users'); Route::resource('users', 'UserController')->middleware('ability:admin,users');
Route::match([
'get',
'post',
'put',
'delete',
], 'users/{id}/typeratings', 'UserController@typeratings')->middleware('ability:admin,users');
// defaults // defaults
Route::get('', ['uses' => 'DashboardController@index']) Route::get('', ['uses' => 'DashboardController@index'])
->middleware('update_pending', 'ability:admin,admin-access'); ->middleware('update_pending', 'ability:admin,admin-access');

View File

@ -0,0 +1,43 @@
<?php
namespace App\Repositories;
use App\Contracts\Repository;
use App\Models\Typerating;
use Prettus\Repository\Contracts\CacheableInterface;
use Prettus\Repository\Traits\CacheableRepository;
class TypeRatingRepository extends Repository implements CacheableInterface
{
use CacheableRepository;
protected $fieldSearchable = [
'name' => 'like',
'type' => 'like',
];
public function model()
{
return Typerating::class;
}
public function selectBoxList($add_blank = false, $only_active = true): array
{
$retval = [];
$where = [
'active' => $only_active,
];
$items = $this->findWhere($where);
if ($add_blank) {
$retval[''] = '';
}
foreach ($items as $i) {
$retval[$i->id] = $i->name;
}
return $retval;
}
}

View File

@ -6,6 +6,7 @@ use App\Contracts\Service;
use App\Models\Flight; use App\Models\Flight;
use App\Models\Rank; use App\Models\Rank;
use App\Models\Subfleet; use App\Models\Subfleet;
use App\Models\Typerating;
class FleetService extends Service class FleetService extends Service
{ {
@ -33,7 +34,36 @@ class FleetService extends Service
public function removeSubfleetFromRank(Subfleet $subfleet, Rank $rank) public function removeSubfleetFromRank(Subfleet $subfleet, Rank $rank)
{ {
$subfleet->ranks()->detach($rank->id); $subfleet->ranks()->detach($rank->id);
$subfleet->save();
$subfleet->refresh();
return $subfleet;
}
/**
* Add the subfleet to a type rating
*
* @param Subfleet $subfleet
* @param Typerating $typerating
*/
public function addSubfleetToTypeRating(Subfleet $subfleet, Typerating $typerating)
{
$subfleet->typeratings()->syncWithoutDetaching([$typerating->id]);
$subfleet->save();
$subfleet->refresh();
return $subfleet;
}
/**
* Remove the subfleet from a type rating
*
* @param Subfleet $subfleet
* @param Typerating $typerating
*/
public function removeSubfleetFromTypeRating(Subfleet $subfleet, Typerating $typerating)
{
$subfleet->typeratings()->detach($typerating->id);
$subfleet->save(); $subfleet->save();
$subfleet->refresh(); $subfleet->refresh();

View File

@ -15,6 +15,7 @@ use App\Models\Enums\UserState;
use App\Models\Pirep; use App\Models\Pirep;
use App\Models\Rank; use App\Models\Rank;
use App\Models\Role; use App\Models\Role;
use App\Models\Typerating;
use App\Models\User; use App\Models\User;
use App\Models\UserFieldValue; use App\Models\UserFieldValue;
use App\Repositories\AircraftRepository; use App\Repositories\AircraftRepository;
@ -343,7 +344,7 @@ class UserService extends Service
/** /**
* Return the subfleets this user is allowed access to, * Return the subfleets this user is allowed access to,
* based on their current rank * based on their current Rank and/or by Type Rating
* *
* @param $user * @param $user
* *
@ -351,14 +352,31 @@ class UserService extends Service
*/ */
public function getAllowableSubfleets($user) public function getAllowableSubfleets($user)
{ {
if ($user === null || setting('pireps.restrict_aircraft_to_rank') === false) { $restrict_rank = setting('pireps.restrict_aircraft_to_rank', true);
/** @var Collection $subfleets */ $restrict_type = setting('pireps.restrict_aircraft_to_typerating', false);
$subfleets = $this->subfleetRepo->with('aircraft')->all(); $restricted_to = [];
if ($user) {
$rank_sf_array = $restrict_rank ? $user->rank->subfleets()->pluck('id')->toArray() : [];
$type_sf_array = $restrict_type ? $user->rated_subfleets->pluck('id')->toArray() : [];
if ($restrict_rank && !$restrict_type) {
$restricted_to = $rank_sf_array;
} elseif (!$restrict_rank && $restrict_type) {
$restricted_to = $type_sf_array;
} elseif ($restrict_rank && $restrict_type) {
$restricted_to = array_intersect($rank_sf_array, $type_sf_array);
}
} else { } else {
/** @var Collection $subfleets */ $restrict_rank = false;
$subfleets = $user->rank->subfleets()->with('aircraft')->get(); $restrict_type = false;
} }
// @var Collection $subfleets
$subfleets = $this->subfleetRepo->when(($restrict_rank || $restrict_type), function ($query) use ($restricted_to) {
return $query->whereIn('id', $restricted_to);
})->with('aircraft')->get();
// Map the subfleets with the proper fare information // Map the subfleets with the proper fare information
return $subfleets->transform(function ($sf, $key) { return $subfleets->transform(function ($sf, $key) {
$sf->fares = $this->fareSvc->getForSubfleet($sf); $sf->fares = $this->fareSvc->getForSubfleet($sf);
@ -398,9 +416,7 @@ class UserService extends Service
return $user; return $user;
} }
Log::info('User '.$user->ident.' state changing from ' Log::info('User '.$user->ident.' state changing from '.UserState::label($old_state).' to '.UserState::label($user->state));
.UserState::label($old_state).' to '
.UserState::label($user->state));
event(new UserStateChanged($user, $old_state)); event(new UserStateChanged($user, $old_state));
@ -561,11 +577,39 @@ class UserService extends Service
// Recalc the rank // Recalc the rank
$this->calculatePilotRank($user); $this->calculatePilotRank($user);
Log::info('User '.$user->ident.' updated; pirep count='.$pirep_count Log::info('User '.$user->ident.' updated; pirep count='.$pirep_count.', rank='.$user->rank->name.', flight_time='.$user->flight_time.' minutes');
.', rank='.$user->rank->name
.', flight_time='.$user->flight_time.' minutes');
$user->save(); $user->save();
return $user; return $user;
} }
/**
* Attach a type rating to the user
*
* @param User $user
* @param Typerating $typerating
*/
public function addUserToTypeRating(User $user, Typerating $typerating)
{
$user->typeratings()->syncWithoutDetaching([$typerating->id]);
$user->save();
$user->refresh();
return $user;
}
/**
* Detach a type rating from the user
*
* @param User $user
* @param Typerating $typerating
*/
public function removeUserFromTypeRating(User $user, Typerating $typerating)
{
$user->typeratings()->detach($typerating->id);
$user->save();
$user->refresh();
return $user;
}
} }

View File

@ -70,7 +70,8 @@
"queueworker/sansdaemon": "^1.2", "queueworker/sansdaemon": "^1.2",
"jpkleemans/attribute-events": "^1.1", "jpkleemans/attribute-events": "^1.1",
"akaunting/laravel-money": "^1.2", "akaunting/laravel-money": "^1.2",
"staudenmeir/belongs-to-through": "^2.5" "staudenmeir/belongs-to-through": "^2.5",
"staudenmeir/eloquent-has-many-deep": "1.14.3"
}, },
"require-dev": { "require-dev": {
"barryvdh/laravel-debugbar": "^3.5", "barryvdh/laravel-debugbar": "^3.5",

56
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "da29c23dc7b0e9f9c4939bbb2fc8359e", "content-hash": "10a632e92b328969dad32f4d0dcbc65f",
"packages": [ "packages": [
{ {
"name": "akaunting/laravel-money", "name": "akaunting/laravel-money",
@ -6524,6 +6524,60 @@
], ],
"time": "2021-08-19T18:23:06+00:00" "time": "2021-08-19T18:23:06+00:00"
}, },
{
"name": "staudenmeir/eloquent-has-many-deep",
"version": "v1.14.3",
"source": {
"type": "git",
"url": "https://github.com/staudenmeir/eloquent-has-many-deep.git",
"reference": "c44115684aa831b2b74fec8440f6e811602186ed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/staudenmeir/eloquent-has-many-deep/zipball/c44115684aa831b2b74fec8440f6e811602186ed",
"reference": "c44115684aa831b2b74fec8440f6e811602186ed",
"shasum": ""
},
"require": {
"illuminate/database": "^8.0",
"php": "^7.3|^8.0"
},
"require-dev": {
"illuminate/pagination": "^8.0",
"laravel/homestead": "^11.0|^12.0",
"phpunit/phpunit": "^9.3",
"scrutinizer/ocular": "^1.8",
"staudenmeir/eloquent-eager-limit": "^1.6"
},
"type": "library",
"autoload": {
"psr-4": {
"Staudenmeir\\EloquentHasManyDeep\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonas Staudenmeir",
"email": "mail@jonas-staudenmeir.de"
}
],
"description": "Laravel Eloquent HasManyThrough relationships with unlimited levels",
"support": {
"issues": "https://github.com/staudenmeir/eloquent-has-many-deep/issues",
"source": "https://github.com/staudenmeir/eloquent-has-many-deep/tree/v1.14.3"
},
"funding": [
{
"url": "https://paypal.me/JonasStaudenmeir",
"type": "custom"
}
],
"time": "2021-08-21T16:45:45+00:00"
},
{ {
"name": "swiftmailer/swiftmailer", "name": "swiftmailer/swiftmailer",
"version": "v6.3.0", "version": "v6.3.0",

View File

@ -62,6 +62,10 @@
<li><a href="{{ url('/admin/ranks') }}"><i class="pe-7s-graph1"></i>ranks</a></li> <li><a href="{{ url('/admin/ranks') }}"><i class="pe-7s-graph1"></i>ranks</a></li>
@endability @endability
@ability('admin', 'typeratings')
<li><a href="{{ url('/admin/typeratings') }}"><i class="pe-7s-plane"></i>type ratings</a></li>
@endability
@ability('admin', 'awards') @ability('admin', 'awards')
<li><a href="{!! url('/admin/awards') !!}"><i class="pe-7s-diamond"></i>awards</a></li> <li><a href="{!! url('/admin/awards') !!}"><i class="pe-7s-diamond"></i>awards</a></li>
@endability @endability

View File

@ -16,6 +16,12 @@
</div> </div>
</div> </div>
<div class="card border-blue-bottom">
<div class="content">
@include('admin.subfleets.type_ratings')
</div>
</div>
<div class="card border-blue-bottom"> <div class="card border-blue-bottom">
<div class="content"> <div class="content">
@include('admin.subfleets.fares') @include('admin.subfleets.fares')

View File

@ -114,6 +114,12 @@
$.pjax.submit(event, '#subfleet_ranks_wrapper', {push: false}); $.pjax.submit(event, '#subfleet_ranks_wrapper', {push: false});
}); });
$(document).on('submit', 'form.modify_typerating', function (event) {
event.preventDefault();
console.log(event);
$.pjax.submit(event, '#subfleet_typeratings_wrapper', {push: false});
});
$(document).on('submit', 'form.modify_expense', function (event) { $(document).on('submit', 'form.modify_expense', function (event) {
event.preventDefault(); event.preventDefault();
console.log(event); console.log(event);

View File

@ -0,0 +1,45 @@
<div id="subfleet_typeratings_wrapper" class="dataTables_wrapper form-inline dt-bootstrap">
<div class="header">
<h3>type ratings</h3>
@component('admin.components.info')
These type ratings are allowed to fly aircraft in this subfleet.
@endcomponent
</div>
<br/>
<table id="subfleet_ranks" class="table table-hover">
@if(count($subfleet->typeratings))
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th></th>
</tr>
</thead>
@endif
<tbody>
@foreach($subfleet->typeratings as $tr)
<tr>
<td class="sorting_1">{{ $tr->type }}</td>
<td>{{ $tr->name }}</td>
<td style="text-align: right; width:3%;">
{{ Form::open(['url' => '/admin/subfleets/'.$subfleet->id.'/typeratings', 'method' => 'delete', 'class' => 'modify_typerating']) }}
{{ Form::hidden('typerating_id', $tr->id) }}
{{ Form::button('<i class="fa fa-times"></i>', ['type' => 'submit', 'class' => 'btn btn-sm btn-danger btn-icon']) }}
{{ Form::close() }}
</td>
</tr>
@endforeach
</tbody>
</table>
<hr/>
<div class="row">
<div class="col-xs-12">
<div class="text-right">
{{ Form::open(['url' => '/admin/subfleets/'.$subfleet->id.'/typeratings', 'method' => 'post', 'class' => 'modify_typerating form-inline']) }}
{{ Form::select('typerating_id', $avail_ratings, null, ['placeholder' => 'Select Type Rating', 'class' => 'ac-fare-dropdown form-control input-lg select2']) }}
{{ Form::button('<i class="glyphicon glyphicon-plus"></i> add', ['type' => 'submit', 'class' => 'btn btn-success btn-s']) }}
{{ Form::close() }}
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,12 @@
@extends('admin.app')
@section('title', 'Add Type Rating')
@section('content')
<div class="card border-blue-bottom">
<div class="content">
{{ Form::open(['route' => 'admin.typeratings.store', 'class' => 'add_typerating', 'method'=>'POST', 'autocomplete' => false]) }}
@include('admin.typeratings.fields')
{{ Form::close() }}
</div>
</div>
@endsection
@include('admin.typeratings.scripts')

View File

@ -0,0 +1,26 @@
@extends('admin.app')
@section('title', 'Edit '.$typerating->name)
@section('content')
<div class="card border-blue-bottom">
<div class="content">
{{ Form::model($typerating, ['route' => ['admin.typeratings.update', $typerating->id], 'method' => 'patch', 'autocomplete' => false]) }}
@include('admin.typeratings.fields')
{{ Form::close() }}
</div>
</div>
<div class="card border-blue-bottom">
<div class="content">
<div class="header">
<h3>Subfleets</h3>
@component('admin.components.info')
These are the subfleets this type rating is allowed to use.
@endcomponent
</div>
<div class="row">
@include('admin.typeratings.subfleets')
</div>
</div>
</div>
@endsection
@include('admin.typeratings.scripts')

View File

@ -0,0 +1,45 @@
<div class="row">
<div class="form-group col-sm-6">
{{ Form::label('name', 'Name:') }}
{{ Form::text('name', null, ['class' => 'form-control']) }}
<p class="text-danger">{{ $errors->first('name') }}</p>
</div>
<div class="form-group col-sm-6">
{{ Form::label('type', 'Type Code:') }}
{{ Form::text('type', null, ['class' => 'form-control']) }}
<p class="text-danger">{{ $errors->first('type') }}</p>
</div>
</div>
<div class="row">
<div class="form-group col-md-7">
{{ Form::label('description', 'Description:') }}
{{ Form::text('description', null, ['class' => 'form-control']) }}
<p class="text-danger">{{ $errors->first('description') }}</p>
</div>
<div class="form-group col-md-5">
{{ Form::label('image_url', 'Image Link:') }}
{{ Form::text('image_url', null, ['class' => 'form-control']) }}
<p class="text-danger">{{ $errors->first('image_url') }}</p>
</div>
</div>
<div class="row">
<div class="form-group col-md-3">
<div class="checkbox">
<label class="checkbox-inline">
{{ Form::hidden('active', false) }}
{{ Form::checkbox('active') }}
{{ Form::label('active', 'Active:') }}
</label>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="text-right">
{{ Form::button('Save', ['type' => 'submit', 'class' => 'btn btn-success']) }}
</div>
</div>
</div>

View File

@ -0,0 +1,18 @@
@extends('admin.app')
@section('title', 'Type Ratings')
@section('actions')
<li>
<a href="{{ route('admin.typeratings.create') }}">
<i class="ti-plus"></i>
Add New
</a>
</li>
@endsection
@section('content')
<div class="card border-blue-bottom">
<div class="content">
@include('admin.typeratings.table')
</div>
</div>
@endsection

View File

@ -0,0 +1,48 @@
@section('scripts')
<script>
function setEditable() {
const token = $('meta[name="csrf-token"]').attr('content');
const api_key = $('meta[name="api-key"]').attr('content');
@if(isset($typerating))
$('#subfleets-table a').editable({
type: 'text',
mode: 'inline',
emptytext: 'inherited',
url: '{{ url('/admin/typerating/'.$typerating->id.'/subfleets') }}',
title: 'Enter override value',
ajaxOptions: {
type: 'put',
headers: {
'x-api-key': api_key,
'X-CSRF-TOKEN': token,
}
},
params: function (params) {
return {
subfleet_id: params.pk,
name: params.name,
value: params.value
}
}
});
@endif
}
$(document).ready(function () {
setEditable();
$(document).on('submit', 'form.pjax_form', function (event) {
event.preventDefault();
$.pjax.submit(event, '#typerating_subfleet_wrapper', {push: false});
});
$(document).on('pjax:complete', function () {
initPlugins();
setEditable();
});
});
</script>
@endsection

View File

@ -0,0 +1,17 @@
@extends('admin.app')
@section('content')
<section class="content-header">
<h1>{{ $typerating->name }}</h1>
</section>
<div class="content">
<div class="box box-primary">
<div class="box-body">
<div class="row" style="padding-left: 20px">
@include('admin.typerating.show_fields')
</div>
</div>
</div>
</div>
@endsection
@include('admin.typerating.scripts')

View File

@ -0,0 +1,42 @@
<!-- Id Field -->
<div class="form-group">
{{ Form::label('id', 'Id:') }}
<p>{{ $typerating->id }}</p>
</div>
<!-- Name Field -->
<div class="form-group">
{{ Form::label('name', 'Name:') }}
<p>{{ $typerating->name }}</p>
</div>
<!-- Type Code Field -->
<div class="form-group">
{{ Form::label('type', 'Type Code:') }}
<p>{{ $typerating->type }}</p>
</div>
<!-- Description Field -->
<div class="form-group">
{{ Form::label('description', 'Description:') }}
<p>{{ $typerating->description }}</p>
</div>
<!-- Image URL Field -->
<div class="form-group">
{{ Form::label('image_url', 'Image URL:') }}
<p>{{ $typerating->image_url }}</p>
</div>
<!-- Created At Field -->
<div class="form-group">
{{ Form::label('created_at', 'Created At:') }}
<p>{{ show_datetime($typerating->created_at) }}</p>
</div>
<!-- Updated At Field -->
<div class="form-group">
{{ Form::label('updated_at', 'Updated At:') }}
<p>{{ show_datetime($typerating->updated_at) }}</p>
</div>

View File

@ -0,0 +1,54 @@
<div id="typerating_subfleet_wrapper" class="dataTables_wrapper form-inline dt-bootstrap col-lg-12">
@if(count($typerating->subfleets) === 0)
@include('admin.common.none_added', ['type' => 'subfleets'])
@endif
<table class="table table-responsive" id="subfleets-table">
@if(count($typerating->subfleets))
<thead>
<th>Airline</th>
<th>Name</th>
<th style="text-align: center;">Actions</th>
</thead>
@endif
<tbody>
@foreach($typerating->subfleets as $sf)
<tr>
<td>{{ $sf->airline->name }}</td>
<td>{{ $sf->name.' ('.$sf->type.')' }}</td>
<td style="width: 10%; text-align: center;" class="form-inline">
{{ Form::open(['url' => '/admin/typeratings/'.$typerating->id.'/subfleets', 'method' => 'delete', 'class' => 'pjax_form']) }}
{{ Form::hidden('subfleet_id', $sf->id) }}
<div class='btn-group'>
{{ Form::button('<i class="fa fa-times"></i>',
['type' => 'submit',
'class' => 'btn btn-sm btn-danger btn-icon'])
}}
</div>
{{ Form::close() }}
</td>
</tr>
@endforeach
</tbody>
</table>
<hr/>
<div class="row">
<div class="col-lg-12">
<div class="input-group input-group-lg pull-right">
{{ Form::open(['url' => url('/admin/typeratings/'.$typerating->id.'/subfleets'),
'method' => 'post',
'class' => 'pjax_form form-inline'
])
}}
{{ Form::select('subfleet_id', $avail_subfleets, null, [
'placeholder' => 'Select Subfleet',
'class' => 'select2 form-control input-lg'])
}}
{{ Form::button('<i class="fa fa-plus"></i> Add',
['type' => 'submit',
'class' => 'btn btn-success btn-small']) }}
{{ Form::close() }}
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,26 @@
<div id="typeratings_table_wrapper">
<table class="table table-hover table-responsive">
<thead>
<th>Type Code</th>
<th>Name</th>
<th>Description</th>
<th></th>
</thead>
<tbody>
@foreach($typeratings as $typerating)
<tr>
<td><a href="{{ route('admin.typeratings.edit', [$typerating->id]) }}">{{ $typerating->type }}</a></td>
<td>{{ $typerating->name }}</td>
<td>{{ $typerating->description }}</td>
<td class="text-right">
{{ Form::open(['route' => ['admin.typeratings.destroy', $typerating->id], 'method' => 'delete']) }}
<a href="{{ route('admin.typeratings.edit', [$typerating->id]) }}" class='btn btn-sm btn-success btn-icon'>
<i class="fas fa-pencil-alt"></i></a>
{{ Form::button('<i class="fa fa-times"></i>', ['type' => 'submit', 'class' => 'btn btn-sm btn-danger btn-icon', 'onclick' => "return confirm('Are you sure?')"]) }}
{{ Form::close() }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>

View File

@ -9,6 +9,15 @@
</div> </div>
</div> </div>
<div class="card border-blue-bottom">
<div class="content">
<div class="header">
<h3>Type Ratings</h3>
</div>
@include('admin.users.type_ratings')
</div>
</div>
<div class="card border-blue-bottom"> <div class="card border-blue-bottom">
<div class="content"> <div class="content">
<div class="header"> <div class="header">
@ -34,3 +43,4 @@
</div> </div>
</div> </div>
@endsection @endsection
@include('admin.users.script')

View File

@ -0,0 +1,25 @@
@section('scripts')
<script>
function setEditable() {
const token = $('meta[name="csrf-token"]').attr('content');
const api_key = $('meta[name="api-key"]').attr('content');
}
$(document).ready(function () {
setEditable();
$(document).on('submit', 'form.modify_typerating', function (event) {
event.preventDefault();
console.log(event);
$.pjax.submit(event, '#user_typeratings_wrapper', {push: false});
});
$(document).on('pjax:complete', function () {
initPlugins();
setEditable();
});
});
</script>
@endsection

View File

@ -0,0 +1,38 @@
<div id="user_typeratings_wrapper" class="dataTables_wrapper form-inline dt-bootstrap">
<table id="user_typeratings" class="table table-hover">
@if(count($user->typeratings))
<thead>
<tr>
<th>Type</th>
<th>Name</th>
<th></th>
</tr>
</thead>
@endif
<tbody>
@foreach($user->typeratings as $tr)
<tr>
<td class="sorting_1">{{ $tr->type }}</td>
<td>{{ $tr->name }}</td>
<td style="text-align: right; width:3%;">
{{ Form::open(['url' => '/admin/users/'.$user->id.'/typeratings', 'method' => 'delete', 'class' => 'modify_typerating']) }}
{{ Form::hidden('typerating_id', $tr->id) }}
{{ Form::button('<i class="fa fa-times"></i>', ['type' => 'submit', 'class' => 'btn btn-sm btn-danger btn-icon']) }}
{{ Form::close() }}
</td>
</tr>
@endforeach
</tbody>
</table>
<hr/>
<div class="row">
<div class="col-xs-12">
<div class="text-right">
{{ Form::open(['url' => '/admin/users/'.$user->id.'/typeratings', 'method' => 'post', 'class' => 'modify_typerating form-inline']) }}
{{ Form::select('typerating_id', $avail_ratings, null, ['placeholder' => 'Select Type Rating', 'class' => 'ac-fare-dropdown form-control input-lg select2']) }}
{{ Form::button('<i class="glyphicon glyphicon-plus"></i> add', ['type' => 'submit', 'class' => 'btn btn-success btn-s']) }}
{{ Form::close() }}
</div>
</div>
</div>
</div>