Deploy Ladill Frontdesk / deploy (push) Successful in 50s
Co-authored-by: Cursor <cursoragent@cursor.com>
213 lines
7.8 KiB
PHP
213 lines
7.8 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Frontdesk;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Controllers\Frontdesk\Concerns\ScopesToAccount;
|
|
use App\Models\AuditLog;
|
|
use App\Models\Branch;
|
|
use App\Models\Host;
|
|
use App\Models\Visitor;
|
|
use App\Services\Frontdesk\FrontdeskPermissions;
|
|
use App\Services\Frontdesk\VisitCheckInService;
|
|
use App\Services\Frontdesk\WatchlistService;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\View\View;
|
|
|
|
class VisitorController extends Controller
|
|
{
|
|
use ScopesToAccount;
|
|
|
|
public function index(Request $request): View
|
|
{
|
|
$this->authorizeAbility($request, 'visitors.view');
|
|
$organization = $this->organization($request);
|
|
|
|
$visitors = Visitor::owned($this->ownerRef($request))
|
|
->where('organization_id', $organization->id)
|
|
->when($request->q, fn ($q, $search) => $q->where('full_name', 'like', "%{$search}%"))
|
|
->orderByDesc('is_frequent')
|
|
->orderBy('full_name')
|
|
->paginate(25)
|
|
->withQueryString();
|
|
|
|
$statsQuery = Visitor::owned($this->ownerRef($request))
|
|
->where('organization_id', $organization->id);
|
|
|
|
$heroStats = [
|
|
'total' => (clone $statsQuery)->count(),
|
|
'frequent' => (clone $statsQuery)->where('is_frequent', true)->count(),
|
|
'flagged' => (clone $statsQuery)->whereIn('watchlist_status', [
|
|
Visitor::WATCHLIST_REQUIRES_APPROVAL,
|
|
Visitor::WATCHLIST_BLACKLISTED,
|
|
])->count(),
|
|
];
|
|
|
|
return view('frontdesk.visitors.index', compact('visitors', 'organization', 'heroStats'));
|
|
}
|
|
|
|
public function show(Request $request, Visitor $visitor): View
|
|
{
|
|
$this->authorizeAbility($request, 'visitors.view');
|
|
$this->authorizeOwner($request, $visitor);
|
|
$visitor->load(['visits' => fn ($q) => $q->latest()->limit(20), 'visits.host']);
|
|
|
|
$visitIds = $visitor->visits()->pluck('id');
|
|
|
|
$activity = AuditLog::owned($this->ownerRef($request))
|
|
->where('organization_id', $visitor->organization_id)
|
|
->where(function ($q) use ($visitor, $visitIds) {
|
|
$q->where(function ($inner) use ($visitor) {
|
|
$inner->where('subject_type', Visitor::class)
|
|
->where('subject_id', $visitor->id);
|
|
});
|
|
|
|
if ($visitIds->isNotEmpty()) {
|
|
$q->orWhere(function ($inner) use ($visitIds) {
|
|
$inner->where('subject_type', Visit::class)
|
|
->whereIn('subject_id', $visitIds);
|
|
});
|
|
}
|
|
})
|
|
->orderByDesc('created_at')
|
|
->limit(30)
|
|
->get();
|
|
|
|
return view('frontdesk.visitors.show', [
|
|
'visitor' => $visitor,
|
|
'activity' => $activity,
|
|
'auditActions' => config('frontdesk.audit_actions'),
|
|
'watchlistStatuses' => config('frontdesk.watchlist_statuses'),
|
|
'canManage' => app(FrontdeskPermissions::class)->can(
|
|
$this->member($request),
|
|
'visitors.manage'
|
|
),
|
|
]);
|
|
}
|
|
|
|
public function checkInForm(Request $request, Visitor $visitor): View
|
|
{
|
|
$this->authorizeAbility($request, 'visits.manage');
|
|
$this->authorizeOwner($request, $visitor);
|
|
$organization = $this->organization($request);
|
|
|
|
$hosts = Host::owned($this->ownerRef($request))
|
|
->where('organization_id', $organization->id);
|
|
$this->scopeToBranch($request, $hosts);
|
|
$hosts = $hosts->orderBy('name')->get();
|
|
|
|
$branches = Branch::owned($this->ownerRef($request))
|
|
->where('organization_id', $organization->id)
|
|
->where('is_active', true)
|
|
->orderBy('name')
|
|
->get();
|
|
|
|
return view('frontdesk.visitors.check-in', compact('visitor', 'organization', 'hosts', 'branches'));
|
|
}
|
|
|
|
public function checkIn(Request $request, Visitor $visitor, VisitCheckInService $checkIn): RedirectResponse
|
|
{
|
|
$this->authorizeAbility($request, 'visits.manage');
|
|
$this->authorizeOwner($request, $visitor);
|
|
$organization = $this->organization($request);
|
|
|
|
$validated = $request->validate([
|
|
'host_id' => ['nullable', 'integer'],
|
|
'branch_id' => ['nullable', 'integer'],
|
|
'visitor_type' => ['required', 'string', 'in:'.implode(',', array_keys(config('frontdesk.visitor_types')))],
|
|
'purpose' => ['nullable', 'string', 'max:500'],
|
|
'policies_accepted' => ['accepted'],
|
|
]);
|
|
|
|
$branchScope = app(\App\Services\Frontdesk\OrganizationResolver::class)
|
|
->branchScope($this->member($request));
|
|
if ($branchScope !== null) {
|
|
$validated['branch_id'] = $branchScope;
|
|
}
|
|
|
|
$validated['visitor_id'] = $visitor->id;
|
|
|
|
$visit = $checkIn->checkIn(
|
|
$this->ownerRef($request),
|
|
$organization,
|
|
$validated,
|
|
$this->ownerRef($request),
|
|
);
|
|
|
|
return redirect()
|
|
->route('frontdesk.visits.show', $visit)
|
|
->with('success', 'Returning visitor checked in.');
|
|
}
|
|
|
|
public function update(Request $request, Visitor $visitor): RedirectResponse
|
|
{
|
|
$this->authorizeAbility($request, 'visitors.manage');
|
|
$this->authorizeOwner($request, $visitor);
|
|
|
|
$validated = $request->validate([
|
|
'full_name' => ['required', 'string', 'max:255'],
|
|
'company' => ['nullable', 'string', 'max:255'],
|
|
'phone' => ['nullable', 'string', 'max:50'],
|
|
'email' => ['nullable', 'email', 'max:255'],
|
|
'notes' => ['nullable', 'string', 'max:2000'],
|
|
'photo' => ['nullable', 'image', 'max:5120'],
|
|
'id_document' => ['nullable', 'file', 'mimes:pdf,jpg,jpeg,png', 'max:10240'],
|
|
]);
|
|
|
|
if ($request->hasFile('photo')) {
|
|
if ($visitor->photo_path) {
|
|
Storage::disk('public')->delete($visitor->photo_path);
|
|
}
|
|
$validated['photo_path'] = $request->file('photo')->store('frontdesk/visitors/photos', 'public');
|
|
}
|
|
|
|
if ($request->hasFile('id_document')) {
|
|
if ($visitor->id_document_path) {
|
|
Storage::disk('public')->delete($visitor->id_document_path);
|
|
}
|
|
$validated['id_document_path'] = $request->file('id_document')->store('frontdesk/visitors/documents', 'public');
|
|
}
|
|
|
|
unset($validated['photo'], $validated['id_document']);
|
|
|
|
$visitor->update($validated);
|
|
|
|
return back()->with('success', 'Visitor profile updated.');
|
|
}
|
|
|
|
public function updateWatchlist(Request $request, Visitor $visitor, WatchlistService $watchlist): RedirectResponse
|
|
{
|
|
$this->authorizeAbility($request, 'visitors.manage');
|
|
$this->authorizeOwner($request, $visitor);
|
|
$organization = $this->organization($request);
|
|
|
|
$validated = $request->validate([
|
|
'watchlist_status' => ['required', 'string', 'in:'.implode(',', array_keys(config('frontdesk.watchlist_statuses')))],
|
|
'notes' => ['nullable', 'string', 'max:2000'],
|
|
]);
|
|
|
|
$visitor->update($validated);
|
|
|
|
$watchlist->syncEntryForVisitor(
|
|
$visitor,
|
|
$validated['watchlist_status'],
|
|
$validated['notes'] ?? null,
|
|
$this->ownerRef($request),
|
|
);
|
|
|
|
AuditLog::record(
|
|
$this->ownerRef($request),
|
|
'watchlist.entry_created',
|
|
$organization->id,
|
|
$this->ownerRef($request),
|
|
Visitor::class,
|
|
$visitor->id,
|
|
['watchlist_status' => $validated['watchlist_status']],
|
|
);
|
|
|
|
return back()->with('success', 'Visitor watchlist status updated.');
|
|
}
|
|
}
|