Skip to content

Commit

Permalink
feat(invite): simplify seat calculation and visualisation (#49)
Browse files Browse the repository at this point in the history
* feat(invite): simplify seat calculation and visualisation

* feat(invite): change special free seat marker
  • Loading branch information
Fenrikur authored Feb 1, 2024
1 parent aa438cc commit aea6b61
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 53 deletions.
10 changes: 4 additions & 6 deletions app/Http/Controllers/Applications/InviteesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class InviteesController extends Controller
public function view()
{
$user = Auth::user();
/** @var Application */
$application = $user->application;

abort_if($application->type !== ApplicationType::Dealer, 403, 'Shares and Assistants cannot manage this.');
Expand All @@ -33,14 +34,11 @@ public function view()

return view('application.invitees', [
'application' => $application,
'seats' => $application->getSeats(),
'currentSeats' => $application->children()->whereNotNull('canceled_at')->count() + 1,
'maxSeats' => $application->requestedTable->seats,
"assistants" => $application->children()->where('type', ApplicationType::Assistant)->with('user')->get(),
"shares" => $application->children()->where('type', ApplicationType::Share)->with('user')->get(),
"shares_count" => $application->getAvailableShares(),
"assistants_count" => $application->getAvailableAssistants(),
"shares_active_count" => $application->getActiveShares(),
"assistants_active_count" => $application->getActiveAssistants(),
'assistants' => $application->assistants()->get(),
'shares' => $application->shares()->get(),
]);
}

Expand Down
18 changes: 11 additions & 7 deletions app/Http/Middleware/ForceOverseatingRedirectMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,30 @@
namespace App\Http\Middleware;

use App\Enums\ApplicationType;
use App\Models\Application;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Route;

class ForceOverseatingRedirectMiddleware
{
public function handle(Request $request, Closure $next)
{
if(\Route::is('applications.invitees.*')) {
if(Route::is('applications.invitees.*')) {
return $next($request);
}

$application = \Auth::user()->application;
if(is_null($application) || $application->type !== ApplicationType::Dealer || !is_null($application->canceled_at)) {
/** @var Application */
$application = Auth::user()->application;
if(is_null($application) || $application->type !== ApplicationType::Dealer || !$application->isActive()) {
return $next($request);
}

$isTooManyAssistant = $application?->getFreeShares() < 0;
$isTooManyShare = $application?->getFreeAssistants() < 0;
if($isTooManyAssistant || $isTooManyShare) {
return \Redirect::route('applications.invitees.view');
$seats = $application->getSeats();
if($seats['free'] < 0) {
return Redirect::route('applications.invitees.view');
}
return $next($request);
}
Expand Down
63 changes: 42 additions & 21 deletions app/Models/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,38 +210,59 @@ public function cancel()
$this->save();
}

public function getActiveShares(): int
public function shares()
{
return $this->children()->whereNull('canceled_at')->where('type', ApplicationType::Share)->count();
return $this->children()->whereNull('canceled_at')->where('type', ApplicationType::Share);
}

public function getActiveAssistants(): int
public function assistants()
{
return $this->children()->whereNull('canceled_at')->where('type', ApplicationType::Assistant)->count();
return $this->children()->whereNull('canceled_at')->where('type', ApplicationType::Assistant);
}

public function getAvailableShares(): int
public function getSeats(): array
{
if (is_null($this->requestedTable)) {
return 0;
/** @var TableType */
$tableType = $this->assignedTable ?? $this->requestedTable;
if (is_null($tableType) || !$this->isActive()) {
return [
'table' => 0,
'dealers' => 0,
'assistants' => 0,
'free' => 0,
'additional' => null,
];
}
$countedAssistants = $this->getActiveShares() < $this->requestedTable->seats - 1 ? $this->getActiveAssistants() : max($this->getActiveAssistants() - 1, 0);
return !is_null($this->requestedTable) ? max($this->requestedTable->seats - 1 - $countedAssistants, 0) : 0;
}

public function getAvailableAssistants(): int
{
return !is_null($this->requestedTable) ? max(1, $this->requestedTable->seats - 1 - $this->getActiveShares()) : 0;
}
$totalSeats = $tableType->seats;

public function getFreeShares(): int
{
return !is_null($this->requestedTable) ? $this->getAvailableShares() - $this->getActiveShares() : 0;
}
$dealers = $this->shares()->count() + 1;
$assistants = $this->assistants()->count();
$additional = null;

public function getFreeAssistants(): int
{
return $this->getAvailableAssistants() - $this->children()->whereNull('canceled_at')->where('type', ApplicationType::Assistant)->count();
if ($totalSeats - $dealers <= 0 && $assistants === 0) {
// Single assistant is available even if table is filled with dealers.
$additional = 'assistant';
}

if ($totalSeats - $dealers === 1 && $assistants === 1) {
// Single assistant doesn't consume dealer seat and results in free dealer-only seat.
$additional = 'dealer';
}

$free = $totalSeats - $dealers - $assistants;
if ($free < 0 && $assistants === 1) {
// Free seat count should not be negative because of minimum assistant.
$free += 1;
}

return [
'table' => $totalSeats,
'dealers' => $dealers,
'assistants' => $assistants,
'free' => $free,
'additional' => $additional,
];
}

public function getStatusAttribute()
Expand Down
83 changes: 64 additions & 19 deletions resources/views/application/invitees.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
Shares and Assistants
@endsection
@section('content')
<style>
.dd-table-button {
width: 5ex;
height: 5ex;
font-weight: bold;
}
.dd-table-button.border-danger {
border-width: 3px
}
</style>
<div class="">
<div class="col-md-6 mx-auto">
<h1 class="text-center">Manage Shares and Assistants</h1>
Expand All @@ -17,26 +28,62 @@
<div class="alert alert-success text-center fw-bold">The user has been removed.</div>
@endif

@if ($assistants_active_count <= $assistants_count && $shares_active_count <= $shares_count)
@if ($seats['free'] >= 0)
<div class="mx-auto text-center mb-4">
<a href="{{ route('dashboard') }}" class="btn btn-sm btn-primary">Return to dashboard</a>
</div>
@endif
<div class="col-md-6 mx-auto">
<h3 class="text-center">Seats in your Dealership</h3>
<div class="mx-auto text-center mb-1">
@for ($i = 0; $i < $seats['dealers']; $i++)
<button
class="btn btn-sm btn-primary dd-table-button @if ($i >= $seats['table']) border-danger text-danger @endif"
type="button" title="Dealer">D</button>
@endfor
@for ($i = 0; $i < $seats['free']; $i++)
<button class="btn btn-sm btn-secondary dd-table-button" type="button" title="Free">F</button>
@endfor
@if (!is_null($seats['additional']))
<button class="btn btn-sm btn-dark dd-table-button" type="button"
title="Free ({{ ucfirst($seats['additional']) }})">F{{ ucfirst(substr($seats['additional'],0,1)) }}</button>
@endif
@for ($i = 0; $i < $seats['assistants']; $i++)
<button
class="btn btn-sm bg-info dd-table-button @if ($i >= max($seats['table'] - $seats['dealers'], 1)) border-danger text-danger @endif"
type="button" title="Assistant">A</button>
@endfor
</div>
<div class="mx-auto text-center mb-4">
Legend:
<span class="badge text-bg-primary">Dealer</span>
<span class="badge text-bg-secondary">Free</span>
@if (!is_null($seats['additional']))
<span class="badge text-bg-dark">Free ({{ ucfirst($seats['additional']) }})</span>
@endif
<span class="badge text-bg-info">Assistant</span>
</div>
</div>
@if ($seats['free'] < 0)
<div class="alert alert-danger text-center fw-bold">
You have too many people in your dealership for the number of
seats available for your table size.<br />
Please remove excess shares or assistants.
</div>
@endif
<div class="row">
<div class="col-md-6">
@if ($shares_active_count > $shares_count)
<div class="alert alert-danger fw-bold">You have too many dealers for your table size. <br>Please remove
dealers.</div>
@endif

<div class="card mb-2 @if ($shares_active_count > $shares_count) bg-danger-subtle @endif">
<div class="card mb-2 @if ($seats['free'] < 0) bg-danger-subtle @endif">
<div class="card-body">
<div class="card-title h5 mb-0"><span
class="badge bg-secondary">{{ $shares_active_count }}/{{ $shares_count }}</span> Share your
space with other dealers</div>
<div class="card-title h4 mb-0">
Share your space with other dealers
</div>
</div>
<ul class="list-group list-group-flush">
@if ($shares_active_count < $shares_count && Carbon\Carbon::parse(config('con.reg_end_date'))->isFuture())
@if (
($seats['free'] > 0 || $seats['additional'] === 'dealer') &&
Carbon\Carbon::parse(config('con.reg_end_date'))->isFuture())
<li class="list-group-item">
<form method="POST" action="{{ route('applications.invitees.codes') }}">
@csrf
Expand Down Expand Up @@ -76,18 +123,16 @@ class="badge bg-secondary">{{ $shares_active_count }}/{{ $shares_count }}</span>
</div>
</div>
<div class="col-md-6">
@if ($assistants_active_count > $assistants_count)
<div class="alert alert-danger fw-bold">You have too many assistants for your table size. <br>Please
remove assistants.</div>
@endif
<div class="card mb-2 @if ($assistants_active_count > $assistants_count) bg-danger-subtle @endif">
<div class="card mb-2 @if ($seats['free'] < 0 && $seats['additional'] !== 'assistant') bg-danger-subtle @endif">
<div class="card-body">
<div class="card-title h5 mb-0"><span
class="badge bg-secondary">{{ $assistants_active_count }}/{{ $assistants_count }}</span>
Invite assistants to your space</div>
<div class="card-title h4 mb-0">
Invite assistants to your space
</div>
</div>
<ul class="list-group list-group-flush">
@if ($assistants_active_count < $assistants_count && Carbon\Carbon::parse(config('con.assistant_end_date'))->isFuture())
@if (
($seats['free'] > 0 || $seats['additional'] === 'assistant') &&
Carbon\Carbon::parse(config('con.assistant_end_date'))->isFuture())
<li class="list-group-item">
<form method="POST" action="{{ route('applications.invitees.codes') }}">
@csrf
Expand Down

0 comments on commit aea6b61

Please sign in to comment.