@php use Illuminate\Support\Carbon; use Illuminate\Support\Str; // Labels come from Job::STATUS_LABELS; only the colours live here. $statusMeta = [ 'pending' => ['label' => App\Models\Job::statusLabel('pending'), 'color' => 'slate', 'dot' => 'bg-slate-400'], 'in_production' => ['label' => App\Models\Job::statusLabel('in_production'), 'color' => 'sky', 'dot' => 'bg-sky-500'], 'on_hold' => ['label' => App\Models\Job::statusLabel('on_hold'), 'color' => 'amber', 'dot' => 'bg-amber-500'], 'completed' => ['label' => App\Models\Job::statusLabel('completed'), 'color' => 'emerald', 'dot' => 'bg-emerald-500'], 'ready_for_collection' => ['label' => App\Models\Job::statusLabel('ready_for_collection'), 'color' => 'amber', 'dot' => 'bg-amber-400'], 'delivered' => ['label' => App\Models\Job::statusLabel('delivered'), 'color' => 'violet', 'dot' => 'bg-violet-500'], 'closed' => ['label' => App\Models\Job::statusLabel('closed'), 'color' => 'slate', 'dot' => 'bg-slate-500'], 'cancelled' => ['label' => App\Models\Job::statusLabel('cancelled'), 'color' => 'rose', 'dot' => 'bg-rose-400'], ]; // Commercial state (spec §2), the funding dimension, separate from status. $commercialMeta = [ 'not_funded' => ['label' => __('Not Funded'), 'color' => 'slate'], 'deposit_paid' => ['label' => __('Deposit Paid'), 'color' => 'amber'], 'fully_paid' => ['label' => __('Fully Paid'), 'color' => 'emerald'], ]; $priorityColor = ['low' => 'slate', 'normal' => 'sky', 'high' => 'amber', 'urgent' => 'rose']; $fulfilmentMeta = [ 'internal' => ['label' => __('Internal'), 'color' => 'slate'], 'outsourced' => ['label' => __('Outsourced'), 'color' => 'rose'], 'hybrid' => ['label' => __('Hybrid'), 'color' => 'violet'], ]; $initials = function (?string $name): string { $words = preg_split('/\s+/', trim((string) $name)) ?: []; return strtoupper(Str::substr($words[0] ?? '?', 0, 1) . (isset($words[1]) ? Str::substr($words[1], 0, 1) : '')) ?: '?'; }; $dueLabel = null; // Once the work is made the countdown stops being the story. if ($job->due_date && ! in_array($job->status, ['completed', 'ready_for_collection', 'delivered', 'closed'], true)) { $d = (int) Carbon::today()->diffInDays($job->due_date->copy()->startOfDay(), false); if ($d < 0) $dueLabel = ['t' => __(':n days overdue', ['n' => abs($d)]), 'c' => 'text-rose-500']; elseif ($d === 0) $dueLabel = ['t' => __('Due today'), 'c' => 'text-rose-500']; elseif ($d <= 3) $dueLabel = ['t' => __(':n days left', ['n' => $d]), 'c' => 'text-amber-600']; else $dueLabel = ['t' => __(':n days left', ['n' => $d]), 'c' => 'text-slate-400']; } $tabs = [ 'overview' => ['label' => __('Overview'), 'icon' => 'squares-2x2'], 'details' => ['label' => __('Details'), 'icon' => 'identification'], 'products' => ['label' => __('Products'), 'icon' => 'cube'], 'financials' => ['label' => __('Costs & Materials'), 'icon' => 'currency-dollar'], 'production' => ['label' => __('Workflow'), 'icon' => 'cog-6-tooth'], 'checklist' => ['label' => __('Quality'), 'icon' => 'shield-check'], 'time' => ['label' => __('Time Tracking'), 'icon' => 'clock'], 'files' => ['label' => __('Files'), 'icon' => 'document-text'], 'activity' => ['label' => __('Activity'), 'icon' => 'chart-bar'], 'notes' => ['label' => __('Notes'), 'icon' => 'pencil-square'], ]; // Literal class strings (Tailwind scans these in source, never interpolate fragments). $tlChip = [ 'indigo' => 'bg-indigo-50 text-indigo-600', 'sky' => 'bg-sky-50 text-sky-600', 'violet' => 'bg-violet-50 text-violet-600', 'emerald' => 'bg-emerald-50 text-emerald-600', 'amber' => 'bg-amber-50 text-amber-600', 'rose' => 'bg-rose-50 text-rose-600', 'slate' => 'bg-slate-100 text-slate-500', ]; $ratingText = [ 'emerald' => 'text-emerald-600', 'sky' => 'text-sky-600', 'amber' => 'text-amber-600', 'rose' => 'text-rose-600', 'slate' => 'text-slate-500', ]; @endphp
{{ __('Ready for collection') }}
@if ($awaitingCollectionSince) {{ __('Finished on :date and waiting at the counter for :customer.', ['date' => $awaitingCollectionSince->format('d M Y'), 'customer' => $job->customer?->name ?: __('the customer')]) }} @else {{ __('Waiting at the counter for :customer.', ['customer' => $job->customer?->name ?: __('the customer')]) }} @endif
{{ __('This job is finished but no materials were recorded') }}
{{ __('No stock was deducted and no material cost was logged. Did you use any stock? Add it under Production, or confirm none were needed.') }}
{{ $tile['label'] }}
@if (! empty($tile['href'])) {{ $tile['value'] }} @else {{ $tile['value'] }} @endif
@if (! empty($tile['pill'])) {{ $tile['pill'] }} @endif @if (isset($tile['bar'])){{ $tile['caption'] }}
@endif @if (! empty($tile['action'])) @endif{{ $event['title'] }}
@if ($event['by']){{ __('by :name', ['name' => $event['by']]) }}
@endif{{ __('Visible to management only.') }}
{{ $label }}
“{{ $feedback->comment }}”@endif
@if ($job->client_portal_sent_at) {{ __('Review invite sent :date, awaiting the client’s feedback.', ['date' => $job->client_portal_sent_at->format('Y-m-d')]) }} @else {{ __('No feedback yet. The client is invited automatically when the job is marked Delivered.') }} @endif
{{-- The review chase applies from the moment the customer has the goods, whether that was a delivery or a counter collection. --}} @if (in_array($job->status, App\Models\Job::HANDED_OVER_STATUSES, true)) @php $waMessage = __(':company, your order :job is ready! View it and leave us a quick review here: :url', [ 'company' => $job->team?->name, 'job' => $job->job_number, 'url' => $portalUrl, ]); $waLink = \App\Support\WhatsApp::link($job->customer?->phone, $waMessage); @endphp{{ __('Everything on record for this job.') }}
{{ $job->description }}
{{ __('Invoice billed in :code :total', ['code' => $invoiceNative['code'], 'total' => number_format($invoiceNative['total'], 2)]) }}
@endif {{-- Per-product P&L. Shown whenever the sale has more than one product, because that is exactly when one blended margin hides a loss-maker. --}} @if ($productProfit && count($productProfit['rows']) > 1) @include('livewire.jobs.partials.product-profit') @endif {{-- Sub-tab nav --}}{{ $u->name }}
{{ $u->email }}
{{ number_format($mh, 2) }} h
@endif{{ $event['title'] }}
@if ($event['by']){{ __('by :name', ['name' => $event['by']]) }}
@endif{{ __('Who sold this job. Commission follows the rep, not whoever raised the invoice.') }}
{{ __('Mark which products go out, and to which partner, on the Products tab.') }}
@endif{{ __('A job is created from a sale, pick the invoice it fulfils.') }}
{{ __('This invoice has no line items to build a job from.') }}
@else{{ __('Tick which product(s) this job is for.') }}
{{ __('A job can only be closed once it has been billed. Closing an unbilled job writes off work nobody will ever chase.') }}
{{ __('Production can\'t start on ":status" because this job\'s invoice has no payment yet. Record a deposit on the invoice, or override to start anyway.', ['status' => $statusMeta[$gateTarget]['label'] ?? $gateTarget]) }}
{{ __('Before this job can move to ":status", these required quality checks must be signed off:', ['status' => $statusMeta[$gateTarget]['label'] ?? $gateTarget]) }}
{{ __('Open the Quality tab to complete them.') }}
@endif @if ($canOverride){{ __('Only an owner or admin can override this gate.') }}
@endif