Stage 3 — WO photos + ETA + QR + public tracking page
- HasMedia (Spatie) on WorkOrder with `photos` collection
- eta_at + tracking_token columns; token auto-generated on create
- Public /t/{token} page — tenant-scoped via subdomain, white-label themed
- QR code SVG via chillerlan/php-qrcode (inline modal + download)
- Filament: SpatieMediaLibraryFileUpload + ETA picker + tracking section
- EditWorkOrder header action "Link client (QR)" modal
- Fix: Auditable::dontSubmitEmptyLogs() → dontLogEmptyChanges() (removed in activitylog)
- Tests: TrackingPageTest (4 pass) covering token gen + cross-tenant isolation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Tenant\WorkOrder;
|
||||
use App\Tenancy\TenantManager;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class TrackingController extends Controller
|
||||
{
|
||||
/**
|
||||
* Public WO tracking page — accessed via QR code or SMS link.
|
||||
* Tenant is resolved by ResolveTenant from the host, so the global
|
||||
* BelongsToTenant scope already filters to the correct tenant.
|
||||
*/
|
||||
public function show(Request $request, string $token)
|
||||
{
|
||||
$tenant = app(TenantManager::class)->current();
|
||||
if (! $tenant) {
|
||||
throw new NotFoundHttpException('Tracking only available on tenant subdomain.');
|
||||
}
|
||||
|
||||
$wo = WorkOrder::with(['client', 'vehicle', 'master', 'media'])
|
||||
->where('tracking_token', $token)
|
||||
->first();
|
||||
|
||||
if (! $wo) {
|
||||
throw new NotFoundHttpException('Fișa nu a fost găsită.');
|
||||
}
|
||||
|
||||
return view('tracking.show', [
|
||||
'wo' => $wo,
|
||||
'tenant' => $tenant,
|
||||
'photos' => $wo->getMedia('photos'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function qr(Request $request, string $token)
|
||||
{
|
||||
$tenant = app(TenantManager::class)->current();
|
||||
if (! $tenant) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$wo = WorkOrder::where('tracking_token', $token)->first();
|
||||
if (! $wo) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
$options = new \chillerlan\QRCode\QROptions([
|
||||
'outputType' => \chillerlan\QRCode\QRCode::OUTPUT_MARKUP_SVG,
|
||||
'eccLevel' => \chillerlan\QRCode\QRCode::ECC_M,
|
||||
'scale' => 6,
|
||||
'imageBase64' => false,
|
||||
'svgViewBoxSize' => 200,
|
||||
'addQuietzone' => true,
|
||||
]);
|
||||
|
||||
$svg = (new \chillerlan\QRCode\QRCode($options))->render($wo->trackingUrl());
|
||||
|
||||
return response($svg, 200, [
|
||||
'Content-Type' => 'image/svg+xml',
|
||||
'Cache-Control' => 'public, max-age=3600',
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user