From 251a9e37aed31eb58f282b97f588fcf90797c84a Mon Sep 17 00:00:00 2001 From: Jevinca Marvella Date: Wed, 10 Dec 2025 11:31:17 +0700 Subject: [PATCH] Update Waiter & Menu --- detail_transaksi.csv | 2 +- main.py | 163 ++++++++++++++++++++++++++----------------- menu.csv | 2 +- transaksi.csv | 1 + 4 files changed, 101 insertions(+), 67 deletions(-) diff --git a/detail_transaksi.csv b/detail_transaksi.csv index da34181..0ca8be3 100644 --- a/detail_transaksi.csv +++ b/detail_transaksi.csv @@ -1,2 +1,2 @@ id,transaksi_id,menu_id,qty,harga_satuan,subtotal_item - +1,1,1,1,20000.0,20000.0 diff --git a/main.py b/main.py index 527cf6c..67192c4 100644 --- a/main.py +++ b/main.py @@ -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("", _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("<>", 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") diff --git a/menu.csv b/menu.csv index 3e8367e..b25386d 100644 --- a/menu.csv +++ b/menu.csv @@ -1,5 +1,5 @@ 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 3,Banana Cake,Dessert,30000,0,,0,0 4,Nasi Goreng,Makanan,35000,0,,0,0 diff --git a/transaksi.csv b/transaksi.csv index a66928d..2fa0884 100644 --- a/transaksi.csv +++ b/transaksi.csv @@ -1 +1,2 @@ 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