Files
ladill-pos/resources/views/pos/floor.blade.php
T
isaaccladandClaude Opus 4.8 d9e4b6e06e
Deploy Ladill POS / deploy (push) Successful in 30s
Add restaurant/café mode: floor, open tabs, and kitchen display (Phase 1)
Gated by a per-location service_style (retail | restaurant). Restaurant mode adds:
- Floor screen with tables (areas, seats, status) — tap a free table to open a
  dine-in tab; takeaway/counter orders open from the same screen.
- Open tickets (tabs): a sale stays pending while items are added; lines persist
  as you go (posTicket Alpine component posts each change to the server).
- Send-to-kitchen fires un-sent lines; a polling Kitchen Display (KDS) shows
  active tickets and bumps items queued → preparing → ready → served.
- Settlement reuses the existing cash / Ladill Pay flow; paying closes the tab
  and frees the table (PosSaleService::closeTicket, wired into both pay paths).
- Settings gains the mode toggle and a tables manager.

Schema is additive (new pos_tables; service_style on locations; order/kitchen
columns on sales + lines). Retail flow is untouched. Sidebar surfaces Floor +
Kitchen only in restaurant mode. New PosRestaurantTest covers the dine-in
lifecycle end to end; suite green (10 passed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 08:38:18 +00:00

88 lines
5.5 KiB
PHP

<x-app-layout title="Floor">
@php $cur = $location->currency; @endphp
<div class="space-y-6">
<div class="flex flex-wrap items-center justify-between gap-3">
<div>
<h1 class="text-lg font-semibold text-slate-900">Floor</h1>
<p class="mt-1 text-sm text-slate-500">Open a tab on a table, or start a takeaway order.</p>
</div>
<div class="flex items-center gap-2">
<form method="post" action="{{ route('pos.tickets.open') }}">
@csrf
<input type="hidden" name="order_type" value="takeaway">
<button class="rounded-xl border border-slate-200 bg-white px-3 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">+ Takeaway</button>
</form>
<form method="post" action="{{ route('pos.tickets.open') }}">
@csrf
<input type="hidden" name="order_type" value="counter">
<button class="btn-primary">+ Counter order</button>
</form>
</div>
</div>
@if(session('error'))
<div class="rounded-xl border border-red-100 bg-red-50 px-4 py-3 text-sm text-red-700">{{ session('error') }}</div>
@endif
{{-- Open non-dine-in tickets --}}
@php $loose = $openTickets->whereNull('table_id'); @endphp
@if($loose->isNotEmpty())
<div>
<h2 class="mb-2 text-xs font-semibold uppercase tracking-wide text-slate-400">Open orders</h2>
<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
@foreach($loose as $t)
<a href="{{ route('pos.tickets.show', $t) }}" class="rounded-2xl border border-slate-200 bg-white p-4 transition hover:border-indigo-200 hover:shadow-sm">
<div class="flex items-center justify-between">
<span class="rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-medium capitalize text-slate-600">{{ str_replace('_', ' ', $t->order_type) }}</span>
@if($t->kitchen_status === \App\Models\PosSale::KITCHEN_ACTIVE)
<span class="rounded-full bg-amber-50 px-2 py-0.5 text-[11px] font-medium text-amber-700">In kitchen</span>
@endif
</div>
<p class="mt-2 text-sm font-semibold text-slate-900">{{ $cur }} {{ number_format($t->total_minor / 100, 2) }}</p>
<p class="mt-0.5 text-xs text-slate-400">{{ $t->lines_count }} item(s) · {{ $t->opened_at?->diffForHumans() }}</p>
</a>
@endforeach
</div>
</div>
@endif
{{-- Tables --}}
@if($tables->isEmpty())
<div class="rounded-2xl border border-dashed border-slate-200 bg-white px-6 py-12 text-center">
<p class="text-sm text-slate-500">No tables yet.</p>
<a href="{{ route('pos.settings') }}" class="mt-3 inline-block text-sm font-semibold text-indigo-600 hover:text-indigo-800">Add tables in Settings</a>
</div>
@else
@foreach($tablesByArea as $area => $areaTables)
<div>
<h2 class="mb-2 text-xs font-semibold uppercase tracking-wide text-slate-400">{{ $area }}</h2>
<div class="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6">
@foreach($areaTables as $table)
@php $occupied = ! $table->isFree() && $table->currentSale; @endphp
@if($occupied)
<a href="{{ route('pos.tickets.show', $table->currentSale) }}"
class="flex flex-col rounded-2xl border-2 border-amber-300 bg-amber-50 p-4 text-left transition hover:border-amber-400">
<span class="text-sm font-semibold text-slate-900">{{ $table->label }}</span>
<span class="mt-1 text-xs text-amber-700">Occupied</span>
<span class="mt-2 text-sm font-semibold text-slate-900">{{ $cur }} {{ number_format($table->currentSale->total_minor / 100, 2) }}</span>
</a>
@else
<form method="post" action="{{ route('pos.tickets.open') }}" class="contents">
@csrf
<input type="hidden" name="order_type" value="dine_in">
<input type="hidden" name="table_id" value="{{ $table->id }}">
<button class="flex w-full flex-col rounded-2xl border-2 border-slate-200 bg-white p-4 text-left transition hover:border-indigo-300 hover:bg-indigo-50/40">
<span class="text-sm font-semibold text-slate-900">{{ $table->label }}</span>
<span class="mt-1 text-xs text-slate-400">{{ $table->seats }} seats · Free</span>
<span class="mt-2 text-xs font-semibold text-indigo-600">Open tab </span>
</button>
</form>
@endif
@endforeach
</div>
</div>
@endforeach
@endif
</div>
</x-app-layout>