Skip to content

Commit

Permalink
feat(frontdesk): allow searching by name or keyword/category (#88)
Browse files Browse the repository at this point in the history
* feat(frontdesk): allow searching by name or keyword/category

* fix(frontdesk): NRE on parent table access

* fix(frontdesk): keypad height with search mode buttons
  • Loading branch information
Fenrikur authored Sep 16, 2024
1 parent 9f7a9fa commit ae88dd5
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 19 deletions.
81 changes: 74 additions & 7 deletions app/Http/Controllers/FrontdeskController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
use App\Http\Requests\CheckInRequest;
use App\Http\Requests\CheckOutRequest;
use App\Http\Requests\CommentRequest;
use App\Models\Category;
use App\Models\Comment;
use App\Models\Keyword;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
Expand All @@ -23,16 +25,79 @@ public function __invoke(Request $request)
$this->authorize('view-any', Application::class);

$search = $request->get('search');
$type = $request->get('type') ?? 'default';

if (empty($search)) {
$categories = null;
if ($type === 'keyword') {
$categories = Category::orderBy('name', 'asc')->get();
}
return view('frontdesk', [
'user' => \Auth::user(),
'search' => null,
'type' => $type,
'applications' => null,
'application' => null,
'applicant' => null
'applicant' => null,
'categories' => $categories,
]);
}

$searchResult = [];
switch ($type) {
case 'name':
$searchResult = $this->nameSearch($search);
break;
case 'keyword':
$searchResult = $this->keywordSearch($search);
break;
case 'default':
default:
$searchResult = $this->defaultSearch($search);
}

return view('frontdesk', array_merge([
'user' => \Auth::user(),
'search' => $search,
'type' => $type,
'showAdditional' => $request->has('show_additional'),
'applications' => null,
'application' => null,
'applicant' => null,
], $searchResult));
}

private function keywordSearch($search)
{
$keywordId = '-1';
$searchString = $search;
if (str_starts_with($search, 'k::')) {
$keywordId = intval(substr($search, 3));
$searchString = Keyword::where('id', $keywordId)?->first()?->name ?? $search;
}
$categoryId = '-1';
if (str_starts_with($search, 'c::')) {
$categoryId = intval(substr($search, 3));
$searchString = Category::where('id', $categoryId)?->first()?->name ?? $search;
}
$applications = Application::whereHas('profile.keywords', function ($query) use ($search, $keywordId) {
$query->where('name', 'like', "%$search%")->orWhere('id', '=', $keywordId);
})->orWhereHas('profile.keywords.category', function ($query) use ($search, $categoryId) {
$query->where('name', 'like', "%$search%")->orWhere('id', '=', $categoryId);
})->get();
return ['applications' => $applications, 'search' => $searchString];
}

private function nameSearch($search)
{
$applications = Application::where('display_name', 'like', "%$search%")->orWhereHas('user', function ($query) use ($search) {
$query->where('name', 'like', "%$search%");
})->get();
return ['applications' => $applications];
}

private function defaultSearch($search)
{
// 1. search by user user_id
// 2. search by user name
$user = User::where('reg_id', $search)->orWhere('name', 'like', $search)->first();
Expand All @@ -41,7 +106,12 @@ public function __invoke(Request $request)
// 2. search by application table_number
// 4. search by dealership display_name
if ($application === null) {
$application = Application::where('table_number', strtoupper($search))->orWhere('display_name', 'like', $search)->first();
$tableNumberVariation1 = preg_replace('/^([a-zA-Z]{1,2}[0-9])([0-9]+)$/', '\1/\2', $search);
$tableNumberVariation2 = preg_replace('/^([a-zA-Z]{1,2})([0-9]+)$/', '\1/\2', $search);
$application = Application::where('table_number', strtoupper($search))
->orWhere('table_number', strtoupper($tableNumberVariation1))
->orWhere('table_number', strtoupper($tableNumberVariation2))
->orWhere('display_name', 'like', $search)->first();
$user = $application ? $application->user : null;
}

Expand All @@ -65,9 +135,7 @@ public function __invoke(Request $request)

$profile = $application ? $application->profile : null;

return view('frontdesk', [
'user' => \Auth::user(),
'search' => $search,
return [
'application' => $application,
'applicant' => $user,
'table' => $table,
Expand All @@ -76,8 +144,7 @@ public function __invoke(Request $request)
'shares' => $shares,
'assistants' => $assistants,
'profile' => $profile,
'showAdditional' => $request->has('show_additional'),
]);
];
}

public function comment(CommentRequest $request)
Expand Down
31 changes: 31 additions & 0 deletions resources/views/components/frontdesk/keywords.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@props(['categories'])
<div class="accordion" id="keywords">
@foreach ($categories as $category)
<div class="accordion-item">
<h2 class="accordion-header d-flex align-items-center">
<a class="flex-shrink-1 btn btn-outline-primary fs-3" href="?type=keyword&search=c::{{ $category->id }}">
🔎
</a>
<button class="accordion-button collapsed w-100 fs-3" type="button" data-bs-toggle="collapse"
data-bs-target="#category-{{ $loop->index }}" aria-expanded="false"
aria-controls="category-{{ $loop->index }}">
{{ $category->name }}
</button>
</h2>
<div id="category-{{ $loop->index }}" class="accordion-collapse collapse">
<div class="accordion-body">
<div class="list-group">
@foreach ($category->keywords()->get() as $keyword)
<a class="list-group-item" href="?type=keyword&search=k::{{ $keyword->id }}">
🔎 {{ $keyword->name }}
</a>
@endforeach
</div>
<div class="form-text">
{{ $category->description }}
</div>
</div>
</div>
</div>
@endforeach
</div>
95 changes: 83 additions & 12 deletions resources/views/frontdesk.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,27 @@ class="btn btn-secondary">Dealer Profiles ⎋</a>
<!-- Numpad & Search Column -->
<div class="col-md-3 mh-100">
<form method="get" action="{{ route('frontdesk') }}" name="search" autocomplete="off">
<div class="container" style="height: 8% !important">
<div class="row row-cols-3 h-100">
<button type="button"
onclick="document.forms.search.elements.type.value = 'default'; document.forms.search.submit();"
class="btn h-100 border align-self-center @if ($type === 'default') btn-primary @else btn-secondary @endif"
tabindex="-1">Def</button>
<button type="button"
onclick="document.forms.search.elements.type.value = 'name'; document.forms.search.submit();"
class="btn h-100 border align-self-center @if ($type === 'name') btn-primary @else btn-secondary @endif"
tabindex="-1">Name</button>
<button type="button"
onclick="document.forms.search.elements.type.value = 'keyword'; document.forms.search.submit();"
class="btn h-100 border align-self-center @if ($type === 'keyword') btn-primary @else btn-secondary @endif"
tabindex="-1">Key</button>
</div>
</div>
<input type="text" class="form-control my-2 text-center fs-1" id="search" name="search"
tabindex="1" autofocus>
<input type="hidden" name="type" value="{{ app('request')->input('type') ?? 'default' }}">
</form>
<div class="container text-center h-75">
<div class="container text-center" style="height: 68% !important">
<div class="row row-cols-3 h-25">
<button type="button"
onclick="document.forms.search.elements.search.value = document.forms.search.elements.search.value + this.innerHTML;"
Expand Down Expand Up @@ -140,7 +157,7 @@ class="btn btn-primary align-self-center h-100 border fs-1" tabindex="-1">3</but
</div>
<div class="row row-cols-3 h-25">
<button type="button" class="btn btn-danger align-self-center h-100 border fs-1"
onclick="document.forms.search.reset();document.forms.search.submit()"
onclick="document.forms.search.reset();document.forms.search.elements.type.value='default';document.forms.search.submit()"
tabindex="-1">✗</button>
<button type="button"
onclick="document.forms.search.elements.search.value = document.forms.search.elements.search.value + this.innerHTML;"
Expand All @@ -153,34 +170,45 @@ class="btn btn-success align-self-center h-100 border fs-1" tabindex="-1">↵</b

<!-- Application Column -->
<div class="col-md-6 fs-3 mh-100 overflow-auto">
@if (empty($search))
@if (empty($search) && $type !== 'keyword')
<div class="card my-2">
<div class="card-header text-center fs-3">
Welcome to the Dealers' Den Frontdesk!
</div>
<div class="card-body fs-4">
You can search Dealers, Shares or Assistants by:
<div class="fs-3"><strong>Def</strong>ault Mode</div>
Search Dealers, Shares or Assistants by:
<ul>
<li><strong>registration ID</strong> (exact match; no checksum!),</li>
<li><strong>attendee nickname</strong> (supports <code>%</code> as wildcard; not
case-sensitive),</li>
<li><strong>table number</strong> (exact match) or</li>
<li><strong>table number</strong> (exact match; slash (/) can be omitted) or</li>
<li><strong>display name</strong> (supports <code>%</code> as wildcard; not
case-sensitive).</li>
</ul>
<div class="fs-3"><strong>Name</strong> Mode</div>
Search for any part of a name or display name.
<div class="fs-3"><strong>Key</strong>word & Category Mode</div>
Search for part of a keyword/category name or select one from the list if
you provide no search text.
</div>
<div class="card-footer fs-5">
The first matching result will be loaded automatically. If it's not who you were looking
for, please try making your search more specific.
<strong>Def</strong>ault mode will always load the first matching result. If it's not
who you
were looking for, please try making your search more specific or use one of the other
search modes.
</div>
</div>
@elseif ($applicant === null)
@elseif(empty($search) && $type === 'keyword')
<x-frontdesk.keywords :categories="$categories"></x-frontdesk.keywords>
@elseif ($applicant === null && ($applications === null || count($applications) === 0))
<div class="card text-bg-danger text-center my-2">
<div class="card-body">
No dealer, share or assistant found for search query <em>"{{ $search }}"</em>.
No dealers, shares or assistants found for search query <em>"{{ $search }}"</em>
in mode <em>{{ $type }}</em>.
</div>
</div>
@else
@elseif ($applicant !== null)
@if ($application->status === \App\Enums\ApplicationStatus::TableAccepted)
<div class="card text-bg-success text-center my-2">
<div class="card-body">
Expand Down Expand Up @@ -264,7 +292,7 @@ class="btn btn-success align-self-center h-100 border fs-1" tabindex="-1">↵</b
<div class="mb-3">
<label for="table" class="form-label">Table</label>
<input type="text" readonly class="form-control fs-4" id="table"
value="{{ $application->table_number }}{{ $table ? '' . $table->name : '' }}"
value="{{ $application->parent?->table_number ?? $application->table_number }}{{ $table ? '' . $table->name : '' }}"
tabindex="-1">
</div>

Expand Down Expand Up @@ -354,7 +382,8 @@ class="btn btn-success align-self-center h-100 border fs-1" tabindex="-1">↵</b
'check-button',
'btn-primary' => $application->is_afterdark,
'btn-secondary' => !$application->is_afterdark,
])>After Dark</button>
])>After
Dark</button>
<button type="button" @class([
'btn',
'fs-4',
Expand Down Expand Up @@ -547,6 +576,48 @@ class="form-check-input @error('power_strip', 'check-out') is-invalid @enderror"
</div>
@endif
</div>
@else
<div class="card my-2">
<div class="card-header text-center fs-3">
Search results for "{{ $search }}"
</div>
<div class="card-body fs-4">
<div class="list-group">
@foreach ($applications as $applicationResult)
<a class="list-group-item list-group-item-action"
href="?type=default&search={{ $applicationResult->user->reg_id }}">
{{ $applicationResult->user->name }}
({{ $applicationResult->user->reg_id }})
[{{ $applicationResult->parent?->table_number ?? $applicationResult->table_number }}]
@switch($applicationResult->type)
@case(\App\Enums\ApplicationType::Dealer)
<span
class="badge text-bg-success mx-2">{{ $applicationResult->type->name }}</span>
@break

@case(\App\Enums\ApplicationType::Share)
<span
class="badge text-bg-warning mx-2">{{ $applicationResult->type->name }}</span>
@break

@case(\App\Enums\ApplicationType::Assistant)
<span
class="badge text-bg-info mx-2">{{ $applicationResult->type->name }}</span>
@break

@default
<span class="badge text-bg-danger mx-2">Unknown Type!</span>
@endswitch
@if ($applicationResult->is_afterdark)
<span class="badge text-bg-dark">ADD</span>
@endif
<span
class="badge text-bg-secondary">{{ $applicationResult->status->name }}</span>
</a>
@endforeach
</div>
</div>
</div>
@endif
</div>

Expand Down

0 comments on commit ae88dd5

Please sign in to comment.