Skip to content

Commit fe8a68c

Browse files
fix: validate table requirement for dine in order
1 parent 4d73382 commit fe8a68c

3 files changed

Lines changed: 25 additions & 20 deletions

File tree

app/Http/Requests/Pos/StorePosOrderRequest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Http\Requests\Pos;
44

55
use Illuminate\Foundation\Http\FormRequest;
6+
use Illuminate\Validation\Rule;
67

78
class StorePosOrderRequest extends FormRequest
89
{
@@ -17,7 +18,7 @@ public function rules(): array
1718
'outlet_id' => ['required', 'exists:outlets,id'],
1819
'order_type' => ['required', 'in:dine_in,takeaway,delivery'],
1920
'customer_id' => ['nullable', 'exists:customers,id'],
20-
'dining_table_ids' => ['nullable', 'array'],
21+
'dining_table_ids' => [Rule::requiredIf($this->input('order_type') === 'dine_in'), 'nullable', 'array', 'min:1'],
2122
'dining_table_ids.*' => ['exists:dining_tables,id'],
2223
'note' => ['nullable', 'string', 'max:1000'],
2324
'items' => ['required', 'array', 'min:1'],

resources/js/components/order-split-modal.tsx

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -93,29 +93,29 @@ export function OrderSplitModal({ orderId, orderNumber, items, tables = [], onCl
9393
</button>
9494
</div>
9595

96+
{/* Table selector — outside scroll area so dropdown isn't clipped */}
97+
{tables.length > 0 && (
98+
<div className="border-b border-border dark:border-stone-800 px-4 py-3 space-y-1.5">
99+
<label className="text-xs font-semibold text-foreground">Table for new order</label>
100+
<SearchableSelect
101+
value={diningTableId ?? ''}
102+
placeholder="Same as original order"
103+
onChange={(e) => setDiningTableId(e.target.value ? Number(e.target.value) : null)}
104+
>
105+
<option value="">Same as original order</option>
106+
{tables.map((t) => (
107+
<option key={t.id} value={t.id}>
108+
{t.dining_area ? `${t.dining_area.name} - ` : ''}{t.name}
109+
</option>
110+
))}
111+
</SearchableSelect>
112+
</div>
113+
)}
114+
96115
{/* Content */}
97116
<div className="flex-1 overflow-y-auto p-4 space-y-3">
98117
<p className="text-xs text-muted-foreground">Select items to move into a new order. The original order keeps the remaining items.</p>
99118

100-
{/* Table selector for new order */}
101-
{tables.length > 0 && (
102-
<div className="rounded-lg border border-border dark:border-stone-700 p-3 space-y-1.5">
103-
<label className="text-xs font-semibold text-foreground">Table for new order</label>
104-
<SearchableSelect
105-
value={diningTableId ?? ''}
106-
placeholder="Same as original order"
107-
onChange={(e) => setDiningTableId(e.target.value ? Number(e.target.value) : null)}
108-
>
109-
<option value="">Same as original order</option>
110-
{tables.map((t) => (
111-
<option key={t.id} value={t.id}>
112-
{t.dining_area ? `${t.dining_area.name} - ` : ''}{t.name}
113-
</option>
114-
))}
115-
</SearchableSelect>
116-
</div>
117-
)}
118-
119119
{/* Items */}
120120
{activeItems.map((item) => {
121121
const max = parseFloat(String(item.quantity));

resources/js/pages/pos/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,10 @@ export default function PosIndex({ outlets, categories, foods, customers: initia
679679

680680
function placeOrder() {
681681
if (cart.length === 0 || !outletId) return;
682+
if (orderType === 'dine_in' && tableIds.length === 0) {
683+
showError('Please select at least one table for dine-in orders.');
684+
return;
685+
}
682686
setProcessing(true);
683687
const csrf = (document.querySelector('meta[name="csrf-token"]') as HTMLMetaElement | null)?.content ?? '';
684688
fetch(posPlaceOrder.url(), {

0 commit comments

Comments
 (0)