Update main.py
This commit is contained in:
parent
f9e901d674
commit
f616da6fa0
635
main.py
635
main.py
@ -21,6 +21,22 @@ DETAIL_TRANSAKSI_CSV = "detail_transaksi.csv"
|
|||||||
FAVORITES_CSV = "favorites.csv"
|
FAVORITES_CSV = "favorites.csv"
|
||||||
IMG_PREVIEW_SIZE = (120, 80)
|
IMG_PREVIEW_SIZE = (120, 80)
|
||||||
|
|
||||||
|
COLORS = {
|
||||||
|
'primary': '#2C3E50', # Dark Blue
|
||||||
|
'secondary': '#E67E22', # Orange
|
||||||
|
'success': '#27AE60', # Green
|
||||||
|
'danger': '#E74C3C', # Red
|
||||||
|
'warning': '#F39C12', # Yellow
|
||||||
|
'info': '#3498DB', # Light Blue
|
||||||
|
'light': '#ECF0F1', # Light Gray
|
||||||
|
'dark': '#34495E', # Dark Gray
|
||||||
|
'bg_gradient_start': '#667eea', # Purple
|
||||||
|
'bg_gradient_end': '#764ba2', # Dark Purple
|
||||||
|
'cafe_brown': '#8B4513', # Saddle Brown
|
||||||
|
'cafe_cream': '#F5DEB3', # Wheat
|
||||||
|
'cafe_green': '#228B22', # Forest Green
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def ensure_file(path, fieldnames):
|
def ensure_file(path, fieldnames):
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
@ -586,12 +602,35 @@ def get_user_favorites(user_id, limit=5):
|
|||||||
class App:
|
class App:
|
||||||
def __init__(self, root):
|
def __init__(self, root):
|
||||||
self.root = root
|
self.root = root
|
||||||
self.root.title("Cafe Totoro Mania")
|
self.root.title("🍵 Cafe Totoro Mania")
|
||||||
self.session = None
|
self.session = None
|
||||||
self.img_cache = {}
|
self.img_cache = {}
|
||||||
self.cart = []
|
self.cart = []
|
||||||
|
self.setup_styles()
|
||||||
self.setup_ui()
|
self.setup_ui()
|
||||||
|
|
||||||
|
def setup_styles(self):
|
||||||
|
style = ttk.Style()
|
||||||
|
style.theme_use('clam')
|
||||||
|
|
||||||
|
# Button styles
|
||||||
|
style.configure('Primary.TButton', background=COLORS['secondary'],
|
||||||
|
foreground='white', font=('Arial', 10, 'bold'))
|
||||||
|
style.map('Primary.TButton', background=[('active', COLORS['warning'])])
|
||||||
|
|
||||||
|
style.configure('Success.TButton', background=COLORS['success'],
|
||||||
|
foreground='white', font=('Arial', 10, 'bold'))
|
||||||
|
|
||||||
|
style.configure('Danger.TButton', background=COLORS['danger'],
|
||||||
|
foreground='white', font=('Arial', 10, 'bold'))
|
||||||
|
|
||||||
|
# Treeview style
|
||||||
|
style.configure('Treeview', background=COLORS['light'],
|
||||||
|
foreground=COLORS['dark'], font=('Arial', 9))
|
||||||
|
style.configure('Treeview.Heading', background=COLORS['primary'],
|
||||||
|
foreground='white', font=('Arial', 10, 'bold'))
|
||||||
|
style.map('Treeview', background=[('selected', COLORS['info'])])
|
||||||
|
|
||||||
def setup_ui(self):
|
def setup_ui(self):
|
||||||
self.root.geometry("1000x650")
|
self.root.geometry("1000x650")
|
||||||
self.root.resizable(False, False)
|
self.root.resizable(False, False)
|
||||||
@ -600,20 +639,77 @@ class App:
|
|||||||
def login_frame(self):
|
def login_frame(self):
|
||||||
for w in self.root.winfo_children():
|
for w in self.root.winfo_children():
|
||||||
w.destroy()
|
w.destroy()
|
||||||
frame = ttk.Frame(self.root, padding=20)
|
|
||||||
frame.pack(expand=True)
|
|
||||||
|
|
||||||
ttk.Label(frame, text="Login kak", font=("Arial", 24)).grid(row=0, column=0, columnspan=2, pady=10)
|
# Main container dengan background cafe
|
||||||
|
main_frame = tk.Frame(self.root, bg=COLORS['cafe_brown'])
|
||||||
|
main_frame.pack(fill='both', expand=True)
|
||||||
|
|
||||||
|
# Center frame
|
||||||
|
center = tk.Frame(main_frame, bg='white', relief='raised', bd=3)
|
||||||
|
center.place(relx=0.5, rely=0.5, anchor='center', width=500, height=550)
|
||||||
|
|
||||||
|
# Logo/Header Section
|
||||||
|
header_frame = tk.Frame(center, bg=COLORS['cafe_green'], height=120)
|
||||||
|
header_frame.pack(fill='x')
|
||||||
|
|
||||||
|
tk.Label(header_frame, text="☕", font=("Arial", 48),
|
||||||
|
bg=COLORS['cafe_green'], fg='white').pack(pady=5)
|
||||||
|
|
||||||
|
tk.Label(header_frame, text="CAFE TOTORO MANIA",
|
||||||
|
font=("Arial", 24, "bold"), bg=COLORS['cafe_green'],
|
||||||
|
fg='white').pack()
|
||||||
|
|
||||||
|
tk.Label(header_frame, text="~ Your Cozy Coffee Corner ~",
|
||||||
|
font=("Arial", 11, "italic"), bg=COLORS['cafe_green'],
|
||||||
|
fg=COLORS['cafe_cream']).pack(pady=2)
|
||||||
|
|
||||||
|
# Form Section
|
||||||
|
form_frame = tk.Frame(center, bg='white', padx=40, pady=30)
|
||||||
|
form_frame.pack(fill='both', expand=True)
|
||||||
|
|
||||||
|
tk.Label(form_frame, text="Selamat Datang! 👋",
|
||||||
|
font=("Arial", 18, "bold"), bg='white',
|
||||||
|
fg=COLORS['cafe_brown']).pack(pady=(0,10))
|
||||||
|
|
||||||
|
tk.Label(form_frame, text="Silakan login untuk melanjutkan",
|
||||||
|
font=("Arial", 10), bg='white',
|
||||||
|
fg=COLORS['dark']).pack(pady=(0,25))
|
||||||
|
|
||||||
|
# Username
|
||||||
|
tk.Label(form_frame, text="👤 Username", font=("Arial", 11, "bold"),
|
||||||
|
bg='white', fg=COLORS['dark']).pack(anchor='w', pady=(10,5))
|
||||||
|
|
||||||
ttk.Label(frame, text="Username:").grid(row=1, column=0, sticky='e', pady=5)
|
|
||||||
self.username_var = tk.StringVar()
|
self.username_var = tk.StringVar()
|
||||||
ttk.Entry(frame, textvariable=self.username_var, width=30).grid(row=1, column=1, pady=5)
|
username_entry = tk.Entry(form_frame, textvariable=self.username_var,
|
||||||
|
font=("Arial", 12), relief='solid', bd=2)
|
||||||
|
username_entry.pack(fill='x', ipady=8)
|
||||||
|
|
||||||
|
# Password
|
||||||
|
tk.Label(form_frame, text="🔒 Password", font=("Arial", 11, "bold"),
|
||||||
|
bg='white', fg=COLORS['dark']).pack(anchor='w', pady=(15,5))
|
||||||
|
|
||||||
ttk.Label(frame, text="Password:").grid(row=2, column=0, sticky='e', pady=5)
|
|
||||||
self.password_var = tk.StringVar()
|
self.password_var = tk.StringVar()
|
||||||
ttk.Entry(frame, textvariable=self.password_var, show="*", width=30).grid(row=2, column=1, pady=5)
|
password_entry = tk.Entry(form_frame, textvariable=self.password_var,
|
||||||
|
show="●", font=("Arial", 12), relief='solid', bd=2)
|
||||||
|
password_entry.pack(fill='x', ipady=8)
|
||||||
|
|
||||||
ttk.Button(frame, text="Login", command=self.handle_login).grid(row=3, column=0, columnspan=2, pady=12)
|
# Login Button
|
||||||
|
login_btn = tk.Button(form_frame, text="🔐 LOGIN", command=self.handle_login,
|
||||||
|
font=("Arial", 13, "bold"), bg=COLORS['cafe_green'],
|
||||||
|
fg='white', relief='flat', cursor='hand2', bd=0)
|
||||||
|
login_btn.pack(fill='x', pady=(25,10), ipady=10)
|
||||||
|
|
||||||
|
# Hover effect
|
||||||
|
login_btn.bind("<Enter>", lambda e: login_btn.config(bg=COLORS['success']))
|
||||||
|
login_btn.bind("<Leave>", lambda e: login_btn.config(bg=COLORS['cafe_green']))
|
||||||
|
|
||||||
|
# Info text
|
||||||
|
info_frame = tk.Frame(center, bg=COLORS['light'], height=60)
|
||||||
|
info_frame.pack(fill='x', side='bottom')
|
||||||
|
|
||||||
|
tk.Label(info_frame, text="💡 Tip: Gunakan user/user123 untuk pembeli",
|
||||||
|
font=("Arial", 9), bg=COLORS['light'],
|
||||||
|
fg=COLORS['dark']).pack(pady=15)
|
||||||
|
|
||||||
def handle_login(self):
|
def handle_login(self):
|
||||||
u = self.username_var.get().strip()
|
u = self.username_var.get().strip()
|
||||||
@ -1002,7 +1098,528 @@ class App:
|
|||||||
self.reload_promo_table()
|
self.reload_promo_table()
|
||||||
|
|
||||||
|
|
||||||
|
# ========== TAB: ORDER SYSTEM (PEMBELI) ==========
|
||||||
|
def build_order_tab(self, parent):
|
||||||
|
"""Tab pemesanan untuk pembeli"""
|
||||||
|
for w in parent.winfo_children():
|
||||||
|
w.destroy()
|
||||||
|
|
||||||
|
# Container utama
|
||||||
|
container = ttk.Frame(parent)
|
||||||
|
container.pack(fill='both', expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
# LEFT: Daftar Menu
|
||||||
|
left = ttk.Frame(container, relief='solid', borderwidth=1)
|
||||||
|
left.pack(side='left', fill='both', expand=True, padx=(0, 5))
|
||||||
|
|
||||||
|
ttk.Label(left, text="🍽️ Menu Tersedia", font=("Arial", 14, "bold")).pack(pady=8)
|
||||||
|
|
||||||
|
# Filter pencarian
|
||||||
|
filter_frm = ttk.Frame(left)
|
||||||
|
filter_frm.pack(fill='x', padx=10, pady=5)
|
||||||
|
ttk.Label(filter_frm, text="Cari Menu:").pack(side='left', padx=5)
|
||||||
|
self.order_search_var = tk.StringVar()
|
||||||
|
ttk.Entry(filter_frm, textvariable=self.order_search_var, width=20).pack(side='left', padx=5)
|
||||||
|
ttk.Button(filter_frm, text="🔍 Cari", command=self.reload_order_menu).pack(side='left', padx=3)
|
||||||
|
ttk.Button(filter_frm, text="🔄 Reset", command=self.reset_order_search).pack(side='left', padx=3)
|
||||||
|
|
||||||
|
# Treeview menu
|
||||||
|
cols = ("ID", "Nama", "Kategori", "Harga", "Stok")
|
||||||
|
self.order_menu_tree = ttk.Treeview(left, columns=cols, show='headings', height=15)
|
||||||
|
for c in cols:
|
||||||
|
self.order_menu_tree.heading(c, text=c)
|
||||||
|
w = 50 if c == "ID" else (180 if c == "Nama" else 100)
|
||||||
|
self.order_menu_tree.column(c, width=w)
|
||||||
|
self.order_menu_tree.pack(fill='both', expand=True, padx=10, pady=5)
|
||||||
|
|
||||||
|
# Tombol tambah ke keranjang
|
||||||
|
add_frame = ttk.Frame(left)
|
||||||
|
add_frame.pack(fill='x', padx=10, pady=8)
|
||||||
|
ttk.Label(add_frame, text="Jumlah:", font=("Arial", 10)).pack(side='left', padx=5)
|
||||||
|
self.order_qty_var = tk.StringVar(value="1")
|
||||||
|
ttk.Entry(add_frame, textvariable=self.order_qty_var, width=8).pack(side='left', padx=5)
|
||||||
|
ttk.Button(add_frame, text="➕ Tambah ke Keranjang", command=self.add_to_cart).pack(side='left', padx=10)
|
||||||
|
|
||||||
|
# RIGHT: Keranjang
|
||||||
|
right = ttk.Frame(container, relief='solid', borderwidth=1)
|
||||||
|
right.pack(side='right', fill='both', padx=(5, 0))
|
||||||
|
right.config(width=350)
|
||||||
|
|
||||||
|
ttk.Label(right, text="🛒 Keranjang Belanja", font=("Arial", 14, "bold")).pack(pady=8)
|
||||||
|
|
||||||
|
# Treeview keranjang
|
||||||
|
cart_cols = ("Menu", "Harga", "Qty", "Subtotal")
|
||||||
|
self.cart_tree = ttk.Treeview(right, columns=cart_cols, show='headings', height=12)
|
||||||
|
for c in cart_cols:
|
||||||
|
self.cart_tree.heading(c, text=c)
|
||||||
|
w = 120 if c == "Menu" else 70
|
||||||
|
self.cart_tree.column(c, width=w)
|
||||||
|
self.cart_tree.pack(fill='both', padx=10, pady=5)
|
||||||
|
|
||||||
|
# Tombol update & hapus
|
||||||
|
cart_btn_frm = ttk.Frame(right)
|
||||||
|
cart_btn_frm.pack(fill='x', padx=10, pady=5)
|
||||||
|
ttk.Button(cart_btn_frm, text="🗑️ Hapus Item", command=self.remove_from_cart).pack(side='left', padx=3)
|
||||||
|
ttk.Button(cart_btn_frm, text="📝 Update Qty", command=self.update_cart_qty).pack(side='left', padx=3)
|
||||||
|
|
||||||
|
# Total
|
||||||
|
self.cart_total_var = tk.StringVar(value="Total: Rp 0")
|
||||||
|
ttk.Label(right, textvariable=self.cart_total_var, font=("Arial", 12, "bold")).pack(pady=5)
|
||||||
|
|
||||||
|
# Form nomor meja
|
||||||
|
meja_frm = ttk.Frame(right)
|
||||||
|
meja_frm.pack(fill='x', padx=10, pady=8)
|
||||||
|
ttk.Label(meja_frm, text="Nomor Meja:", font=("Arial", 10)).pack(side='left', padx=5)
|
||||||
|
self.nomor_meja_var = tk.StringVar()
|
||||||
|
ttk.Entry(meja_frm, textvariable=self.nomor_meja_var, width=10).pack(side='left', padx=5)
|
||||||
|
|
||||||
|
# Tombol pesan
|
||||||
|
ttk.Button(right, text="📋 PESAN SEKARANG", command=self.submit_order,
|
||||||
|
style="Accent.TButton").pack(pady=10, padx=10, fill='x')
|
||||||
|
|
||||||
|
self.reload_order_menu()
|
||||||
|
|
||||||
|
def reload_order_menu(self):
|
||||||
|
"""Reload daftar menu untuk order"""
|
||||||
|
for item in self.order_menu_tree.get_children():
|
||||||
|
self.order_menu_tree.delete(item)
|
||||||
|
|
||||||
|
search = self.order_search_var.get().strip() if hasattr(self, 'order_search_var') else ""
|
||||||
|
menus = menu_list(available_only=True, search_text=search or None)
|
||||||
|
|
||||||
|
for m in menus:
|
||||||
|
mid, nama, kategori, harga, stok, foto, tersedia, disc = m
|
||||||
|
self.order_menu_tree.insert("", tk.END, values=(mid, nama, kategori, f"Rp {harga:,.0f}", stok))
|
||||||
|
|
||||||
|
def reset_order_search(self):
|
||||||
|
self.order_search_var.set("")
|
||||||
|
self.reload_order_menu()
|
||||||
|
|
||||||
|
def add_to_cart(self):
|
||||||
|
"""Tambah item ke keranjang"""
|
||||||
|
sel = self.order_menu_tree.selection()
|
||||||
|
if not sel:
|
||||||
|
messagebox.showwarning("Pilih Menu", "Pilih menu terlebih dahulu!")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
qty = int(self.order_qty_var.get())
|
||||||
|
if qty <= 0:
|
||||||
|
raise ValueError()
|
||||||
|
except:
|
||||||
|
messagebox.showerror("Error", "Jumlah harus angka positif!")
|
||||||
|
return
|
||||||
|
|
||||||
|
item_vals = self.order_menu_tree.item(sel)['values']
|
||||||
|
menu_id = item_vals[0]
|
||||||
|
nama = item_vals[1]
|
||||||
|
harga_str = item_vals[3].replace("Rp ", "").replace(",", "")
|
||||||
|
harga = float(harga_str)
|
||||||
|
stok = int(item_vals[4])
|
||||||
|
|
||||||
|
if qty > stok:
|
||||||
|
messagebox.showerror("Stok Habis", f"Stok hanya tersedia {stok}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Cek apakah sudah ada di cart
|
||||||
|
found = False
|
||||||
|
for cart_item in self.cart:
|
||||||
|
if cart_item['menu_id'] == menu_id:
|
||||||
|
cart_item['qty'] += qty
|
||||||
|
cart_item['subtotal'] = cart_item['harga'] * cart_item['qty']
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
self.cart.append({
|
||||||
|
'menu_id': menu_id,
|
||||||
|
'nama': nama,
|
||||||
|
'harga': harga,
|
||||||
|
'qty': qty,
|
||||||
|
'subtotal': harga * qty
|
||||||
|
})
|
||||||
|
|
||||||
|
self.update_cart_display()
|
||||||
|
messagebox.showinfo("Berhasil", f"✅ {nama} x{qty} ditambahkan ke keranjang!")
|
||||||
|
|
||||||
|
def update_cart_display(self):
|
||||||
|
"""Update tampilan keranjang"""
|
||||||
|
for item in self.cart_tree.get_children():
|
||||||
|
self.cart_tree.delete(item)
|
||||||
|
|
||||||
|
total = 0.0
|
||||||
|
for item in self.cart:
|
||||||
|
self.cart_tree.insert("", tk.END, values=(
|
||||||
|
item['nama'],
|
||||||
|
f"Rp {item['harga']:,.0f}",
|
||||||
|
item['qty'],
|
||||||
|
f"Rp {item['subtotal']:,.0f}"
|
||||||
|
))
|
||||||
|
total += item['subtotal']
|
||||||
|
|
||||||
|
self.cart_total_var.set(f"Total: Rp {total:,.0f}")
|
||||||
|
|
||||||
|
def remove_from_cart(self):
|
||||||
|
"""Hapus item dari keranjang"""
|
||||||
|
sel = self.cart_tree.selection()
|
||||||
|
if not sel:
|
||||||
|
messagebox.showwarning("Pilih Item", "Pilih item yang akan dihapus!")
|
||||||
|
return
|
||||||
|
|
||||||
|
idx = self.cart_tree.index(sel)
|
||||||
|
del self.cart[idx]
|
||||||
|
self.update_cart_display()
|
||||||
|
messagebox.showinfo("Dihapus", "Item berhasil dihapus dari keranjang")
|
||||||
|
|
||||||
|
def update_cart_qty(self):
|
||||||
|
"""Update jumlah item di keranjang"""
|
||||||
|
sel = self.cart_tree.selection()
|
||||||
|
if not sel:
|
||||||
|
messagebox.showwarning("Pilih Item", "Pilih item yang akan diupdate!")
|
||||||
|
return
|
||||||
|
|
||||||
|
idx = self.cart_tree.index(sel)
|
||||||
|
item = self.cart[idx]
|
||||||
|
|
||||||
|
# Dialog input qty baru
|
||||||
|
new_qty = tk.simpledialog.askinteger("Update Jumlah",
|
||||||
|
f"Jumlah baru untuk {item['nama']}:",
|
||||||
|
initialvalue=item['qty'],
|
||||||
|
minvalue=1)
|
||||||
|
if new_qty:
|
||||||
|
item['qty'] = new_qty
|
||||||
|
item['subtotal'] = item['harga'] * new_qty
|
||||||
|
self.update_cart_display()
|
||||||
|
|
||||||
|
def submit_order(self):
|
||||||
|
"""Submit pesanan"""
|
||||||
|
if not self.cart:
|
||||||
|
messagebox.showwarning("Keranjang Kosong", "Tambahkan menu ke keranjang terlebih dahulu!")
|
||||||
|
return
|
||||||
|
|
||||||
|
nomor_meja = self.nomor_meja_var.get().strip()
|
||||||
|
if not nomor_meja:
|
||||||
|
messagebox.showwarning("Nomor Meja", "Masukkan nomor meja!")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
trans_id = create_transaksi(self.session['id'], nomor_meja, self.cart)
|
||||||
|
|
||||||
|
# Tambah ke favorites
|
||||||
|
for item in self.cart:
|
||||||
|
add_to_favorites(self.session['id'], item['menu_id'])
|
||||||
|
|
||||||
|
messagebox.showinfo("Berhasil",
|
||||||
|
f"✅ Pesanan berhasil dibuat!\n\n"
|
||||||
|
f"ID Transaksi: {trans_id}\n"
|
||||||
|
f"Nomor Meja: {nomor_meja}\n"
|
||||||
|
f"Total: Rp {sum(i['subtotal'] for i in self.cart):,.0f}\n\n"
|
||||||
|
f"Status: Menunggu\n"
|
||||||
|
f"Pesanan Anda akan segera diproses oleh waiter!")
|
||||||
|
|
||||||
|
# Reset
|
||||||
|
self.cart = []
|
||||||
|
self.nomor_meja_var.set("")
|
||||||
|
self.order_qty_var.set("1")
|
||||||
|
self.update_cart_display()
|
||||||
|
self.reload_order_menu()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Error", f"Gagal membuat pesanan: {e}")
|
||||||
|
|
||||||
|
# ========== TAB: WAITER DASHBOARD ==========
|
||||||
|
def build_waiter_tab(self, parent):
|
||||||
|
"""Dashboard untuk waiter mengelola pesanan"""
|
||||||
|
for w in parent.winfo_children():
|
||||||
|
w.destroy()
|
||||||
|
|
||||||
|
ttk.Label(parent, text="📋 Dashboard Waiter - Kelola Pesanan",
|
||||||
|
font=("Arial", 16, "bold")).pack(pady=10)
|
||||||
|
|
||||||
|
# Filter status
|
||||||
|
filter_frm = ttk.Frame(parent)
|
||||||
|
filter_frm.pack(fill='x', padx=10, pady=5)
|
||||||
|
ttk.Label(filter_frm, text="Filter Status:").pack(side='left', padx=5)
|
||||||
|
ttk.Button(filter_frm, text="🕐 Menunggu",
|
||||||
|
command=lambda: self.reload_waiter_orders("Menunggu")).pack(side='left', padx=3)
|
||||||
|
ttk.Button(filter_frm, text="⏳ Diproses",
|
||||||
|
command=lambda: self.reload_waiter_orders("Diproses")).pack(side='left', padx=3)
|
||||||
|
ttk.Button(filter_frm, text="✅ Selesai",
|
||||||
|
command=lambda: self.reload_waiter_orders("Selesai")).pack(side='left', padx=3)
|
||||||
|
ttk.Button(filter_frm, text="📋 Semua",
|
||||||
|
command=lambda: self.reload_waiter_orders(None)).pack(side='left', padx=3)
|
||||||
|
ttk.Button(filter_frm, text="🔄 Refresh",
|
||||||
|
command=lambda: self.reload_waiter_orders()).pack(side='right', padx=10)
|
||||||
|
|
||||||
|
# Treeview pesanan
|
||||||
|
cols = ("ID", "Tanggal", "Meja", "Total", "Status")
|
||||||
|
self.waiter_tree = ttk.Treeview(parent, columns=cols, show='headings', height=10)
|
||||||
|
for c in cols:
|
||||||
|
self.waiter_tree.heading(c, text=c)
|
||||||
|
w = 50 if c == "ID" else (150 if c == "Tanggal" else 80)
|
||||||
|
self.waiter_tree.column(c, width=w)
|
||||||
|
self.waiter_tree.pack(fill='both', expand=True, padx=10, pady=10)
|
||||||
|
self.waiter_tree.bind("<<TreeviewSelect>>", self.on_waiter_select)
|
||||||
|
|
||||||
|
# Detail pesanan
|
||||||
|
detail_frm = ttk.LabelFrame(parent, text="📝 Detail Pesanan", padding=10)
|
||||||
|
detail_frm.pack(fill='both', padx=10, pady=5)
|
||||||
|
|
||||||
|
self.waiter_detail_text = tk.Text(detail_frm, height=8, width=80, state='disabled')
|
||||||
|
self.waiter_detail_text.pack(side='left', fill='both', expand=True)
|
||||||
|
|
||||||
|
# Tombol aksi
|
||||||
|
btn_frm = ttk.Frame(parent)
|
||||||
|
btn_frm.pack(fill='x', padx=10, pady=10)
|
||||||
|
ttk.Button(btn_frm, text="⏳ Proses Pesanan",
|
||||||
|
command=lambda: self.change_order_status("Diproses")).pack(side='left', padx=5)
|
||||||
|
ttk.Button(btn_frm, text="✅ Selesai Dilayani",
|
||||||
|
command=lambda: self.change_order_status("Selesai")).pack(side='left', padx=5)
|
||||||
|
|
||||||
|
self.reload_waiter_orders()
|
||||||
|
|
||||||
|
def reload_waiter_orders(self, status=None):
|
||||||
|
"""Reload daftar pesanan untuk waiter"""
|
||||||
|
for item in self.waiter_tree.get_children():
|
||||||
|
self.waiter_tree.delete(item)
|
||||||
|
|
||||||
|
orders = get_all_transaksi(status=status)
|
||||||
|
|
||||||
|
for order in orders:
|
||||||
|
status_icon = {"Menunggu": "🕐", "Diproses": "⏳", "Selesai": "✅"}.get(order['status'], "")
|
||||||
|
self.waiter_tree.insert("", tk.END, values=(
|
||||||
|
order['id'],
|
||||||
|
order['tanggal'],
|
||||||
|
order['nomor_meja'],
|
||||||
|
f"Rp {order['total']:,.0f}",
|
||||||
|
f"{status_icon} {order['status']}"
|
||||||
|
))
|
||||||
|
|
||||||
|
def on_waiter_select(self, event):
|
||||||
|
"""Ketika waiter pilih pesanan, tampilkan detail"""
|
||||||
|
sel = self.waiter_tree.selection()
|
||||||
|
if not sel:
|
||||||
|
return
|
||||||
|
|
||||||
|
trans_id = self.waiter_tree.item(sel)['values'][0]
|
||||||
|
details = get_transaksi_detail(trans_id)
|
||||||
|
|
||||||
|
self.waiter_detail_text.config(state='normal')
|
||||||
|
self.waiter_detail_text.delete('1.0', tk.END)
|
||||||
|
|
||||||
|
text = f"ID Transaksi: {trans_id}\n"
|
||||||
|
text += f"{'='*50}\n"
|
||||||
|
text += f"{'Menu':<25} {'Qty':<5} {'Harga':<12} {'Subtotal':<12}\n"
|
||||||
|
text += f"{'-'*50}\n"
|
||||||
|
|
||||||
|
for d in details:
|
||||||
|
text += f"{d['nama_menu']:<25} {d['jumlah']:<5} Rp {d['harga_satuan']:>8,.0f} Rp {d['subtotal']:>8,.0f}\n"
|
||||||
|
|
||||||
|
self.waiter_detail_text.insert('1.0', text)
|
||||||
|
self.waiter_detail_text.config(state='disabled')
|
||||||
|
|
||||||
|
def change_order_status(self, new_status):
|
||||||
|
"""Ubah status pesanan"""
|
||||||
|
sel = self.waiter_tree.selection()
|
||||||
|
if not sel:
|
||||||
|
messagebox.showwarning("Pilih Pesanan", "Pilih pesanan terlebih dahulu!")
|
||||||
|
return
|
||||||
|
|
||||||
|
trans_id = self.waiter_tree.item(sel)['values'][0]
|
||||||
|
current_status = self.waiter_tree.item(sel)['values'][4].split()[-1]
|
||||||
|
|
||||||
|
# Validasi flow status
|
||||||
|
if current_status == "Selesai":
|
||||||
|
messagebox.showinfo("Info", "Pesanan sudah selesai dilayani!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# ========== LANJUTAN DARI ARTIFACTS SEBELUMNYA ==========
|
||||||
|
# Paste kode ini setelah fungsi change_order_status
|
||||||
|
|
||||||
|
if new_status == "Selesai" and current_status == "Menunggu":
|
||||||
|
if not messagebox.askyesno("Konfirmasi", "Pesanan belum diproses. Langsung selesai?"):
|
||||||
|
return
|
||||||
|
|
||||||
|
update_transaksi_status(trans_id, new_status)
|
||||||
|
messagebox.showinfo("Berhasil", f"Status pesanan diubah menjadi: {new_status}")
|
||||||
|
self.reload_waiter_orders()
|
||||||
|
|
||||||
|
# ========== TAB: FAVORITES (PEMBELI) ==========
|
||||||
|
def build_favorites_tab(self, parent):
|
||||||
|
"""Tab menu favorit untuk pembeli"""
|
||||||
|
for w in parent.winfo_children():
|
||||||
|
w.destroy()
|
||||||
|
|
||||||
|
ttk.Label(parent, text="⭐ Menu Favorit Saya",
|
||||||
|
font=("Arial", 16, "bold")).pack(pady=10)
|
||||||
|
|
||||||
|
ttk.Label(parent, text="Menu yang paling sering Anda pesan",
|
||||||
|
font=("Arial", 10)).pack(pady=5)
|
||||||
|
|
||||||
|
# Treeview favorites
|
||||||
|
cols = ("Nama Menu", "Kategori", "Harga", "Sering Dipesan")
|
||||||
|
self.fav_tree = ttk.Treeview(parent, columns=cols, show='headings', height=12)
|
||||||
|
for c in cols:
|
||||||
|
self.fav_tree.heading(c, text=c)
|
||||||
|
w = 200 if c == "Nama Menu" else 120
|
||||||
|
self.fav_tree.column(c, width=w)
|
||||||
|
self.fav_tree.pack(fill='both', expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
# Tombol pesan cepat
|
||||||
|
btn_frm = ttk.Frame(parent)
|
||||||
|
btn_frm.pack(pady=10)
|
||||||
|
ttk.Label(btn_frm, text="Pesan Cepat - Jumlah:").pack(side='left', padx=5)
|
||||||
|
self.fav_qty_var = tk.StringVar(value="1")
|
||||||
|
ttk.Entry(btn_frm, textvariable=self.fav_qty_var, width=8).pack(side='left', padx=5)
|
||||||
|
ttk.Button(btn_frm, text="🚀 Pesan Langsung",
|
||||||
|
command=self.quick_order_from_fav).pack(side='left', padx=10)
|
||||||
|
ttk.Button(btn_frm, text="🔄 Refresh",
|
||||||
|
command=self.reload_favorites).pack(side='left', padx=5)
|
||||||
|
|
||||||
|
self.reload_favorites()
|
||||||
|
|
||||||
|
def reload_favorites(self):
|
||||||
|
"""Reload menu favorit user"""
|
||||||
|
for item in self.fav_tree.get_children():
|
||||||
|
self.fav_tree.delete(item)
|
||||||
|
|
||||||
|
favs = get_user_favorites(self.session['id'], limit=10)
|
||||||
|
|
||||||
|
if not favs:
|
||||||
|
self.fav_tree.insert("", tk.END, values=(
|
||||||
|
"Belum ada menu favorit", "-", "-", "0"
|
||||||
|
))
|
||||||
|
return
|
||||||
|
|
||||||
|
for fav in favs:
|
||||||
|
menu = fav['menu']
|
||||||
|
mid, nama, kategori, harga, stok, foto, tersedia, disc = menu
|
||||||
|
self.fav_tree.insert("", tk.END, values=(
|
||||||
|
nama,
|
||||||
|
kategori,
|
||||||
|
f"Rp {harga:,.0f}",
|
||||||
|
f"{fav['count']}x"
|
||||||
|
), tags=(str(mid),))
|
||||||
|
|
||||||
|
def quick_order_from_fav(self):
|
||||||
|
"""Pesan langsung dari menu favorit"""
|
||||||
|
sel = self.fav_tree.selection()
|
||||||
|
if not sel:
|
||||||
|
messagebox.showwarning("Pilih Menu", "Pilih menu favorit terlebih dahulu!")
|
||||||
|
return
|
||||||
|
|
||||||
|
item_vals = self.fav_tree.item(sel)['values']
|
||||||
|
if item_vals[0] == "Belum ada menu favorit":
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ambil menu_id dari tags
|
||||||
|
menu_id = int(self.fav_tree.item(sel)['tags'][0])
|
||||||
|
menu = menu_get(menu_id)
|
||||||
|
|
||||||
|
if not menu:
|
||||||
|
messagebox.showerror("Error", "Menu tidak ditemukan!")
|
||||||
|
return
|
||||||
|
|
||||||
|
mid, nama, kategori, harga, stok, foto, tersedia, disc = menu
|
||||||
|
|
||||||
|
if not tersedia or stok == 0:
|
||||||
|
messagebox.showwarning("Stok Habis", f"{nama} sedang tidak tersedia!")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
qty = int(self.fav_qty_var.get())
|
||||||
|
if qty <= 0:
|
||||||
|
raise ValueError()
|
||||||
|
except:
|
||||||
|
messagebox.showerror("Error", "Jumlah harus angka positif!")
|
||||||
|
return
|
||||||
|
|
||||||
|
if qty > stok:
|
||||||
|
messagebox.showerror("Stok Tidak Cukup", f"Stok hanya tersedia {stok}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Tanya nomor meja
|
||||||
|
nomor_meja = tk.simpledialog.askstring("Nomor Meja", "Masukkan nomor meja Anda:")
|
||||||
|
if not nomor_meja:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Buat transaksi langsung
|
||||||
|
cart_item = [{
|
||||||
|
'menu_id': mid,
|
||||||
|
'nama': nama,
|
||||||
|
'harga': harga,
|
||||||
|
'qty': qty,
|
||||||
|
'subtotal': harga * qty
|
||||||
|
}]
|
||||||
|
|
||||||
|
try:
|
||||||
|
trans_id = create_transaksi(self.session['id'], nomor_meja, cart_item)
|
||||||
|
add_to_favorites(self.session['id'], mid)
|
||||||
|
|
||||||
|
messagebox.showinfo("Berhasil",
|
||||||
|
f"✅ Pesanan Cepat Berhasil!\n\n"
|
||||||
|
f"{nama} x{qty}\n"
|
||||||
|
f"Total: Rp {harga * qty:,.0f}\n"
|
||||||
|
f"Meja: {nomor_meja}\n\n"
|
||||||
|
f"ID Transaksi: {trans_id}")
|
||||||
|
|
||||||
|
self.reload_favorites()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Error", f"Gagal membuat pesanan: {e}")
|
||||||
|
|
||||||
|
# ========== UPDATE DASHBOARD FRAME ==========
|
||||||
|
# GANTI fungsi dashboard_frame yang lama dengan ini:
|
||||||
|
|
||||||
|
def dashboard_frame(self):
|
||||||
|
for w in self.root.winfo_children():
|
||||||
|
w.destroy()
|
||||||
|
|
||||||
|
# Header
|
||||||
|
top = ttk.Frame(self.root)
|
||||||
|
top.pack(fill='x')
|
||||||
|
ttk.Label(top, text=f"👤 User: {self.session['username']} | Role: {self.session['role'].upper()}",
|
||||||
|
font=("Arial", 12, "bold")).pack(side='left', padx=15, pady=8)
|
||||||
|
ttk.Button(top, text="🚪 Logout", command=self.logout).pack(side='right', padx=15)
|
||||||
|
|
||||||
|
# Notebook tabs
|
||||||
|
main = ttk.Notebook(self.root)
|
||||||
|
main.pack(fill='both', expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
|
role = self.session['role']
|
||||||
|
|
||||||
|
# Setup tabs berdasarkan role
|
||||||
|
if role == 'pembeli':
|
||||||
|
self.tab_order = ttk.Frame(main)
|
||||||
|
self.tab_favorites = ttk.Frame(main)
|
||||||
|
main.add(self.tab_order, text="🛒 Pesan Menu")
|
||||||
|
main.add(self.tab_favorites, text="⭐ Menu Favorit")
|
||||||
|
self.build_order_tab(self.tab_order)
|
||||||
|
self.build_favorites_tab(self.tab_favorites)
|
||||||
|
|
||||||
|
elif role == 'waiter':
|
||||||
|
self.tab_waiter = ttk.Frame(main)
|
||||||
|
main.add(self.tab_waiter, text="📋 Dashboard Waiter")
|
||||||
|
self.build_waiter_tab(self.tab_waiter)
|
||||||
|
|
||||||
|
elif role == 'admin':
|
||||||
|
self.tab_menu_view = ttk.Frame(main)
|
||||||
|
self.tab_menu_manage = ttk.Frame(main)
|
||||||
|
self.tab_promo = ttk.Frame(main)
|
||||||
|
self.tab_waiter = ttk.Frame(main)
|
||||||
|
main.add(self.tab_menu_view, text="📖 Menu - View")
|
||||||
|
main.add(self.tab_menu_manage, text="⚙️ Menu - Manage")
|
||||||
|
main.add(self.tab_promo, text="🎁 Promo - Manage")
|
||||||
|
main.add(self.tab_waiter, text="📋 Dashboard Waiter")
|
||||||
|
self.build_menu_view_tab(self.tab_menu_view)
|
||||||
|
self.build_menu_manage_tab(self.tab_menu_manage)
|
||||||
|
self.build_promo_tab(self.tab_promo)
|
||||||
|
self.build_waiter_tab(self.tab_waiter)
|
||||||
|
|
||||||
|
else: # kasir, owner
|
||||||
|
self.tab_menu_view = ttk.Frame(main)
|
||||||
|
main.add(self.tab_menu_view, text="📖 Menu - View")
|
||||||
|
self.build_menu_view_tab(self.tab_menu_view)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user