Skip to content

Commit

Permalink
Merge pull request #49 from czqoocavatsim/development
Browse files Browse the repository at this point in the history
1.3.0 - User interface refresh, conflict detector, track sorting
  • Loading branch information
Liesel Downes authored Mar 26, 2023
2 parents 87e512e + c506ca0 commit a82d86a
Show file tree
Hide file tree
Showing 44 changed files with 6,195 additions and 8,220 deletions.
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1

MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

UPDATE_TRACKS=true

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/node_modules
/public/hot
/public/build
/public/storage
/storage/*.key
/vendor
Expand All @@ -16,3 +17,4 @@ yarn-error.log
/_ide_helper.php
/.phpstorm.meta.php
/storage/debugbar/
.DS_Store
19 changes: 19 additions & 0 deletions app/Enums/ConflictLevelEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace App\Enums;

enum ConflictLevelEnum: int
{
case None = 0;
case Potential = 1;
case Warning = 2;

public function colour(): string
{
return match ($this) {
ConflictLevelEnum::None => 'conflict-green',
ConflictLevelEnum::Potential => 'conflict-potential',
ConflictLevelEnum::Warning => 'conflict-warning'
};
}
}
11 changes: 11 additions & 0 deletions app/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,14 @@ function current_tmi(): string
$dataService = new VatsimDataService();
return $dataService->getTmi() ?? 'N/A';
}

function flashAlert(string $type, ?string $title, ?string $message, bool $toast, bool $timer): void
{
\Illuminate\Support\Facades\Session::flash('alert', [
'icon' => $type,
'title' => $title ?? '',
'text' => $message ?? '',
'toast' => $toast,
'timer' => $timer ? 3000 : null
]);
}
18 changes: 9 additions & 9 deletions app/Http/Controllers/AdministrationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function addAccess(Request $request)
$vatsimAccount->access_level = AccessLevelEnum::Administrator;
$vatsimAccount->save();

toastr()->success('Account added!');
flashAlert(type: 'success', title: 'Account added', message: null, toast: true, timer: true);
return redirect()->route('administration.accounts');
}

Expand All @@ -48,17 +48,17 @@ public function addControllerAccess(Request $request)
$vatsimAccount = VatsimAccount::whereId($request->get('id'))->first();

if (!$vatsimAccount) {
toastr()->error('Account not found. They may need to login to natTrak first.');
flashAlert(type: 'error', title: 'Account not found. They may need to login to natTrak first.', message: null, toast: false, timer: false);
return redirect()->route('administration.controllers');
} elseif ($vatsimAccount->can('administrate')) {
toastr()->error('Account already an administrator.');
flashAlert(type: 'error', title: 'Account already an administrator.', message: null, toast: false, timer: false);
return redirect()->route('administration.controllers');
}

$vatsimAccount->access_level = AccessLevelEnum::Controller;
$vatsimAccount->save();

toastr()->success('Account added!');
flashAlert(type: 'success', title: 'Account added', message: null, toast: true, timer: true);
return redirect()->route('administration.controllers');
}

Expand All @@ -67,10 +67,10 @@ public function removeAccess(Request $request)
$vatsimAccount = VatsimAccount::whereId($request->get('vatsimAccountId'))->first();

if (Auth::id() == $vatsimAccount->id) {
toastr()->error('You can\'t remove yourself!');
flashAlert(type: 'error', title: 'You can\'t remove yourself!', message: null, toast: false, timer: false);
return redirect()->route('administration.accounts');
} elseif ($vatsimAccount->access_level == AccessLevelEnum::Root) {
toastr()->error('You can\'t remove a root user.');
flashAlert(type: 'error', title: 'You can\'t remove a root user.', message: null, toast: false, timer: false);
return redirect()->route('administration.accounts');
}

Expand All @@ -82,7 +82,7 @@ public function removeAccess(Request $request)

$vatsimAccount->save();

toastr()->success("$vatsimAccount->id's access has been removed");
flashAlert(type: 'info', title: "$vatsimAccount->id's access has been removed.", message: null, toast: false, timer: false);
return redirect()->route('administration.accounts');
}

Expand All @@ -91,14 +91,14 @@ public function removeControllerAccess(Request $request)
$vatsimAccount = VatsimAccount::whereId($request->get('vatsimAccountId'))->first();

if (Auth::id() == $vatsimAccount->id) {
toastr()->error('You can\'t remove yourself!');
flashAlert(type: 'error', title: 'You can\'t remove yourself!', message: null, toast: false, timer: false);
return redirect()->route('administration.controllers');
}

$vatsimAccount->access_level = AccessLevelEnum::Pilot;
$vatsimAccount->save();

toastr()->success("$vatsimAccount->id's access has been removed");
flashAlert(type: 'info', title: "$vatsimAccount->id's access has been removed.", message: null, toast: false, timer: false);
return redirect()->route('administration.controllers');
}

Expand Down
41 changes: 27 additions & 14 deletions app/Http/Controllers/ClxMessagesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,30 @@ public function getPending(Request $request)

public function getProcessed(Request $request)
{
$track = null;
if ($request->has('sortByTrack') && !in_array($request->get('sortByTrack'), ['all', 'rr'])) {
$track = Track::active()->whereIdentifier($request->get('sortByTrack'))->firstOrFail();
$display = $request->get('display') ?? [];
$processedRclMsgs = collect();

foreach ($display as $id) {
$trackMessages = RclMessage::cleared()
->with('latestClxMessage')
->when($id != 'RR', function ($query) use ($id) { // Track
$query->where('track_id', Track::whereIdentifier($id)->firstOrFail()->id);
}, function ($query) { // RR
$query->where('track_id', null);
})
->orderByDesc('request_time')
->get();

foreach ($trackMessages as $message) {
$processedRclMsgs->add($message);
}
}

$processedRclMsgs = RclMessage::cleared()->with('latestClxMessage')->when($track, function ($query) use ($track) {
$query->whereTrackId($track->id);
})->orderBy('request_time')->get();

return view('controllers.clx.processed', [
'displayedTrack' => $track,
'displayed' => $display,
'tracks' => Track::active()->get(),
'processedRclMsgs' => $processedRclMsgs,
'_pageTitle' => $track ? "Track {$track->identifier}" : "All tracks"
'_pageTitle' => $display ? 'Tracks ' . implode(", ", $display) : "Select tracks"
]);
}

Expand Down Expand Up @@ -121,6 +131,7 @@ public function transmit(RclMessage $rclMessage, ClxMessageRequest $request)
'mach' => $request->filled('atc_mach') ? $request->get('atc_mach') : $rclMessage->mach,
'entry_fix' => $newEntryFix ?? $rclMessage->entry_fix,
'entry_time_restriction' => $entryRequirement ?? null,
'raw_entry_time_restriction' => $request->get('entry_time_requirement'),
'free_text' => $isReclearance ? '** RECLEARANCE ' . now()->format('Hi') . ' ** ' . $request->get('free_text') : $request->get('free_text'),
'datalink_authority' => DatalinkAuthorities::from($request->get('datalink_authority'))
]);
Expand Down Expand Up @@ -150,7 +161,8 @@ public function transmit(RclMessage $rclMessage, ClxMessageRequest $request)
} else {
$array[] = 'FM ' . $clxMessage->entry_fix . '/' . $rclMessage->entry_time . ' MNTN F' . $clxMessage->flight_level . ' M' . $clxMessage->mach;
}
if ($clxMessage->entry_time_restriction) {
// Only show crossing restriction if entry time =/= the restriction due to the bodge
if ($clxMessage->entry_time_restriction && ($clxMessage->raw_entry_time_restriction != $rclMessage->entry_time)) {
$array[] = "/ATC CROSS {$clxMessage->entry_fix} {$clxMessage->formatEntryTimeRestriction()}";
}
if (($clxMessage->mach != $rclMessage->mach) || ($rclMessage->latestClxMessage && ($clxMessage->mach != $rclMessage->latestClxMessage->mach))) {
Expand All @@ -170,7 +182,8 @@ public function transmit(RclMessage $rclMessage, ClxMessageRequest $request)
} else {
$msg = "{$clxMessage->datalink_authority->name} clears {$rclMessage->callsign} to {$rclMessage->destination} via {$clxMessage->entry_fix}, random routeing {$clxMessage->random_routeing}. From {$clxMessage->entry_fix} maintain Flight Level {$clxMessage->flight_level}, Mach {$clxMessage->mach}.";
}
if ($clxMessage->entry_time_restriction) {
// Only show crossing restriction if entry time =/= the restriction due to the bodge
if ($clxMessage->entry_time_restriction && ($clxMessage->raw_entry_time_restriction != $rclMessage->entry_time)) {
$msg .= " Cross {$clxMessage->entry_fix} " . strtolower($clxMessage->formatEntryTimeRestriction()) . ".";
}
if (($clxMessage->mach != $rclMessage->mach) || ($rclMessage->latestClxMessage && ($clxMessage->mach != $rclMessage->latestClxMessage->mach))) {
Expand Down Expand Up @@ -201,15 +214,15 @@ public function transmit(RclMessage $rclMessage, ClxMessageRequest $request)
->withProperties(['datalink' => $clxMessage->data_link_message])
->log("CLX Message Transmitted By " . $clxMessage->datalink_authority->name);

toastr()->success('Clearance transmitted.');
flashAlert(type: 'success', title: null, message: 'Clearance transmitted.', toast: true, timer: true);
return redirect()->route('controllers.clx.show-rcl-message', $rclMessage);
}

public function deleteRclMessage(Request $request, RclMessage $rclMessage)
{
$redirectToProcessed = $rclMessage->clxMessages->count() > 0;
$rclMessage->delete();
toastr()->info('RCL deleted.');
flashAlert(type: 'success', title: null, message: 'Request deleted.', toast: true, timer: true);
if ($redirectToProcessed) {
return redirect()->route('controllers.clx.processed');
} else {
Expand All @@ -226,7 +239,7 @@ public function revertToVoice(Request $request, RclMessage $rclMessage)
'free_text' => 'REVERT TO VOICE. REQUEST FREQ FROM DOMESTIC CONTROL.'
]);

toastr()->success('Revert to voice message sent. You can now delete the request.');
flashAlert(type: 'success', title: null, message: 'Revert to voice message sent. You can delete the request now.', toast: true, timer: true);
return redirect()->route('controllers.clx.show-rcl-message', $rclMessage);
}
}
2 changes: 1 addition & 1 deletion app/Http/Controllers/RclMessagesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function index()
public function create()
{
if (RclMessage::whereVatsimAccountId(Auth::id())->whereClxMessageId(null)->exists()) {
toastr()->error('You already have a pending oceanic clearance request. If it has been waiting for more than 10 minutes, let the controller know.');
flashAlert(type: 'warning', title: 'You can\'t submit another request yet.', message: 'You already have a pending oceanic clearance request. If it has been waiting for more than 10 minutes, let the controller know via vPilot/xPilot/Swift private message.', toast: false, timer: false);
return redirect()->route('pilots.rcl.index');
}

Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/VatsimAuthController.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public function authenticate(Request $request)
Auth::login($user, false);

//Redirect
toastr()->success("Hi, $user->given_name!");
flashAlert(type: 'success', title: 'Hello!', message: null, toast: true, timer: true);
if ($request->session()->exists('intended')) {
return redirect($request->session()->get('intended'));
} else {
Expand Down
19 changes: 19 additions & 0 deletions app/Http/Controllers/ViewsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

class ViewsController extends Controller
{
public function welcome()
{
$notams = Cache::remember('notams', now()->addMinutes(10), function () {
return json_decode(Http::timeout(5)->get('https://ganderoceanicoca.ams3.digitaloceanspaces.com/resources/data/nattrak/notams.json'));
});
return view('welcome', [
'notams' => $notams
]);
}
}
117 changes: 117 additions & 0 deletions app/Http/Livewire/Controllers/ConflictChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php

namespace App\Http\Livewire\Controllers;

use App\Enums\ConflictLevelEnum;
use App\Models\ClxMessage;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Illuminate\Support\Collection;
use Livewire\Component;

class ConflictChecker extends Component
{
public $originalLevel;
public $level;
public $originalEntry;
public $entry;
public $originalTime;
public $time;

public $conflicts = [];
public ConflictLevelEnum $conflictLevel = ConflictLevelEnum::None;

protected $listeners = ['levelChanged', 'timeChanged'];

public function render()
{
return view('livewire.controllers.conflict-checker');
}

public function mount()
{
$this->originalLevel = $this->level;
$this->originalEntry = $this->entry;
$this->originalTime = $this->time;
}

public function levelChanged(string $newLevel)
{
if (empty($newLevel)) {
$this->level = $this->originalLevel;
} else {
$this->level = $newLevel;
}
$this->check();
}

public function timeChanged(string $newTime)
{
if (empty($newTime)) {
$this->time = $this->originalTime;
} else {
$this->time = $newTime;
}
$this->check();
}

private function getTimeRange(string $time, int $minutes): array
{
$period = CarbonPeriod::since(Carbon::parse($time)->subMinutes($minutes))->minutes()->until(Carbon::parse($time)->addMinutes($minutes));
$times = [];
foreach ($period as $time) {
$times[] = $time->format('Hi');
}
return $times;
}

private function formatDiff(Carbon $a, Carbon $b): string
{
if ($a->diffInMinutes($b) < 2) {
return "Same";
} else {
return $a->longAbsoluteDiffForHumans($b);
}
}

private function determineConflictLevel(Collection $aircraft): ConflictLevelEnum
{
$level = ConflictLevelEnum::None;
foreach ($aircraft as $a) {
if ($a['diffMinutes'] < 5) {
$level = ConflictLevelEnum::Warning;
}
if ($a['diffMinutes'] >= 5 && $a['diffMinutes'] <= 10) {
$level = ConflictLevelEnum::Potential;
}
}

return $level;
}

public function check()
{
$timeArray = $this->getTimeRange($this->time, 10);
$this->conflicts = [];
$results = ClxMessage::whereEntryFix($this->entry)
->whereIn(
'raw_entry_time_restriction',
$timeArray
)
->whereFlightLevel($this->level)
->with('rclMessage')
->get();
$mapped = $results->map(function (ClxMessage $message, $key) {
return [
'id' => $key,
'callsign' => $message->rclMessage->callsign,
'level' => $message->flight_level,
'time' => $message->formatEntryTimeRestriction(),
'diffVisual' => $this->formatDiff(Carbon::parse($this->time), Carbon::parse($message->raw_entry_time_restriction)),
'diffMinutes' => Carbon::parse($this->time)->diffInMinutes(Carbon::parse($message->raw_entry_time_restriction))
];
});
$this->conflictLevel = $this->determineConflictLevel($mapped);
$this->conflicts = $mapped;
}
}
Loading

0 comments on commit a82d86a

Please sign in to comment.