Files
ladill-pos/resources/views/pos/menu/index.blade.php
T
isaaccladandClaude Opus 4.8 50b170b0af
Deploy Ladill POS / deploy (push) Successful in 35s
Restaurant mode Phase 2: menu depth — categories, stations, modifiers, courses
Builds on Phase 1 with the menu structure restaurants need:
- Menu setup page (restaurant mode): categories, kitchen stations, and modifier
  groups with options (price deltas).
- Products gain category, kitchen station, default course, and attached modifier
  groups (managed on the product form).
- Ordering: the ticket groups the catalog by category and, when a product has
  modifier groups, opens a picker (respects min/max select) for options + notes +
  course before adding. Effective price = base + modifier deltas (resolved
  server-side; client prices are never trusted).
- Fire-by-course: send the whole tab or just one course to the kitchen.
- KDS: filter by station; each item shows its station, course, modifiers and notes.

Schema additive (pos_categories, pos_stations, pos_modifier_groups, pos_modifiers,
product↔group pivot, pos_sale_line_modifiers; category/station/course columns on
products and lines). PosRestaurantTest covers modifiers, course and station routing;
suite green (11 passed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:58:57 +00:00

129 lines
8.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<x-app-layout title="Menu setup">
<div class="mx-auto max-w-3xl space-y-6">
<div>
<h1 class="text-lg font-semibold text-slate-900">Menu setup</h1>
<p class="mt-1 text-sm text-slate-500">Categories, kitchen stations, and modifier options for your products.</p>
</div>
<div class="grid gap-6 sm:grid-cols-2">
{{-- Categories --}}
<section class="rounded-2xl border border-slate-200 bg-white p-5">
<h2 class="text-sm font-semibold text-slate-900">Categories</h2>
<p class="mt-1 text-xs text-slate-400">Group products on the register (e.g. Coffee, Food).</p>
<form method="POST" action="{{ route('pos.menu.categories.store') }}" class="mt-3 flex gap-2">
@csrf
<input type="text" name="name" placeholder="New category" required
class="flex-1 rounded-xl border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
<button class="btn-primary">Add</button>
</form>
<ul class="mt-3 divide-y divide-slate-100">
@forelse ($categories as $category)
<li class="flex items-center justify-between py-2 text-sm">
<span class="text-slate-800">{{ $category->name }}</span>
<form method="POST" action="{{ route('pos.menu.categories.destroy', $category) }}" onsubmit="return confirm('Remove category?');">
@csrf @method('DELETE')
<button class="text-xs font-medium text-red-500 hover:text-red-700">Remove</button>
</form>
</li>
@empty
<li class="py-2 text-xs text-slate-400">No categories yet.</li>
@endforelse
</ul>
</section>
{{-- Stations --}}
<section class="rounded-2xl border border-slate-200 bg-white p-5">
<h2 class="text-sm font-semibold text-slate-900">Kitchen stations</h2>
<p class="mt-1 text-xs text-slate-400">Route items to a prep station (e.g. Kitchen, Bar).</p>
<form method="POST" action="{{ route('pos.menu.stations.store') }}" class="mt-3 flex gap-2">
@csrf
<input type="text" name="name" placeholder="New station" required
class="flex-1 rounded-xl border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
<button class="btn-primary">Add</button>
</form>
<ul class="mt-3 divide-y divide-slate-100">
@forelse ($stations as $station)
<li class="flex items-center justify-between py-2 text-sm">
<span class="text-slate-800">{{ $station->name }}</span>
<form method="POST" action="{{ route('pos.menu.stations.destroy', $station) }}" onsubmit="return confirm('Remove station?');">
@csrf @method('DELETE')
<button class="text-xs font-medium text-red-500 hover:text-red-700">Remove</button>
</form>
</li>
@empty
<li class="py-2 text-xs text-slate-400">No stations yet.</li>
@endforelse
</ul>
</section>
</div>
{{-- Modifier groups --}}
<section class="rounded-2xl border border-slate-200 bg-white p-5">
<div class="flex items-start justify-between gap-3">
<div>
<h2 class="text-sm font-semibold text-slate-900">Modifier groups</h2>
<p class="mt-1 text-xs text-slate-400">Options like Size or Add-ons. Attach groups to products on the product form.</p>
</div>
</div>
<form method="POST" action="{{ route('pos.menu.groups.store') }}" class="mt-3 grid gap-2 sm:grid-cols-[1fr_6rem_6rem_auto]">
@csrf
<input type="text" name="name" placeholder="Group name (e.g. Size)" required
class="rounded-xl border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
<input type="number" name="min_select" value="0" min="0" max="20" title="Minimum to choose"
class="rounded-xl border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500" placeholder="Min">
<input type="number" name="max_select" min="0" max="20" title="Maximum to choose (blank = any)"
class="rounded-xl border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500" placeholder="Max">
<button class="btn-primary">Add</button>
</form>
<div class="mt-4 space-y-4">
@forelse ($groups as $group)
<div class="rounded-xl border border-slate-200 p-4">
<div class="flex items-center justify-between">
<p class="text-sm font-semibold text-slate-900">
{{ $group->name }}
<span class="ml-1 text-xs font-normal text-slate-400">
choose {{ $group->min_select }}{{ $group->max_select ?? 'any' }}
</span>
</p>
<form method="POST" action="{{ route('pos.menu.groups.destroy', $group) }}" onsubmit="return confirm('Remove group and its options?');">
@csrf @method('DELETE')
<button class="text-xs font-medium text-red-500 hover:text-red-700">Remove group</button>
</form>
</div>
<ul class="mt-2 divide-y divide-slate-100">
@foreach ($group->modifiers as $modifier)
<li class="flex items-center justify-between py-1.5 text-sm">
<span class="text-slate-700">
{{ $modifier->name }}
@if ($modifier->price_delta_minor !== 0)
<span class="text-xs text-slate-400">({{ $modifier->price_delta_minor > 0 ? '+' : '' }}{{ number_format($modifier->price_delta_minor / 100, 2) }})</span>
@endif
</span>
<form method="POST" action="{{ route('pos.menu.modifiers.destroy', $modifier) }}">
@csrf @method('DELETE')
<button class="text-xs text-red-500 hover:text-red-700">×</button>
</form>
</li>
@endforeach
</ul>
<form method="POST" action="{{ route('pos.menu.modifiers.store', $group) }}" class="mt-2 flex gap-2">
@csrf
<input type="text" name="name" placeholder="Option (e.g. Large)" required
class="flex-1 rounded-lg border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
<input type="number" name="price_delta" step="0.01" placeholder="±price"
class="w-24 rounded-lg border-slate-300 text-sm focus:border-indigo-500 focus:ring-indigo-500">
<button class="rounded-lg border border-slate-200 px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-slate-50">Add option</button>
</form>
</div>
@empty
<p class="text-xs text-slate-400">No modifier groups yet.</p>
@endforelse
</div>
</section>
</div>
</x-app-layout>