Update Waiter & Menu

This commit is contained in:
Jevinca Marvella 2025-12-10 11:31:17 +07:00
parent 988ada5393
commit 251a9e37ae
4 changed files with 101 additions and 67 deletions

View File

@ -1,2 +1,2 @@
id,transaksi_id,menu_id,qty,harga_satuan,subtotal_item id,transaksi_id,menu_id,qty,harga_satuan,subtotal_item
1,1,1,1,20000.0,20000.0

1 id transaksi_id menu_id qty harga_satuan subtotal_item
2 1 1 1 1 20000.0 20000.0

163
main.py
View File

@ -1083,7 +1083,7 @@ class App:
right.pack(side='right', fill='both', padx=6, pady=6) right.pack(side='right', fill='both', padx=6, pady=6)
# === PANEL KIRI: Daftar Menu dengan Card === # === PANEL KIRI: Daftar Menu dengan Card ===
ttk.Label(left, text="Daftar Menu", font=("Arial", 13, "bold")).pack(pady=6) ttk.Label(left, text="Daftar Menu", font=("Arial", 12, "bold")).pack(pady=4)
# Filter search # Filter search
search_frame = ttk.Frame(left) search_frame = ttk.Frame(left)
@ -1094,8 +1094,8 @@ class App:
ttk.Button(search_frame, text="Cari", command=self.reload_order_menu_cards).pack(side='left', padx=3) ttk.Button(search_frame, text="Cari", command=self.reload_order_menu_cards).pack(side='left', padx=3)
ttk.Button(search_frame, text="Reset", command=self.reset_order_search).pack(side='left', padx=3) ttk.Button(search_frame, text="Reset", command=self.reset_order_search).pack(side='left', padx=3)
# Scrollable frame untuk cards # Scrollable frame untuk cards (DENGAN BACKGROUND PUTIH)
canvas = tk.Canvas(left, bg='white') canvas = tk.Canvas(left, bg='#f5f5f5', height=450, highlightthickness=0)
scrollbar = ttk.Scrollbar(left, orient="vertical", command=canvas.yview) scrollbar = ttk.Scrollbar(left, orient="vertical", command=canvas.yview)
self.menu_cards_frame = ttk.Frame(canvas) self.menu_cards_frame = ttk.Frame(canvas)
@ -1116,18 +1116,18 @@ class App:
canvas.bind_all("<MouseWheel>", _on_mousewheel) canvas.bind_all("<MouseWheel>", _on_mousewheel)
# === PANEL KANAN: Keranjang === # === PANEL KANAN: Keranjang ===
ttk.Label(right, text="Keranjang Belanja", font=("Arial", 13, "bold")).pack(pady=6) ttk.Label(right, text="Keranjang Belanja", font=("Arial", 12, "bold")).pack(pady=4)
# Treeview cart # Treeview cart
cart_cols = ("Menu", "Qty", "Harga", "Subtotal") cart_cols = ("Menu", "Qty", "Harga", "Subtotal")
self.cart_tree = ttk.Treeview(right, columns=cart_cols, show='headings', height=12) self.cart_tree = ttk.Treeview(right, columns=cart_cols, show='headings', height=8)
for c in cart_cols: for c in cart_cols:
self.cart_tree.heading(c, text=c) self.cart_tree.heading(c, text=c)
if c == "Menu": if c == "Menu":
self.cart_tree.column(c, width=150) self.cart_tree.column(c, width=150)
else: else:
self.cart_tree.column(c, width=70) self.cart_tree.column(c, width=70)
self.cart_tree.pack(fill='both', expand=True) self.cart_tree.pack(fill='x', pady=4)
# Tombol hapus item atau kosongkan cart # Tombol hapus item atau kosongkan cart
cart_btn_frame = ttk.Frame(right) cart_btn_frame = ttk.Frame(right)
@ -1136,32 +1136,49 @@ class App:
ttk.Button(cart_btn_frame, text="Kosongkan", command=self.clear_cart).pack(side='left', padx=3) ttk.Button(cart_btn_frame, text="Kosongkan", command=self.clear_cart).pack(side='left', padx=3)
# Info total # Info total
self.cart_subtotal_label = ttk.Label(right, text="Subtotal: Rp 0", font=("Arial", 10)) total_frame = ttk.Frame(right)
self.cart_subtotal_label.pack(pady=2) total_frame.pack(fill='x', pady=4)
self.cart_discount_label = ttk.Label(right, text="Diskon Item: Rp 0", font=("Arial", 10))
self.cart_discount_label.pack(pady=2)
self.cart_promo_label = ttk.Label(right, text="Diskon Promo: Rp 0", font=("Arial", 10))
self.cart_promo_label.pack(pady=2)
self.cart_total_label = ttk.Label(right, text="TOTAL: Rp 0", font=("Arial", 12, "bold"))
self.cart_total_label.pack(pady=4)
# Input nomor meja dan promo self.cart_subtotal_label = ttk.Label(total_frame, text="Subtotal: Rp 0", font=("Arial", 9))
self.cart_subtotal_label.pack()
self.cart_discount_label = ttk.Label(total_frame, text="Diskon Item: Rp 0", font=("Arial", 9))
self.cart_discount_label.pack()
self.cart_promo_label = ttk.Label(total_frame, text="Diskon Promo: Rp 0", font=("Arial", 9))
self.cart_promo_label.pack()
self.cart_total_label = ttk.Label(total_frame, text="TOTAL: Rp 0", font=("Arial", 11, "bold"))
self.cart_total_label.pack(pady=2)
# Input nomor meja dan promo (LAYOUT RAPI)
checkout_frame = ttk.Frame(right) checkout_frame = ttk.Frame(right)
checkout_frame.pack(pady=6) checkout_frame.pack(fill='x', pady=6, padx=10)
ttk.Label(checkout_frame, text="No. Meja:").grid(row=0, column=0, sticky='e', padx=3, pady=3)
# Row 0: No. Meja
ttk.Label(checkout_frame, text="No. Meja:", font=("Arial", 9)).grid(row=0, column=0, sticky='w', padx=3, pady=3)
self.order_meja_var = tk.StringVar() self.order_meja_var = tk.StringVar()
ttk.Entry(checkout_frame, textvariable=self.order_meja_var, width=15).grid(row=0, column=1, pady=3) ttk.Entry(checkout_frame, textvariable=self.order_meja_var, width=20).grid(row=0, column=1, columnspan=2, pady=3, sticky='ew')
ttk.Label(checkout_frame, text="Kode Promo:").grid(row=1, column=0, sticky='e', padx=3, pady=3) # Row 1: Kode Promo
ttk.Label(checkout_frame, text="Kode Promo:", font=("Arial", 9)).grid(row=1, column=0, sticky='w', padx=3, pady=3)
self.order_promo_var = tk.StringVar() self.order_promo_var = tk.StringVar()
ttk.Entry(checkout_frame, textvariable=self.order_promo_var, width=15).grid(row=1, column=1, pady=3) ttk.Entry(checkout_frame, textvariable=self.order_promo_var, width=12).grid(row=1, column=1, pady=3, sticky='ew')
ttk.Button(checkout_frame, text="Terapkan", command=self.update_cart_display).grid(row=1, column=2, padx=3) ttk.Button(checkout_frame, text="Terapkan", command=self.update_cart_display, width=10).grid(row=1, column=2, padx=3, sticky='e')
# Tombol checkout # Configure columns
ttk.Button(right, text="🛒 CHECKOUT", command=self.checkout_order, style="Accent.TButton").pack(pady=8) checkout_frame.columnconfigure(1, weight=1)
# Tombol checkout (PASTI KELIHATAN)
checkout_btn_frame = ttk.Frame(right)
checkout_btn_frame.pack(fill='x', pady=10, padx=20)
ttk.Button(
checkout_btn_frame,
text="🛒 CHECKOUT PESANAN",
command=self.checkout_order,
width=30
).pack()
# Init cart data # Init cart data
self.cart_items = [] # List of dict: {'menu_id': int, 'qty': int} self.cart_items = []
# Load menu cards # Load menu cards
self.reload_order_menu_cards() self.reload_order_menu_cards()
@ -1187,8 +1204,15 @@ class App:
for menu_data in results: for menu_data in results:
mid, nama, kategori, harga, stok, foto, tersedia, item_disc = menu_data mid, nama, kategori, harga, stok, foto, tersedia, item_disc = menu_data
# Create card frame # Create card frame (DENGAN BACKGROUND PUTIH + BORDER)
card = tk.Frame(self.menu_cards_frame, relief='ridge', borderwidth=2, bg='white', padx=10, pady=10) card = tk.Frame(
self.menu_cards_frame,
relief='solid',
borderwidth=1,
bg='white',
padx=10,
pady=10
)
card.grid(row=row, column=col, padx=8, pady=8, sticky='nsew') card.grid(row=row, column=col, padx=8, pady=8, sticky='nsew')
# Gambar # Gambar
@ -1198,14 +1222,13 @@ class App:
img = img.resize((150, 100), Image.Resampling.LANCZOS) img = img.resize((150, 100), Image.Resampling.LANCZOS)
photo = ImageTk.PhotoImage(img) photo = ImageTk.PhotoImage(img)
# Simpan reference agar tidak di-garbage collect
img_label = tk.Label(card, image=photo, bg='white') img_label = tk.Label(card, image=photo, bg='white')
img_label.image = photo img_label.image = photo
img_label.pack() img_label.pack()
except: except:
tk.Label(card, text="[No Image]", bg='lightgray', width=20, height=6).pack() tk.Label(card, text="[No Image]", bg='#e0e0e0', width=20, height=6).pack()
else: else:
tk.Label(card, text="[No Image]", bg='lightgray', width=20, height=6).pack() tk.Label(card, text="[No Image]", bg='#e0e0e0', width=20, height=6).pack()
# Nama menu # Nama menu
tk.Label(card, text=nama, font=("Arial", 11, "bold"), bg='white', wraplength=150).pack(pady=(5, 2)) tk.Label(card, text=nama, font=("Arial", 11, "bold"), bg='white', wraplength=150).pack(pady=(5, 2))
@ -1217,7 +1240,9 @@ class App:
harga_text = f"Rp {harga:,.0f}" harga_text = f"Rp {harga:,.0f}"
if item_disc > 0: if item_disc > 0:
harga_text += f" (-{item_disc}%)" harga_text += f" (-{item_disc}%)"
tk.Label(card, text=harga_text, font=("Arial", 10, "bold"), fg='green', bg='white').pack(pady=2) tk.Label(card, text=harga_text, font=("Arial", 10, "bold"), fg='#4CAF50', bg='white').pack(pady=2)
else:
tk.Label(card, text=harga_text, font=("Arial", 10, "bold"), fg='green', bg='white').pack(pady=2)
# Stok info # Stok info
tk.Label(card, text=f"Stok: {stok}", font=("Arial", 8), fg='blue', bg='white').pack(pady=2) tk.Label(card, text=f"Stok: {stok}", font=("Arial", 8), fg='blue', bg='white').pack(pady=2)
@ -1237,6 +1262,8 @@ class App:
bg='#FF5722', bg='#FF5722',
fg='white', fg='white',
width=3, width=3,
borderwidth=0,
cursor='hand2',
command=lambda m=mid: self.decrease_from_card(m) command=lambda m=mid: self.decrease_from_card(m)
).pack(side='left', padx=2) ).pack(side='left', padx=2)
@ -1255,6 +1282,8 @@ class App:
bg='#4CAF50', bg='#4CAF50',
fg='white', fg='white',
width=3, width=3,
borderwidth=0,
cursor='hand2',
command=lambda m=mid, s=stok: self.increase_from_card(m, s) command=lambda m=mid, s=stok: self.increase_from_card(m, s)
).pack(side='left', padx=2) ).pack(side='left', padx=2)
else: else:
@ -1266,15 +1295,17 @@ class App:
bg='#4CAF50', bg='#4CAF50',
fg='white', fg='white',
width=12, width=12,
borderwidth=0,
cursor='hand2',
command=lambda m=mid, s=stok: self.increase_from_card(m, s) command=lambda m=mid, s=stok: self.increase_from_card(m, s)
).pack() ).pack()
# Next column # Next column
col += 1 col += 1
if col >= 2: # 2 cards per row if col >= 2:
col = 0 col = 0
row += 1 row += 1
def reset_order_search(self): def reset_order_search(self):
self.order_search_var.set("") self.order_search_var.set("")
self.reload_order_menu_cards() self.reload_order_menu_cards()
@ -1420,7 +1451,7 @@ class App:
self.order_meja_var.set("") self.order_meja_var.set("")
self.order_promo_var.set("") self.order_promo_var.set("")
self.update_cart_display() self.update_cart_display()
self.reload_order_menu() # Refresh stok self.reload_order_menu_cards() # Refresh stok
else: else:
messagebox.showerror("Error", f"Gagal menyimpan pesanan: {result}") messagebox.showerror("Error", f"Gagal menyimpan pesanan: {result}")
@ -1438,26 +1469,26 @@ class App:
# Header # Header
header = ttk.Frame(parent) header = ttk.Frame(parent)
header.pack(fill='x', padx=10, pady=8) header.pack(fill='x', padx=10, pady=6)
ttk.Label(header, text="Dashboard Waiter - Kelola Pesanan", font=("Arial", 14, "bold")).pack(side='left') ttk.Label(header, text="Dashboard Waiter - Kelola Pesanan", font=("Arial", 13, "bold")).pack(side='left')
ttk.Button(header, text="🔄 Refresh", command=self.reload_waiter_orders).pack(side='right', padx=6) ttk.Button(header, text="🔄 Refresh", command=self.reload_waiter_orders).pack(side='right', padx=6)
# Filter status # Filter status
filter_frame = ttk.Frame(parent) filter_frame = ttk.Frame(parent)
filter_frame.pack(fill='x', padx=10, pady=6) filter_frame.pack(fill='x', padx=10, pady=4)
ttk.Label(filter_frame, text="Filter Status:").pack(side='left', padx=6) ttk.Label(filter_frame, text="Filter Status:").pack(side='left', padx=6)
ttk.Button(filter_frame, text="Semua", command=lambda: self.reload_waiter_orders(None)).pack(side='left', padx=3) ttk.Button(filter_frame, text="Semua", command=lambda: self.reload_waiter_orders(None), width=8).pack(side='left', padx=2)
ttk.Button(filter_frame, text="Pending", command=lambda: self.reload_waiter_orders('pending')).pack(side='left', padx=3) ttk.Button(filter_frame, text="Pending", command=lambda: self.reload_waiter_orders('pending'), width=8).pack(side='left', padx=2)
ttk.Button(filter_frame, text="Menunggu", command=lambda: self.reload_waiter_orders('menunggu')).pack(side='left', padx=3) ttk.Button(filter_frame, text="Menunggu", command=lambda: self.reload_waiter_orders('menunggu'), width=10).pack(side='left', padx=2)
ttk.Button(filter_frame, text="Diproses", command=lambda: self.reload_waiter_orders('diproses')).pack(side='left', padx=3) ttk.Button(filter_frame, text="Diproses", command=lambda: self.reload_waiter_orders('diproses'), width=8).pack(side='left', padx=2)
ttk.Button(filter_frame, text="Selesai", command=lambda: self.reload_waiter_orders('selesai')).pack(side='left', padx=3) ttk.Button(filter_frame, text="Selesai", command=lambda: self.reload_waiter_orders('selesai'), width=8).pack(side='left', padx=2)
# Treeview pesanan # Treeview pesanan (LEBIH PENDEK)
tree_frame = ttk.Frame(parent) tree_frame = ttk.Frame(parent)
tree_frame.pack(fill='x', padx=10, pady=6) tree_frame.pack(fill='x', padx=10, pady=4)
cols = ("ID", "User ID", "No.Meja", "Total", "Status", "Promo", "Tanggal") cols = ("ID", "User ID", "No.Meja", "Total", "Status", "Promo", "Tanggal")
self.waiter_tree = ttk.Treeview(tree_frame, columns=cols, show='headings', height=10) self.waiter_tree = ttk.Treeview(tree_frame, columns=cols, show='headings', height=8)
self.waiter_tree.heading("ID", text="ID") self.waiter_tree.heading("ID", text="ID")
self.waiter_tree.heading("User ID", text="User ID") self.waiter_tree.heading("User ID", text="User ID")
@ -1485,53 +1516,53 @@ class App:
# Bind event saat pilih pesanan # Bind event saat pilih pesanan
self.waiter_tree.bind("<<TreeviewSelect>>", self.on_waiter_select) self.waiter_tree.bind("<<TreeviewSelect>>", self.on_waiter_select)
# Detail pesanan dengan SCROLLBAR # Detail pesanan (LEBIH PENDEK - height=6 aja)
detail_frame = ttk.LabelFrame(parent, text="📋 Detail Pesanan Terpilih", padding=10) detail_frame = ttk.LabelFrame(parent, text="📋 Detail Pesanan", padding=8)
detail_frame.pack(fill='both', expand=True, padx=10, pady=6) detail_frame.pack(fill='x', padx=10, pady=4)
# Frame untuk text + scrollbar # Frame untuk text + scrollbar
text_frame = ttk.Frame(detail_frame) text_frame = ttk.Frame(detail_frame)
text_frame.pack(fill='both', expand=True) text_frame.pack(fill='both', expand=True)
self.waiter_detail_text = tk.Text(text_frame, height=8, width=100, font=("Courier New", 9), wrap='word') self.waiter_detail_text = tk.Text(text_frame, height=6, width=100, font=("Courier New", 8), wrap='word')
detail_scroll = ttk.Scrollbar(text_frame, orient='vertical', command=self.waiter_detail_text.yview) detail_scroll = ttk.Scrollbar(text_frame, orient='vertical', command=self.waiter_detail_text.yview)
self.waiter_detail_text.configure(yscrollcommand=detail_scroll.set) self.waiter_detail_text.configure(yscrollcommand=detail_scroll.set)
self.waiter_detail_text.pack(side='left', fill='both', expand=True) self.waiter_detail_text.pack(side='left', fill='both', expand=True)
detail_scroll.pack(side='right', fill='y') detail_scroll.pack(side='right', fill='y')
# Tombol aksi - PASTI KELIHATAN # TOMBOL AKSI - PASTI KELIHATAN (TIDAK PAKAI expand=True)
action_frame = ttk.LabelFrame(parent, text="🎯 Ubah Status Pesanan", padding=15) action_frame = ttk.LabelFrame(parent, text="🎯 Ubah Status Pesanan", padding=12)
action_frame.pack(fill='x', padx=10, pady=10) action_frame.pack(fill='x', padx=10, pady=8)
# Grid 2x2 untuk 4 tombol # Grid 2x2 untuk 4 tombol
ttk.Button( ttk.Button(
action_frame, action_frame,
text="✅ Terima Pesanan (Pending → Menunggu)", text="✅ Terima (Pending → Menunggu)",
command=lambda: self.update_order_status('menunggu'), command=lambda: self.update_order_status('menunggu'),
width=40 width=35
).grid(row=0, column=0, padx=5, pady=5, sticky='ew') ).grid(row=0, column=0, padx=4, pady=4, sticky='ew')
ttk.Button( ttk.Button(
action_frame, action_frame,
text="🍳 Proses Pesanan (Menunggu → Diproses)", text="🍳 Proses (Menunggu → Diproses)",
command=lambda: self.update_order_status('diproses'), command=lambda: self.update_order_status('diproses'),
width=40 width=35
).grid(row=0, column=1, padx=5, pady=5, sticky='ew') ).grid(row=0, column=1, padx=4, pady=4, sticky='ew')
ttk.Button( ttk.Button(
action_frame, action_frame,
text="🍽️ Selesai Disajikan (Diproses → Selesai)", text="🍽️ Selesai (Diproses → Selesai)",
command=lambda: self.update_order_status('selesai'), command=lambda: self.update_order_status('selesai'),
width=40 width=35
).grid(row=1, column=0, padx=5, pady=5, sticky='ew') ).grid(row=1, column=0, padx=4, pady=4, sticky='ew')
ttk.Button( ttk.Button(
action_frame, action_frame,
text="💰 Selesai Dilayani (Selesai → Dibayar)", text="💰 Dibayar (Selesai → Dibayar)",
command=lambda: self.update_order_status('dibayar'), command=lambda: self.update_order_status('dibayar'),
width=40 width=35
).grid(row=1, column=1, padx=5, pady=5, sticky='ew') ).grid(row=1, column=1, padx=4, pady=4, sticky='ew')
# Buat kolom responsive # Buat kolom responsive
action_frame.columnconfigure(0, weight=1) action_frame.columnconfigure(0, weight=1)
@ -1651,7 +1682,7 @@ class App:
return return
# Konfirmasi # Konfirmasi
if not messagebox.askyesno("Konfirmasi Ubah Status", if not messagebox.askyesno("Konfirmasi",
f"Ubah status pesanan #{transaksi_id}\n\n" f"Ubah status pesanan #{transaksi_id}\n\n"
f"Dari: {current_status.upper()}\n" f"Dari: {current_status.upper()}\n"
f"Ke: {new_status.upper()}\n\n" f"Ke: {new_status.upper()}\n\n"
@ -1664,11 +1695,13 @@ class App:
if success: if success:
messagebox.showinfo("✅ Berhasil", f"Status berhasil diubah menjadi '{new_status.upper()}'") messagebox.showinfo("✅ Berhasil", f"Status berhasil diubah menjadi '{new_status.upper()}'")
self.reload_waiter_orders() self.reload_waiter_orders()
# Auto select row yang sama setelah reload # Auto select row yang sama
for item_id in self.waiter_tree.get_children(): for item_id in self.waiter_tree.get_children():
if self.waiter_tree.item(item_id)['values'][0] == transaksi_id: if self.waiter_tree.item(item_id)['values'][0] == transaksi_id:
self.waiter_tree.selection_set(item_id) self.waiter_tree.selection_set(item_id)
self.waiter_tree.see(item_id) self.waiter_tree.see(item_id)
# Trigger event untuk reload detail
self.on_waiter_select(None)
break break
else: else:
messagebox.showerror("❌ Gagal", "Gagal mengubah status pesanan") messagebox.showerror("❌ Gagal", "Gagal mengubah status pesanan")

View File

@ -1,5 +1,5 @@
id,nama,kategori,harga,stok,foto,tersedia,item_discount_pct id,nama,kategori,harga,stok,foto,tersedia,item_discount_pct
1,Americano,Minuman,20000,6,,1,0 1,Americano,Minuman,20000,5,,1,0
2,Latte,Minuman,25000,1,,1,10 2,Latte,Minuman,25000,1,,1,10
3,Banana Cake,Dessert,30000,0,,0,0 3,Banana Cake,Dessert,30000,0,,0,0
4,Nasi Goreng,Makanan,35000,0,,0,0 4,Nasi Goreng,Makanan,35000,0,,0,0

1 id nama kategori harga stok foto tersedia item_discount_pct
2 1 Americano Minuman 20000 6 5 1 0
3 2 Latte Minuman 25000 1 1 10
4 3 Banana Cake Dessert 30000 0 0 0
5 4 Nasi Goreng Makanan 35000 0 0 0

View File

@ -1 +1,2 @@
id,user_id,nomor_meja,total,status,promo_code,subtotal,item_discount,promo_discount,tanggal id,user_id,nomor_meja,total,status,promo_code,subtotal,item_discount,promo_discount,tanggal
1,4,5,20000.0,dibayar,,20000.0,0.0,0.0,2025-12-10 11:26:40

1 id user_id nomor_meja total status promo_code subtotal item_discount promo_discount tanggal
2 1 4 5 20000.0 dibayar 20000.0 0.0 0.0 2025-12-10 11:26:40