Redesign POS order flow with order type picker and counter/takeaway support
- Add OrderTypePicker screen: Table / Counter / Takeaway cards shown when no active session, replacing the old always-visible table board - Move PosTableBoard into a modal overlay (opens on Table selection or "Assign Table" for counter orders) - Add orderType field + setOrderType action to cart store - Counter and Takeaway orders no longer require a table to submit - Add "Assign Table →" button in cart for counter orders with active session - Rewrite category tabs as horizontal scrollable row (no wrapping) - Larger product cards with 4:3 thumbnail + quantity badge overlay - Bigger quantity controls (h-8 w-8) and "New order" back button in header - Add i18n keys for order types in en/fa/ar Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -229,7 +229,18 @@
|
|||||||
"customerSaveError": "تعذّر حفظ العميل",
|
"customerSaveError": "تعذّر حفظ العميل",
|
||||||
"customerPhoneExists": "الهاتف مسجّل مسبقاً — ابحث واختر",
|
"customerPhoneExists": "الهاتف مسجّل مسبقاً — ابحث واختر",
|
||||||
"newCustomerHint": "للطلب الحالي فقط، أو احفظ في CRM عبر «إضافة عميل»",
|
"newCustomerHint": "للطلب الحالي فقط، أو احفظ في CRM عبر «إضافة عميل»",
|
||||||
"offlineQueueNotice": "غير متصل — تم حفظ الطلب في الطابور وسيتم إرساله عند الاتصال"
|
"offlineQueueNotice": "غير متصل — تم حفظ الطلب في الطابور وسيتم إرساله عند الاتصال",
|
||||||
|
"orderTypePicker": "كيف تريد تسجيل هذا الطلب؟",
|
||||||
|
"orderTypeTable": "طاولة",
|
||||||
|
"orderTypeTableDesc": "إجلاس الضيف على طاولة",
|
||||||
|
"orderTypeCounter": "كاونتر",
|
||||||
|
"orderTypeCounterDesc": "دون تخصيص طاولة",
|
||||||
|
"orderTypeTakeaway": "تيك أواي",
|
||||||
|
"orderTypeTakeawayDesc": "طلب للخارج",
|
||||||
|
"counterBadge": "كاونتر",
|
||||||
|
"takeawayBadge": "تيك أواي",
|
||||||
|
"assignTable": "تعيين طاولة",
|
||||||
|
"newOrder": "طلب جديد"
|
||||||
},
|
},
|
||||||
"print": {
|
"print": {
|
||||||
"printReceipt": "طباعة الإيصال",
|
"printReceipt": "طباعة الإيصال",
|
||||||
|
|||||||
@@ -231,7 +231,18 @@
|
|||||||
"customerSaveError": "Could not save customer",
|
"customerSaveError": "Could not save customer",
|
||||||
"customerPhoneExists": "Phone already registered — search and select",
|
"customerPhoneExists": "Phone already registered — search and select",
|
||||||
"newCustomerHint": "Use for this order only, or tap Add customer to save to CRM",
|
"newCustomerHint": "Use for this order only, or tap Add customer to save to CRM",
|
||||||
"offlineQueueNotice": "Offline — order saved in queue and will sync when connected"
|
"offlineQueueNotice": "Offline — order saved in queue and will sync when connected",
|
||||||
|
"orderTypePicker": "How would you like to take this order?",
|
||||||
|
"orderTypeTable": "Table",
|
||||||
|
"orderTypeTableDesc": "Seat guest at a specific table",
|
||||||
|
"orderTypeCounter": "Counter",
|
||||||
|
"orderTypeCounterDesc": "Walk-in, no table yet",
|
||||||
|
"orderTypeTakeaway": "Takeaway",
|
||||||
|
"orderTypeTakeawayDesc": "Order to go",
|
||||||
|
"counterBadge": "Counter",
|
||||||
|
"takeawayBadge": "Takeaway",
|
||||||
|
"assignTable": "Assign table",
|
||||||
|
"newOrder": "New order"
|
||||||
},
|
},
|
||||||
"print": {
|
"print": {
|
||||||
"printReceipt": "Print receipt",
|
"printReceipt": "Print receipt",
|
||||||
|
|||||||
@@ -231,7 +231,18 @@
|
|||||||
"customerSaveError": "خطا در ذخیره مشتری",
|
"customerSaveError": "خطا در ذخیره مشتری",
|
||||||
"customerPhoneExists": "این موبایل قبلاً ثبت شده — از جستجو انتخاب کنید",
|
"customerPhoneExists": "این موبایل قبلاً ثبت شده — از جستجو انتخاب کنید",
|
||||||
"newCustomerHint": "میتوانید فقط برای این سفارش نام بزنید یا با «افزودن مشتری» در CRM ذخیره کنید",
|
"newCustomerHint": "میتوانید فقط برای این سفارش نام بزنید یا با «افزودن مشتری» در CRM ذخیره کنید",
|
||||||
"offlineQueueNotice": "آفلاین ‐ سفارش در صف ذخیره شد و پس از اتصال ارسال میشود"
|
"offlineQueueNotice": "آفلاین ‐ سفارش در صف ذخیره شد و پس از اتصال ارسال میشود",
|
||||||
|
"orderTypePicker": "سفارش چطور ثبت میشود؟",
|
||||||
|
"orderTypeTable": "میز",
|
||||||
|
"orderTypeTableDesc": "مهمان روی میز مینشیند",
|
||||||
|
"orderTypeCounter": "پیشخوان",
|
||||||
|
"orderTypeCounterDesc": "بدون تخصیص میز",
|
||||||
|
"orderTypeTakeaway": "بیرونبر",
|
||||||
|
"orderTypeTakeawayDesc": "سفارش برای بیرون",
|
||||||
|
"counterBadge": "پیشخوان",
|
||||||
|
"takeawayBadge": "بیرونبر",
|
||||||
|
"assignTable": "تخصیص میز",
|
||||||
|
"newOrder": "سفارش جدید"
|
||||||
},
|
},
|
||||||
"print": {
|
"print": {
|
||||||
"printReceipt": "چاپ رسید",
|
"printReceipt": "چاپ رسید",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,8 @@ export interface AppliedCoupon {
|
|||||||
discountAmount: number;
|
discountAmount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OrderType = "table" | "counter" | "takeaway";
|
||||||
|
|
||||||
interface CartState {
|
interface CartState {
|
||||||
items: CartItem[];
|
items: CartItem[];
|
||||||
syncedQtyByMenuId: Record<string, number>;
|
syncedQtyByMenuId: Record<string, number>;
|
||||||
@@ -27,6 +29,7 @@ interface CartState {
|
|||||||
customerId: string | null;
|
customerId: string | null;
|
||||||
guestName: string;
|
guestName: string;
|
||||||
guestPhone: string;
|
guestPhone: string;
|
||||||
|
orderType: OrderType | null;
|
||||||
getPendingLines: () => { menuItemId: string; quantity: number; notes?: string }[];
|
getPendingLines: () => { menuItemId: string; quantity: number; notes?: string }[];
|
||||||
addItem: (item: MenuItem) => void;
|
addItem: (item: MenuItem) => void;
|
||||||
removeItem: (menuItemId: string) => void;
|
removeItem: (menuItemId: string) => void;
|
||||||
@@ -35,6 +38,7 @@ interface CartState {
|
|||||||
setAppliedCoupon: (coupon: AppliedCoupon | null) => void;
|
setAppliedCoupon: (coupon: AppliedCoupon | null) => void;
|
||||||
clearCoupon: () => void;
|
clearCoupon: () => void;
|
||||||
setTableId: (tableId: string | null) => void;
|
setTableId: (tableId: string | null) => void;
|
||||||
|
setOrderType: (type: OrderType | null) => void;
|
||||||
setActiveOrderId: (orderId: string | null) => void;
|
setActiveOrderId: (orderId: string | null) => void;
|
||||||
setGuestName: (name: string) => void;
|
setGuestName: (name: string) => void;
|
||||||
setGuestPhone: (phone: string) => void;
|
setGuestPhone: (phone: string) => void;
|
||||||
@@ -77,6 +81,7 @@ export const useCartStore = create<CartState>((set, get) => ({
|
|||||||
customerId: null,
|
customerId: null,
|
||||||
guestName: "",
|
guestName: "",
|
||||||
guestPhone: "",
|
guestPhone: "",
|
||||||
|
orderType: null,
|
||||||
|
|
||||||
getPendingLines: () => {
|
getPendingLines: () => {
|
||||||
const { items, syncedQtyByMenuId } = get();
|
const { items, syncedQtyByMenuId } = get();
|
||||||
@@ -134,6 +139,7 @@ export const useCartStore = create<CartState>((set, get) => ({
|
|||||||
setAppliedCoupon: (coupon) => set({ appliedCoupon: coupon }),
|
setAppliedCoupon: (coupon) => set({ appliedCoupon: coupon }),
|
||||||
clearCoupon: () => set(clearCouponState),
|
clearCoupon: () => set(clearCouponState),
|
||||||
setTableId: (tableId) => set({ tableId }),
|
setTableId: (tableId) => set({ tableId }),
|
||||||
|
setOrderType: (orderType) => set({ orderType }),
|
||||||
setActiveOrderId: (activeOrderId) => set({ activeOrderId, activeOrderDisplayNumber: null }),
|
setActiveOrderId: (activeOrderId) => set({ activeOrderId, activeOrderDisplayNumber: null }),
|
||||||
setGuestName: (guestName) =>
|
setGuestName: (guestName) =>
|
||||||
set((s) => ({
|
set((s) => ({
|
||||||
@@ -197,6 +203,7 @@ export const useCartStore = create<CartState>((set, get) => ({
|
|||||||
customerId: null,
|
customerId: null,
|
||||||
guestName: "",
|
guestName: "",
|
||||||
guestPhone: "",
|
guestPhone: "",
|
||||||
|
orderType: null,
|
||||||
...clearCouponState,
|
...clearCouponState,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user