|
|
|
|
@ -1083,7 +1083,7 @@ class App:
|
|
|
|
|
right.pack(side='right', fill='both', padx=6, pady=6)
|
|
|
|
|
|
|
|
|
|
# === 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
|
|
|
|
|
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="Reset", command=self.reset_order_search).pack(side='left', padx=3)
|
|
|
|
|
|
|
|
|
|
# Scrollable frame untuk cards
|
|
|
|
|
canvas = tk.Canvas(left, bg='white')
|
|
|
|
|
# Scrollable frame untuk cards (DENGAN BACKGROUND PUTIH)
|
|
|
|
|
canvas = tk.Canvas(left, bg='#f5f5f5', height=450, highlightthickness=0)
|
|
|
|
|
scrollbar = ttk.Scrollbar(left, orient="vertical", command=canvas.yview)
|
|
|
|
|
self.menu_cards_frame = ttk.Frame(canvas)
|
|
|
|
|
|
|
|
|
|
@ -1116,18 +1116,18 @@ class App:
|
|
|
|
|
canvas.bind_all("<MouseWheel>", _on_mousewheel)
|
|
|
|
|
|
|
|
|
|
# === 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
|
|
|
|
|
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:
|
|
|
|
|
self.cart_tree.heading(c, text=c)
|
|
|
|
|
if c == "Menu":
|
|
|
|
|
self.cart_tree.column(c, width=150)
|
|
|
|
|
else:
|
|
|
|
|
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
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
# Info total
|
|
|
|
|
self.cart_subtotal_label = ttk.Label(right, text="Subtotal: Rp 0", font=("Arial", 10))
|
|
|
|
|
self.cart_subtotal_label.pack(pady=2)
|
|
|
|
|
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)
|
|
|
|
|
total_frame = ttk.Frame(right)
|
|
|
|
|
total_frame.pack(fill='x', 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.pack(pady=6)
|
|
|
|
|
ttk.Label(checkout_frame, text="No. Meja:").grid(row=0, column=0, sticky='e', padx=3, pady=3)
|
|
|
|
|
checkout_frame.pack(fill='x', pady=6, padx=10)
|
|
|
|
|
|
|
|
|
|
# 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()
|
|
|
|
|
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()
|
|
|
|
|
ttk.Entry(checkout_frame, textvariable=self.order_promo_var, width=15).grid(row=1, column=1, pady=3)
|
|
|
|
|
ttk.Button(checkout_frame, text="Terapkan", command=self.update_cart_display).grid(row=1, column=2, padx=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, width=10).grid(row=1, column=2, padx=3, sticky='e')
|
|
|
|
|
|
|
|
|
|
# Tombol checkout
|
|
|
|
|
ttk.Button(right, text="🛒 CHECKOUT", command=self.checkout_order, style="Accent.TButton").pack(pady=8)
|
|
|
|
|
# Configure columns
|
|
|
|
|
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
|
|
|
|
|
self.cart_items = [] # List of dict: {'menu_id': int, 'qty': int}
|
|
|
|
|
self.cart_items = []
|
|
|
|
|
|
|
|
|
|
# Load menu cards
|
|
|
|
|
self.reload_order_menu_cards()
|
|
|
|
|
@ -1187,8 +1204,15 @@ class App:
|
|
|
|
|
for menu_data in results:
|
|
|
|
|
mid, nama, kategori, harga, stok, foto, tersedia, item_disc = menu_data
|
|
|
|
|
|
|
|
|
|
# Create card frame
|
|
|
|
|
card = tk.Frame(self.menu_cards_frame, relief='ridge', borderwidth=2, bg='white', padx=10, pady=10)
|
|
|
|
|
# Create card frame (DENGAN BACKGROUND PUTIH + BORDER)
|
|
|
|
|
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')
|
|
|
|
|
|
|
|
|
|
# Gambar
|
|
|
|
|
@ -1198,14 +1222,13 @@ class App:
|
|
|
|
|
img = img.resize((150, 100), Image.Resampling.LANCZOS)
|
|
|
|
|
photo = ImageTk.PhotoImage(img)
|
|
|
|
|
|
|
|
|
|
# Simpan reference agar tidak di-garbage collect
|
|
|
|
|
img_label = tk.Label(card, image=photo, bg='white')
|
|
|
|
|
img_label.image = photo
|
|
|
|
|
img_label.pack()
|
|
|
|
|
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:
|
|
|
|
|
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
|
|
|
|
|
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}"
|
|
|
|
|
if item_disc > 0:
|
|
|
|
|
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
|
|
|
|
|
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',
|
|
|
|
|
fg='white',
|
|
|
|
|
width=3,
|
|
|
|
|
borderwidth=0,
|
|
|
|
|
cursor='hand2',
|
|
|
|
|
command=lambda m=mid: self.decrease_from_card(m)
|
|
|
|
|
).pack(side='left', padx=2)
|
|
|
|
|
|
|
|
|
|
@ -1255,6 +1282,8 @@ class App:
|
|
|
|
|
bg='#4CAF50',
|
|
|
|
|
fg='white',
|
|
|
|
|
width=3,
|
|
|
|
|
borderwidth=0,
|
|
|
|
|
cursor='hand2',
|
|
|
|
|
command=lambda m=mid, s=stok: self.increase_from_card(m, s)
|
|
|
|
|
).pack(side='left', padx=2)
|
|
|
|
|
else:
|
|
|
|
|
@ -1266,15 +1295,17 @@ class App:
|
|
|
|
|
bg='#4CAF50',
|
|
|
|
|
fg='white',
|
|
|
|
|
width=12,
|
|
|
|
|
borderwidth=0,
|
|
|
|
|
cursor='hand2',
|
|
|
|
|
command=lambda m=mid, s=stok: self.increase_from_card(m, s)
|
|
|
|
|
).pack()
|
|
|
|
|
|
|
|
|
|
# Next column
|
|
|
|
|
col += 1
|
|
|
|
|
if col >= 2: # 2 cards per row
|
|
|
|
|
if col >= 2:
|
|
|
|
|
col = 0
|
|
|
|
|
row += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def reset_order_search(self):
|
|
|
|
|
self.order_search_var.set("")
|
|
|
|
|
self.reload_order_menu_cards()
|
|
|
|
|
@ -1420,7 +1451,7 @@ class App:
|
|
|
|
|
self.order_meja_var.set("")
|
|
|
|
|
self.order_promo_var.set("")
|
|
|
|
|
self.update_cart_display()
|
|
|
|
|
self.reload_order_menu() # Refresh stok
|
|
|
|
|
self.reload_order_menu_cards() # Refresh stok
|
|
|
|
|
else:
|
|
|
|
|
messagebox.showerror("Error", f"Gagal menyimpan pesanan: {result}")
|
|
|
|
|
|
|
|
|
|
@ -1438,26 +1469,26 @@ class App:
|
|
|
|
|
|
|
|
|
|
# Header
|
|
|
|
|
header = ttk.Frame(parent)
|
|
|
|
|
header.pack(fill='x', padx=10, pady=8)
|
|
|
|
|
ttk.Label(header, text="Dashboard Waiter - Kelola Pesanan", font=("Arial", 14, "bold")).pack(side='left')
|
|
|
|
|
header.pack(fill='x', padx=10, pady=6)
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
# Filter status
|
|
|
|
|
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.Button(filter_frame, text="Semua", command=lambda: self.reload_waiter_orders(None)).pack(side='left', padx=3)
|
|
|
|
|
ttk.Button(filter_frame, text="Pending", command=lambda: self.reload_waiter_orders('pending')).pack(side='left', padx=3)
|
|
|
|
|
ttk.Button(filter_frame, text="Menunggu", command=lambda: self.reload_waiter_orders('menunggu')).pack(side='left', padx=3)
|
|
|
|
|
ttk.Button(filter_frame, text="Diproses", command=lambda: self.reload_waiter_orders('diproses')).pack(side='left', padx=3)
|
|
|
|
|
ttk.Button(filter_frame, text="Selesai", command=lambda: self.reload_waiter_orders('selesai')).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'), width=8).pack(side='left', padx=2)
|
|
|
|
|
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'), width=8).pack(side='left', padx=2)
|
|
|
|
|
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.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")
|
|
|
|
|
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("User ID", text="User ID")
|
|
|
|
|
@ -1485,53 +1516,53 @@ class App:
|
|
|
|
|
# Bind event saat pilih pesanan
|
|
|
|
|
self.waiter_tree.bind("<<TreeviewSelect>>", self.on_waiter_select)
|
|
|
|
|
|
|
|
|
|
# Detail pesanan dengan SCROLLBAR
|
|
|
|
|
detail_frame = ttk.LabelFrame(parent, text="📋 Detail Pesanan Terpilih", padding=10)
|
|
|
|
|
detail_frame.pack(fill='both', expand=True, padx=10, pady=6)
|
|
|
|
|
# Detail pesanan (LEBIH PENDEK - height=6 aja)
|
|
|
|
|
detail_frame = ttk.LabelFrame(parent, text="📋 Detail Pesanan", padding=8)
|
|
|
|
|
detail_frame.pack(fill='x', padx=10, pady=4)
|
|
|
|
|
|
|
|
|
|
# Frame untuk text + scrollbar
|
|
|
|
|
text_frame = ttk.Frame(detail_frame)
|
|
|
|
|
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)
|
|
|
|
|
self.waiter_detail_text.configure(yscrollcommand=detail_scroll.set)
|
|
|
|
|
|
|
|
|
|
self.waiter_detail_text.pack(side='left', fill='both', expand=True)
|
|
|
|
|
detail_scroll.pack(side='right', fill='y')
|
|
|
|
|
|
|
|
|
|
# Tombol aksi - PASTI KELIHATAN
|
|
|
|
|
action_frame = ttk.LabelFrame(parent, text="🎯 Ubah Status Pesanan", padding=15)
|
|
|
|
|
action_frame.pack(fill='x', padx=10, pady=10)
|
|
|
|
|
# TOMBOL AKSI - PASTI KELIHATAN (TIDAK PAKAI expand=True)
|
|
|
|
|
action_frame = ttk.LabelFrame(parent, text="🎯 Ubah Status Pesanan", padding=12)
|
|
|
|
|
action_frame.pack(fill='x', padx=10, pady=8)
|
|
|
|
|
|
|
|
|
|
# Grid 2x2 untuk 4 tombol
|
|
|
|
|
ttk.Button(
|
|
|
|
|
action_frame,
|
|
|
|
|
text="✅ Terima Pesanan (Pending → Menunggu)",
|
|
|
|
|
text="✅ Terima (Pending → Menunggu)",
|
|
|
|
|
command=lambda: self.update_order_status('menunggu'),
|
|
|
|
|
width=40
|
|
|
|
|
).grid(row=0, column=0, padx=5, pady=5, sticky='ew')
|
|
|
|
|
width=35
|
|
|
|
|
).grid(row=0, column=0, padx=4, pady=4, sticky='ew')
|
|
|
|
|
|
|
|
|
|
ttk.Button(
|
|
|
|
|
action_frame,
|
|
|
|
|
text="🍳 Proses Pesanan (Menunggu → Diproses)",
|
|
|
|
|
text="🍳 Proses (Menunggu → Diproses)",
|
|
|
|
|
command=lambda: self.update_order_status('diproses'),
|
|
|
|
|
width=40
|
|
|
|
|
).grid(row=0, column=1, padx=5, pady=5, sticky='ew')
|
|
|
|
|
width=35
|
|
|
|
|
).grid(row=0, column=1, padx=4, pady=4, sticky='ew')
|
|
|
|
|
|
|
|
|
|
ttk.Button(
|
|
|
|
|
action_frame,
|
|
|
|
|
text="🍽️ Selesai Disajikan (Diproses → Selesai)",
|
|
|
|
|
text="🍽️ Selesai (Diproses → Selesai)",
|
|
|
|
|
command=lambda: self.update_order_status('selesai'),
|
|
|
|
|
width=40
|
|
|
|
|
).grid(row=1, column=0, padx=5, pady=5, sticky='ew')
|
|
|
|
|
width=35
|
|
|
|
|
).grid(row=1, column=0, padx=4, pady=4, sticky='ew')
|
|
|
|
|
|
|
|
|
|
ttk.Button(
|
|
|
|
|
action_frame,
|
|
|
|
|
text="💰 Selesai Dilayani (Selesai → Dibayar)",
|
|
|
|
|
text="💰 Dibayar (Selesai → Dibayar)",
|
|
|
|
|
command=lambda: self.update_order_status('dibayar'),
|
|
|
|
|
width=40
|
|
|
|
|
).grid(row=1, column=1, padx=5, pady=5, sticky='ew')
|
|
|
|
|
width=35
|
|
|
|
|
).grid(row=1, column=1, padx=4, pady=4, sticky='ew')
|
|
|
|
|
|
|
|
|
|
# Buat kolom responsive
|
|
|
|
|
action_frame.columnconfigure(0, weight=1)
|
|
|
|
|
@ -1651,7 +1682,7 @@ class App:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# Konfirmasi
|
|
|
|
|
if not messagebox.askyesno("Konfirmasi Ubah Status",
|
|
|
|
|
if not messagebox.askyesno("Konfirmasi",
|
|
|
|
|
f"Ubah status pesanan #{transaksi_id}\n\n"
|
|
|
|
|
f"Dari: {current_status.upper()}\n"
|
|
|
|
|
f"Ke: {new_status.upper()}\n\n"
|
|
|
|
|
@ -1664,11 +1695,13 @@ class App:
|
|
|
|
|
if success:
|
|
|
|
|
messagebox.showinfo("✅ Berhasil", f"Status berhasil diubah menjadi '{new_status.upper()}'")
|
|
|
|
|
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():
|
|
|
|
|
if self.waiter_tree.item(item_id)['values'][0] == transaksi_id:
|
|
|
|
|
self.waiter_tree.selection_set(item_id)
|
|
|
|
|
self.waiter_tree.see(item_id)
|
|
|
|
|
# Trigger event untuk reload detail
|
|
|
|
|
self.on_waiter_select(None)
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
messagebox.showerror("❌ Gagal", "Gagal mengubah status pesanan")
|
|
|
|
|
|