@php use App\Enums\NotificationStatus; use App\Models\JobPhoto; $left = JobPhoto::MAX_PER_JOB - $job->photos->count(); // Data for the full-size viewer. $photoData = $job->photos->map(fn (JobPhoto $p) => [ 'full' => $p->url(), 'caption' => $p->caption, 'info' => collect([ $p->stage->label(), $p->uploadedBy?->name, $p->created_at->timezone(config('app.timezone'))->format('d M Y, g:i A'), ])->filter()->join(' · '), 'updateUrl' => route('jobs.photos.update', [$job, $p]), 'deleteUrl' => auth()->user()->can('delete', $p) ? route('jobs.photos.destroy', [$job, $p]) : null, ])->values(); @endphp ← Job cards

Job {{ $job->job_no }}

@if ($job->priority === \App\Enums\JobPriority::Urgent) Urgent @endif

Received {{ $job->created_at->format('d M Y, g:i A') }} by {{ $job->receivedBy?->name }} · Technician: {{ $job->assignedTo?->name ?? 'Not assigned' }}

{{-- Admin only: edit / delete the job card --}} @canany(['update', 'delete'], $job)
@can('update', $job) Edit job @endcan @can('delete', $job) @endcan
@can('delete', $job)
@csrf @method('DELETE')

Delete job {{ $job->job_no }}? It will be hidden from all lists and the customer's tracking link will stop working. Parts reserved for it go back to stock. Payments and parts already used stay in the records. For a normal job that will not be repaired, change the status to Cancelled instead.

@endcan
@endcanany
@php $action = 'flex min-h-14 flex-col items-center justify-center gap-1 rounded-xl text-sm font-semibold shadow-sm sm:flex-row sm:gap-2 sm:text-base'; $soon = $action.' bg-gray-200 text-gray-500 cursor-not-allowed shadow-none'; @endphp {{-- Tabs: Details / Parts / Messages (front desk). #parts and #messages keep the tab on reload; any other link (#photos) opens Details. --}}
{{-- ========== Ready: hand it over ========== --}} @can('deliver', $job) @php $handoverBalance = \App\Support\Handover::balanceCents($job); @endphp

Ready for pickup

@if ($handoverBalance > 0) Balance to pay: {{ \App\Support\Money::format(\App\Support\Money::fromCents($handoverBalance)) }} @else Nothing left to pay. @endif

Deliver device
@endcan {{-- ========== Big action buttons ========== --}}
@include('jobs.partials.change-status', ['class' => $action]) @include('jobs.partials.job-card-buttons', ['class' => $action]) Print sticker @can('addPhotos', $job) Photos @endcan @can('use-front-desk') Call customer @endcan
@php $tabClass = 'flex flex-1 min-h-11 items-center justify-center gap-2 rounded-md px-3 text-sm font-semibold'; $partsCount = $job->jobParts()->where('status', \App\Enums\JobPartStatus::Used)->count() + count(\App\Actions\JobParts::reservedFor($job)); @endphp @can('use-front-desk')
@include('jobs.partials.messages')
@endcan
@include('jobs.partials.parts')
@include('jobs.partials.assign-technician') @include('jobs.partials.diagnosis') @include('jobs.partials.quotations') @include('jobs.partials.notes') @include('jobs.partials.warranty') @include('jobs.partials.summary') {{-- ========== Handover (after delivery) ========== --}} @if ($job->status === \App\Enums\JobStatus::Delivered && $job->collected_by_name)

Handover

Delivered
{{ $job->delivered_at?->format('d M Y, g:i A') }} by {{ $job->deliveredBy?->name }}
Collected by
{{ $job->collected_by_name }}@if ($job->collected_by_phone) · {{ \App\Support\Phone::display($job->collected_by_phone) }}@endif
@php $given = collect($job->handoverItems())->only($job->accessories_returned ?? []); $notGiven = collect($job->handoverItems())->except($job->accessories_returned ?? []); @endphp @if ($given->isNotEmpty() || $notGiven->isNotEmpty())
Given back
{{ $given->join(', ') ?: 'Nothing' }}
@if ($notGiven->isNotEmpty())
Not given back
{{ $notGiven->join(', ') }}
@endif @endif @if ($job->handover_notes)
Notes
{{ $job->handover_notes }}
@endif @can('view-billing') @if ((float) $job->balance_at_delivery > 0)
Left with a balance
{{ \App\Support\Money::format($job->balance_at_delivery) }} · allowed by {{ $job->balanceOverrideBy?->name }}: {{ $job->balance_override_reason }}
@endif @endcan
@if ($job->signature_path && auth()->user()->can('use-front-desk'))

Signature

Customer signature
@endif
@endif {{-- ========== QR code: opens the customer tracking page ========== --}}
QR code for job {{ $job->job_no }}

QR code

The customer scans this to see the repair status.

{{ $job->trackingUrl() }}

{{-- ========== Status timeline ========== --}}

Timeline

    @foreach ($job->statusHistories->sortByDesc('id') as $history)
  1. @unless ($loop->last) @endunless $loop->first, 'bg-gray-300 ring-gray-100' => ! $loop->first, ])>

    {{ $history->created_at->format('d M Y, g:i A') }} · {{ $history->changedBy?->name ?? 'System' }} @unless ($history->is_public) · staff only @endunless

    @if ($history->note)

    {{ $history->note }}

    @endif
  2. @endforeach
{{-- ========== Invoice (never shown to technicians) ========== --}} @can('view-billing') @php $invoices = $job->invoices()->with('payments')->get(); @endphp

Invoice

@can('create', [\App\Models\Invoice::class, $job]) Create invoice @endcan
@if ($invoices->isEmpty())

No invoice yet. "Create invoice" fills in the approved quotation and the parts used.

@else
    @foreach ($invoices->sortByDesc('id') as $inv)
  • {{ $inv->displayNo() }} {{ $inv->status->label() }}@if ($inv->isIssued()) · {{ $inv->payment_status->label() }} @endif · Total Rs. {{ number_format($inv->total, 2) }} @if ($inv->isIssued() && (float) $inv->balance() > 0) · Balance Rs. {{ number_format($inv->balance(), 2) }} @endif
  • @endforeach
@endif
@endcan {{-- ========== Payments (never shown to technicians) ========== --}} @can('view-billing')

Payments

@php $paid = $job->payments->sum(fn ($p) => $p->type === \App\Enums\PaymentType::Refund ? -$p->amount : $p->amount); @endphp Paid so far: Rs. {{ number_format($paid, 2) }}
@if ($job->payments->isEmpty())

No payments yet.

@else
    @foreach ($job->payments->sortBy('id') as $payment)
  • {{ $payment->type->label() }} · {{ $payment->method->label() }}

    Receipt {{ $payment->receipt_no }} · {{ $payment->paid_at->format('d M Y, g:i A') }} · {{ $payment->receivedBy?->name }}

    $payment->type === \App\Enums\PaymentType::Refund])> {{ $payment->type === \App\Enums\PaymentType::Refund ? '−' : '' }}Rs. {{ number_format($payment->amount, 2) }}

  • @endforeach
@endif
@endcan {{-- ========== Photos ========== --}}

Device photos

{{ $job->photos->count() }} of {{ JobPhoto::MAX_PER_JOB }}
@if ($job->photos->isEmpty())

No photos yet.

@else
    @foreach ($job->photos as $i => $photo)
  • {{-- Works without JavaScript too: opens the full photo. --}} {{ $photo->caption ?: 'Device photo '.($i + 1) }} {{ $photo->stage->label() }} @if ($photo->caption)

    {{ $photo->caption }}

    @endif
  • @endforeach
@endif {{-- Add more photos --}} @can('addPhotos', $job) @if ($left > 0)
@csrf

Add photos

@else

This job has the most photos allowed ({{ JobPhoto::MAX_PER_JOB }}). Delete one to add another.

@endif @endcan {{-- Full-size viewer --}}
{{-- /details --}}
@can('use-front-desk') @include('jobs.partials.whatsapp-update') @endcan