@php $kpiIcon = [ 'indigo' => 'bg-indigo-50 text-indigo-600', 'amber' => 'bg-amber-50 text-amber-600', 'emerald' => 'bg-emerald-50 text-emerald-600', 'sky' => 'bg-sky-50 text-sky-600', ]; // Both lanes share the status column, so this covers the counter path // (printing → ready → collected) and the quote path (quoting → quoted → // converted). Unknown values fall back rather than blowing up the row. $statusMeta = [ 'new' => ['label' => __('New'), 'badge' => 'bg-indigo-50 text-indigo-700 ring-indigo-200'], 'printing' => ['label' => __('Printing'), 'badge' => 'bg-sky-50 text-sky-700 ring-sky-200'], 'ready' => ['label' => __('Ready'), 'badge' => 'bg-emerald-50 text-emerald-700 ring-emerald-200'], 'completed' => ['label' => __('Collected'), 'badge' => 'bg-slate-100 text-slate-600 ring-slate-200'], 'cancelled' => ['label' => __('Cancelled'), 'badge' => 'bg-rose-50 text-rose-700 ring-rose-200'], 'quoting' => ['label' => __('Being quoted'), 'badge' => 'bg-sky-50 text-sky-700 ring-sky-200'], 'quoted' => ['label' => __('Quote sent'), 'badge' => 'bg-amber-50 text-amber-700 ring-amber-200'], 'converted' => ['label' => __('Accepted'), 'badge' => 'bg-emerald-50 text-emerald-700 ring-emerald-200'], ]; $statusFor = fn ($s) => $statusMeta[$s] ?? ['label' => ucfirst((string) $s), 'badge' => 'bg-slate-100 text-slate-600 ring-slate-200']; $money = fn ($n, $cur = 'USD') => $cur . ' ' . number_format((float) $n, 2); @endphp
{{-- Paper that went to nobody's bill (a customer's invoice printed for them): off the shelf, no sale. --}} {{-- The customer who walked in without scanning anything. --}} {{ __('New sale') }}
{{-- ========================== NEW SALE (TILL) ========================== --}} {{ __('New sale') }}

{{ __('For the customer standing at the counter. Take their details, then add what they are paying for.') }}

{{ __('Needed only if they might leave change behind.') }}

@foreach (['cash' => __('Cash'), 'ecocash' => __('EcoCash')] as $method => $label) @endforeach
{{ __('Cancel') }} {{ __('Start sale') }} {{ __('Starting…') }}
{{-- ===================== CHANGE WALLET LOOKUP ====================== --}} {{ __('Change wallet') }}

{{ __('A customer who overpaid left their change here. Look it up by the phone number they gave.') }}

@php $walletUsable = $this->walletPhoneIsUsable(); $totals = $this->walletTotals(); $entries = $this->walletEntries(); @endphp @if (! $walletUsable)

{{ __('Type a phone number to see what is waiting.') }}

@elseif (empty($totals))

{{ __('No change is being held for this number.') }}

{{ __('It may have already been used, or the week may have passed.') }}

@else
@foreach ($totals as $currency => $amount)

{{ $currency }} {{ number_format($amount, 2) }}

@endforeach

{{ __('Applied automatically on their next cash job at this number.') }}

    @foreach ($entries as $entry)
  • {{ $entry['currency'] }} {{ number_format($entry['remaining'], 2) }}

    {{ $entry['reference'] ? __('Change from :ref', ['ref' => $entry['reference']]) : __('Change held') }}

    @if ($entry['held_since']) {{ __('Since :date', ['date' => $entry['held_since']->timezone(config('app.display_timezone'))->format('j M Y')]) }} @endif
  • @endforeach
@endif
{{ __('Close') }}
{{-- ========================= SERVICES MODAL ========================= --}} {{ __('Services on your intake link') }}

{{ __('What customers can choose from when they scan your QR code. Switch off anything you do not offer and it stops appearing.') }}

@foreach ($serviceRows as $i => $row)
{{ $row['lane'] === 'counter' ? __('Pays here') : __('Quote') }}
@if ($row['lane'] !== 'counter') @endif
@if (! empty($row['fields']))
{{ trans_choice('{1}1 question|[2,*]:count questions', count($row['fields']), ['count' => count($row['fields'])]) }}
@foreach ($row['fields'] as $f => $field)
@if (in_array($field['block'], ['choice', 'multi_choice'], true)) @endif
@endforeach

{{ __('You can reword a question and change its options. Which questions a service asks is fixed, so every answer still lands on the right part of the quote.') }}

@endif
@endforeach
{{ __('Cancel') }} {{ __('Save services') }}
{{-- Intake status strip --}} @if (! $setting->print_intake_enabled)
{{ __('The public intake link is turned off, customers cannot submit yet.') }}
@elseif ($intakeUrl)
{{ __('Intake is live. Share the QR code or link with walk-in customers.') }}
{{ __('Poster') }}
@endif {{-- KPI cards --}}
@foreach ($kpis as $kpi)

{{ $kpi['label'] }}

{{ $kpi['value'] }}

{{ $kpi['sub'] }}

@endforeach
{{-- Filters --}}
@if ($services->count() > 1) @endif {{-- Delete asks waiting on a director. Shown only while there are any (or the filter is on), so the bar stays quiet the rest of the time. --}} @if ($pendingDeletions > 0 || $pendingFilter) @endif
{{-- List --}}
@forelse ($requests as $req) @php $sm = $statusFor($req->status); @endphp @empty

{{ __('No print requests yet. Share your intake QR code to get started.') }}

@endforelse
{{ $requests->links() }}
{{-- ========================= DETAIL MODAL ========================= --}} @if ($detail) {{ $detail->customer_name }} {{ $detail->reference }} @endif @if ($detail) @php $sm = $statusFor($detail->status); @endphp
{{-- Top line: status + payment --}}
{{ $sm['label'] }} @if ($detail->isQuoteLane()) {{ $detail->intakeService?->name ?: __('Enquiry') }} @elseif ($detail->isPaid()) {{ __('Paid') }} · {{ $detail->paid_at?->timezone(config('app.display_timezone'))->format('j M, H:i') }} @else {{ __('Unpaid') }} @endif @if ($detail->completed_at) {{ __('Done') }} · {{ $detail->completed_at->timezone(config('app.display_timezone'))->format('j M Y, H:i') }} @endif @if ($detail->contact_phone) {{ $detail->contact_phone }} @endif
{{-- Deletion ask, waiting on a director --}} @if ($detail->hasPendingDeletion())

{{ __(':who asked for this request to be deleted', ['who' => $detail->deletionRequester?->name ?? __('A staff member')]) }} · {{ $detail->deletion_requested_at?->diffForHumans() }}

{{ $detail->deletion_reason }}

{{ $canDeleteOutright ? __('Approving deletes it. If it was paid, the income and stock it booked are reversed.') : __('Nothing changes until a director approves or declines it.') }}

@elseif ($detail->deletion_declined_at)

{{ __('Delete request declined by :who', ['who' => $detail->deletionDecliner?->name ?? __('a director')]) }} · {{ $detail->deletion_declined_at->diffForHumans() }}

@if ($detail->deletion_decline_reason)

{{ $detail->deletion_decline_reason }}

@endif
@endif {{-- ============ THE BRIEF (quote lane) ============ --}} @if ($detail->isQuoteLane()) @php $answers = $detail->answerSummary(); @endphp

{{ __('What they asked for') }}

@forelse ($answers as $row)
{{ $row['label'] }}
{{ $row['value'] }}
@empty

{{ __('No answers recorded.') }}

@endforelse @if ($detail->needed_by)
{{ __('Needed by') }}
{{ $detail->needed_by->format('j M Y') }}
@endif
{{-- Where this enquiry has got to in the pipeline. --}}
@if ($detail->quote) {{ __('Quote :n', ['n' => $detail->quote->quote_number]) }} @elseif ($detail->opportunity_id && $detail->status === \App\Models\ServiceRequest::STATUS_QUOTING)
@if ($canQuote) @endif {{ __('Open the opportunity') }}

{{ __('The quote starts with the answers above on its line. Add your prices.') }}

@else
@if ($detail->opportunity_id) {{ __('Open the opportunity') }} @endif

{{ $detail->opportunity_id ? __('Already in the pipeline as a prospect. Start quoting to move this request to Quoting.') : __('Opens a prospect organisation and an opportunity owned by you, then moves this request to Quoting.') }}

@endif
@endif {{-- Files --}}

{{ $detail->isQuoteLane() ? __('Artwork & references') : __('Files to print') }}

@if ($detail->files->isEmpty())

{{ $detail->isQuoteLane() ? __('Nothing attached.') : __('Files were automatically removed (24-hour retention).') }}

@endif
    @foreach ($detail->files as $file)
  • {{ $file->original_name }}

    {{ $file->humanSize() }} · {{ trans_choice('{1}:count page|[2,*]:count pages', $file->pages, ['count' => $file->pages]) }}

    {{ __('Open') }}
  • @endforeach
{{-- Notes + voice --}} @if ($detail->notes || $detail->audio_path)

{{ __('Customer instructions') }}

@if ($detail->notes)

{{ $detail->notes }}

@endif @if ($detail->audio_path) @endif
@endif {{-- Everything below is the counter lane: paper options, the price adjustment and taking the money. A quote-lane enquiry has no price yet and is settled through the quote, so none of it applies. --}} @unless ($detail->isQuoteLane()) {{-- ================= THE SALE ================= Every charge on this transaction: the printing, the lamination on top of it, the half-hour somebody spent typing it. Editable until the money is taken. --}}

{{ __('The sale') }}

@if ($detail->isPaid()) {{ __('Paid') }} @endif
{{-- Lines --}}
@forelse ($detail->lines as $line)

{{ $line->name }}

{{ $line->workingLabel($detail->currency_code) }}

@unless ($detail->isPaid()) @endunless {{ $money($line->amount, $detail->currency_code) }} @unless ($detail->isPaid()) @endunless
@empty

{{ __('Nothing on this sale yet.') }}

@endforelse
{{-- Add from the catalogue --}} @unless ($detail->isPaid())
{{ __('Add') }}

{{ __('A per-page item takes its quantity from the pages and copies below.') }}

{{-- Something the price list doesn't cover --}}
{{ __('Charge for something else') }}
{{ __('Add') }}
@endunless {{-- The document itself: what the per-page lines are counted from --}} @if ($detail->lines->contains(fn ($l) => $l->isPerPage()) || $detail->files->isNotEmpty())
@unless ($detail->isPaid()) {{ __('Re-price') }} @endunless
@endif {{-- What this sale takes out of the store --}} @php $draw = $detail->stockDraw(); @endphp @if ($draw)

{{ $detail->isPaid() ? __('Taken from stock:') : __('Comes off stock on payment:') }} {{ $detail->lines->whereNotNull('material_variant_id')->where('stock_units', '>', 0) ->map(fn ($l) => rtrim(rtrim(number_format((float) $l->stock_units, 2), '0'), '.').' × '.($l->variant?->label ?: $l->name)) ->unique()->implode(', ') }}

@endif {{-- Totals --}}
{{ __('Subtotal') }}{{ $money($detail->subtotal, $detail->currency_code) }}
@if ($detail->ecocash_fee > 0)
{{ __('EcoCash fee') }}{{ $money($detail->ecocash_fee, $detail->currency_code) }}
@endif
{{ __('Customer pays') }}{{ $money($detail->total, $detail->currency_code) }}
{{-- EcoCash POP --}} @if ($detail->isEcocash() && $detail->pop_path) {{ __('View EcoCash proof') }} @elseif ($detail->isEcocash())

{{ __('Customer did not attach EcoCash proof, verify before confirming.') }}

@endif @endunless {{-- Fulfilment status buttons (labels follow the lane) --}}

{{ __('Fulfilment') }}

@foreach ($detail->laneStatuses() as $key) @php $label = $key === 'cancelled' ? __('Cancel') : $statusFor($key)['label']; @endphp @endforeach
{{-- Confirm payment (counter lane only; quote work is paid through its invoice, never here) --}} @unless ($detail->isPaid() || $detail->isQuoteLane())

{{ __('Confirm payment') }}

{{ __('Records :amount as income into the account you pick.', ['amount' => $money($detail->subtotal, $detail->currency_code)]) }}

{{-- Change wallet (cash only) --}} @if ($pay && $pay['isCash'] && $pay['available'] > 0) @endif @if ($pay && $pay['isCash'])
{{ $money($pay['cashDue'], $detail->currency_code) }}
@if ($pay['change'] > 0)

{{ __('Change of :amt will be saved to :phone until they come back for it.', ['amt' => $money($pay['change'], $detail->currency_code), 'phone' => $detail->contact_phone ?: __('this customer')]) }} @unless ($detail->contact_phone) {{ __('(no phone, change cannot be saved)') }} @endunless

@endif @endif
@endunless
@endif
@if ($detail) {{-- Directors delete on their own say-so. Everyone else asks, and the ask sits on the request until a director answers it. --}}
@if ($detail->hasPendingDeletion()) @if ($canDeleteOutright) @elseif ($detail->deletion_requested_by === auth()->id()) @endif @elseif ($canDeleteOutright) @else @endif
@endif {{ __('Close') }}
{{-- ========================= ASK TO DELETE ========================= --}} {{ __('Ask to delete this request') }}

{{ __('A director will see this and either delete the request or turn it down. It stays in the queue until then.') }}

{{ __('Cancel') }} {{ __('Send for approval') }}
{{-- ========================= DECLINE DELETION ========================= --}} {{ __('Decline the delete request') }}

{{ __('The request stays in the queue. A note tells the person who asked why, so they do not ask again for the same reason.') }}

{{ __('Cancel') }} {{ __('Decline') }}
{{-- ========================= SETTINGS MODAL ========================= --}} {{ __('Intake & pricing') }}
{{-- Enable + QR --}}

{{ __('When on, customers can scan the code or open the link to submit prints.') }}

@if ($intakeUrl)

{{ __('Intake link') }}

@endif
@if ($intakeQr) @endif
{{-- EcoCash --}}

{{ __('EcoCash collection') }}

{{-- Default fund accounts --}}

{{ __('Where the money lands') }}

{{ __('Pre-selected when you confirm payment (still changeable per request).') }}

{{-- Papers and sizes: the shop-wide lists every price row picks from --}}

{{ __('Papers & sizes') }}

{{ __('What the price list can choose from. Keep the wording you use at the counter.') }}

@foreach ([ ['label' => __('Paper types'), 'list' => $s_paper_types, 'model' => 'newPaperType', 'add' => 'addPaperType', 'remove' => 'removePaperType', 'hint' => __('e.g. Plain paper')], ['label' => __('Sizes'), 'list' => $s_sizes, 'model' => 'newSize', 'add' => 'addSize', 'remove' => 'removeSize', 'hint' => __('e.g. A4')], ] as $opt)
@forelse ($opt['list'] as $value) {{ $value }} @empty {{ __('Nothing yet.') }} @endforelse
{{ __('Add') }}
@endforeach

{{ __('Removing one here leaves prices already using it alone.') }}

{{-- Counter catalogue --}}

{{ __('Counter price list') }}

{{ __('Title, paper, size, colour and what it costs. Add a bulk price for "10 for $1".') }}

@foreach ($catalogue as $i => $row) @php $baseCode = $setting->base_currency_code ?: 'USD'; @endphp
{{-- Title · paper type · size · colour · price. Everything but the title and the price is a dropdown, so two people naming the same paper cannot end up with two papers. --}}
{{ ($row['price_currency_code'] ?? '') ?: $baseCode }}
{{-- How it is counted, where it can be ordered, what it takes off the shelf, and the currency it is quoted in. All dropdowns, all in view. --}}
{{-- Bulk price, in the counter's own words: this many for this much, once they reach the MOQ. --}} @foreach ($row['tiers'] ?? [] as $j => $tier)
{{ __('copies for') }} {{ __('from') }}
@error("catalogue.{$i}.tiers.{$j}.bundle_amount")

{{ $message }}

@enderror @error("catalogue.{$i}.tiers.{$j}.min_qty")

{{ $message }}

@enderror @endforeach
@endforeach
@error('catalogue.*.price')

{{ $message }}

@enderror @error('catalogue.*.name')

{{ $message }}

@enderror
{{-- Change wallet --}}

{{ __('Change wallet') }}

{{ __('Overpaid change is kept against the customer\'s phone number and applied on their next visit.') }}

{{ __('It never expires. Whatever is still held when a month is closed is carried into the next month, and stays the customer\'s until they come back for it.') }}

{{ __('Cancel') }} {{ __('Save') }}