From fb42d1243573897821777dc591df9279db527a21 Mon Sep 17 00:00:00 2001 From: Jevinca Marvella Date: Sat, 6 Dec 2025 21:50:05 +0700 Subject: [PATCH] Revisi Users Order System --- main.py | 708 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 467 insertions(+), 241 deletions(-) diff --git a/main.py b/main.py index b972cf9..ea2690c 100644 --- a/main.py +++ b/main.py @@ -9,7 +9,7 @@ import os import csv import tkinter as tk -from tkinter import ttk, messagebox, filedialog +from tkinter import ttk, messagebox, filedialog, simpledialog from PIL import Image, ImageTk from datetime import datetime @@ -22,19 +22,20 @@ FAVORITES_CSV = "favorites.csv" 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 + # Totoro Forest Theme + 'primary': '#5D8A66', # Forest Green (warna daun) + 'secondary': '#8B7355', # Wood Brown (warna pohon) + 'success': '#7BA05B', # Soft Green + 'danger': '#C1666B', # Soft Red + 'warning': '#E8B86D', # Warm Yellow + 'info': '#6B9AC4', # Sky Blue + 'light': '#F5F3E7', # Cream (kertas antik) + 'dark': '#3E4E3A', # Dark Forest + 'totoro_gray': '#9CA3AF', # Abu-abu Totoro + 'totoro_belly': '#E8DCC4', # Perut Totoro (cream) + 'leaf_green': '#A8D5A3', # Hijau muda daun + 'tree_brown': '#6B4423', # Cokelat tua kayu + 'soft_cream': '#FAF8F1', # Background lembut } @@ -385,17 +386,6 @@ def promo_get(code): - - - - - - - - - - - # 19 juta lapangan badmin # Buat logika diskon + promok @@ -596,13 +586,18 @@ def get_user_favorites(user_id, limit=5): # Wilayah dikuasai UI - - +def add_forest_decoration(parent_frame): + deco = tk.Label(parent_frame, text="πŸŒΏπŸƒ", + font=("Arial", 12), + bg=parent_frame['bg'] if 'bg' in parent_frame.keys() else COLORS['soft_cream'], + fg=COLORS['leaf_green']) + return deco class App: def __init__(self, root): self.root = root - self.root.title("🍡 Cafe Totoro Mania") + self.root.title("🌳 Cafe Totoro - Forest Coffee & Treats 🐾") + self.root.configure(bg=COLORS['tree_brown']) self.session = None self.img_cache = {} self.cart = [] @@ -613,24 +608,58 @@ class App: 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'])]) + # Totoro Theme Colors + style.configure('TFrame', background=COLORS['soft_cream']) + style.configure('TLabel', background=COLORS['soft_cream'], + foreground=COLORS['dark'], font=('Georgia', 10)) + style.configure('TNotebook', background=COLORS['light']) + style.configure('TNotebook.Tab', padding=[20, 10], + font=('Georgia', 10, 'bold')) - style.configure('Success.TButton', background=COLORS['success'], - foreground='white', font=('Arial', 10, 'bold')) + # Button styles - Totoro Theme + style.configure('Primary.TButton', + background=COLORS['primary'], + foreground='white', + font=('Georgia', 10, 'bold'), + borderwidth=0, + focuscolor='none') + style.map('Primary.TButton', + background=[('active', COLORS['leaf_green']), + ('pressed', COLORS['success'])]) - style.configure('Danger.TButton', background=COLORS['danger'], - foreground='white', font=('Arial', 10, 'bold')) + style.configure('Success.TButton', + background=COLORS['success'], + foreground='white', + font=('Georgia', 10, 'bold')) + style.map('Success.TButton', + background=[('active', COLORS['leaf_green'])]) - # 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'])]) + style.configure('Danger.TButton', + background=COLORS['danger'], + foreground='white', + font=('Georgia', 10, 'bold')) + style.configure('Accent.TButton', + background=COLORS['warning'], + foreground=COLORS['dark'], + font=('Georgia', 11, 'bold'), + padding=10) + + # Treeview style - Forest Theme + style.configure('Treeview', + background=COLORS['soft_cream'], + foreground=COLORS['dark'], + fieldbackground=COLORS['soft_cream'], + font=('Georgia', 9), + rowheight=25) + style.configure('Treeview.Heading', + background=COLORS['primary'], + foreground='white', + font=('Georgia', 10, 'bold'), + relief='flat') + style.map('Treeview', + background=[('selected', COLORS['leaf_green'])], + foreground=[('selected', 'white')]) def setup_ui(self): self.root.geometry("1000x650") self.root.resizable(False, False) @@ -639,41 +668,44 @@ class App: def login_frame(self): for w in self.root.winfo_children(): w.destroy() - + # Main container dengan background cafe - main_frame = tk.Frame(self.root, bg=COLORS['cafe_brown']) + main_frame = tk.Frame(self.root, bg=COLORS['tree_brown']) main_frame.pack(fill='both', expand=True) - # Center frame + # 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) + + # Logo/Header Section - Totoro Theme + header_frame = tk.Frame(center, bg=COLORS['primary'], height=140) 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) + # Totoro ASCII Art / Emoji + tk.Label(header_frame, text="🌳", font=("Arial", 28), + bg=COLORS['primary'], fg=COLORS['leaf_green']).pack(side='left', padx=10, pady=10) + + tk.Label(header_frame, text="γ…€πŸΎ", font=("Arial", 40), + bg=COLORS['primary'], fg=COLORS['totoro_gray']).pack(pady=5) + + tk.Label(header_frame, text="🌿", font=("Arial", 28), + bg=COLORS['primary'], fg=COLORS['leaf_green']).pack(side='right', padx=10, pady=10) + + tk.Label(header_frame, text="CAFE TOTORO", + font=("Georgia", 28, "bold"), bg=COLORS['primary'], + fg=COLORS['soft_cream']).pack() + + tk.Label(header_frame, text="✦ Welcome to the Forest Cafe ✦", + font=("Georgia", 11, "italic"), bg=COLORS['primary'], + fg=COLORS['totoro_belly']).pack(pady=5) # Form Section - form_frame = tk.Frame(center, bg='white', padx=40, pady=30) + form_frame = tk.Frame(center, bg=COLORS['soft_cream'], 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)) + tk.Label(form_frame, text="Mari masuk ke hutan ajaib kami", + font=("Georgia", 10, "italic"), bg=COLORS['soft_cream'], + fg=COLORS['totoro_gray']).pack(pady=(0,25)) # Username tk.Label(form_frame, text="πŸ‘€ Username", font=("Arial", 11, "bold"), @@ -681,34 +713,37 @@ class App: self.username_var = tk.StringVar() username_entry = tk.Entry(form_frame, textvariable=self.username_var, - font=("Arial", 12), relief='solid', bd=2) + font=("Arial", 12), relief='solid', bd=2, + bg='white', fg=COLORS['dark']) 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)) + tk.Label(form_frame, text="πŸ”’ Password", font=("Georgia", 11, "bold"), + bg=COLORS['soft_cream'], fg=COLORS['dark']).pack(anchor='w', pady=(15,5)) self.password_var = tk.StringVar() password_entry = tk.Entry(form_frame, textvariable=self.password_var, - show="●", font=("Arial", 12), relief='solid', bd=2) + show="●", font=("Arial", 12), relief='solid', bd=2, + bg='white', fg=COLORS['dark']) password_entry.pack(fill='x', ipady=8) # 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 = tk.Button(form_frame, text="🌿 MASUK KE HUTAN", command=self.handle_login, + font=("Georgia", 13, "bold"), bg=COLORS['primary'], + fg='white', relief='flat', cursor='hand2', bd=0, + activebackground=COLORS['success']) login_btn.pack(fill='x', pady=(25,10), ipady=10) # Hover effect - login_btn.bind("", lambda e: login_btn.config(bg=COLORS['success'])) - login_btn.bind("", lambda e: login_btn.config(bg=COLORS['cafe_green'])) + login_btn.bind("", lambda e: login_btn.config(bg=COLORS['leaf_green'])) + login_btn.bind("", lambda e: login_btn.config(bg=COLORS['primary'])) # Info text - info_frame = tk.Frame(center, bg=COLORS['light'], height=60) + info_frame = tk.Frame(center, bg=COLORS['totoro_belly'], 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'], + tk.Label(info_frame, text="πŸƒ Tip: Gunakan user/user123 untuk role pembeli", + font=("Georgia", 9), bg=COLORS['totoro_belly'], fg=COLORS['dark']).pack(pady=15) def handle_login(self): @@ -734,28 +769,79 @@ class App: def dashboard_frame(self): for w in self.root.winfo_children(): w.destroy() - top = ttk.Frame(self.root) + + # Header + top = tk.Frame(self.root, bg=COLORS['primary'], height=50) top.pack(fill='x') - ttk.Label(top, text=f"User: {self.session['username']} | Role: {self.session['role']}", - font=("Arial", 12)).pack(side='left', padx=10, pady=6) - ttk.Button(top, text="Logout", command=self.logout).pack(side='right', padx=10) + + welcome_text = f"🐾 Halo, {self.session['username']} β€’ {self.session['role'].upper()}" + tk.Label(top, text=welcome_text, + font=("Georgia", 12, "bold"), + bg=COLORS['primary'], + fg=COLORS['soft_cream']).pack(side='left', padx=20, pady=12) + + logout_btn = tk.Button(top, text="πŸŒ™ Logout", command=self.logout, + font=("Georgia", 10, "bold"), + bg=COLORS['secondary'], + fg='white', + relief='flat', + cursor='hand2', + padx=15, pady=5) + logout_btn.pack(side='right', padx=20, pady=8) + logout_btn.bind("", lambda e: logout_btn.config(bg=COLORS['danger'])) + logout_btn.bind("", lambda e: logout_btn.config(bg=COLORS['secondary'])) + + # Notebook tabs main = ttk.Notebook(self.root) - main.pack(fill='both', expand=True, padx=10, pady=8) - self.tab_menu_manage = ttk.Frame(main) - self.tab_menu_view = ttk.Frame(main) - self.tab_promo = ttk.Frame(main) - - main.add(self.tab_menu_view, text="Menu - View") - if self.session['role'] == 'admin': - main.add(self.tab_menu_manage, text="Menu - Manage") - main.add(self.tab_promo, text="Promo - Manage") - else: - pass - - self.build_menu_view_tab(self.tab_menu_view) - if self.session['role'] == 'admin': + 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) + + def build_order_tab(self, parent): + """Tab pemesanan untuk pembeli""" + print("πŸ”§ build_order_tab called") + + # Initialize cart dan qty variable PERTAMA KALI + if not hasattr(self, 'cart'): + self.cart = [] + print(" βœ“ Cart initialized") + + if not hasattr(self, 'order_qty_var'): + self.order_qty_var = tk.StringVar(value="1") + print(" βœ“ Quantity variable initialized") def build_menu_view_tab(self, parent): for w in parent.winfo_children(): @@ -1098,9 +1184,16 @@ class App: self.reload_promo_table() + # ========== TAB: ORDER SYSTEM (PEMBELI) ========== # ========== TAB: ORDER SYSTEM (PEMBELI) ========== def build_order_tab(self, parent): """Tab pemesanan untuk pembeli""" + # Initialize cart kalau belum ada + if not hasattr(self, 'cart'): + self.cart = [] + print("πŸ”§ Cart initialized") + + # Clear parent for w in parent.winfo_children(): w.destroy() @@ -1108,93 +1201,195 @@ class App: container = ttk.Frame(parent) container.pack(fill='both', expand=True, padx=10, pady=10) - # LEFT: Daftar Menu + # ========== LEFT SIDE: 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) + # Header + ttk.Label(left, text="🌿 Menu Hutan Totoro", + font=("Georgia", 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) + 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) + 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) + # ========== SECTION: TAMBAH KE KERANJANG ========== + add_frame = tk.Frame(left, bg=COLORS['soft_cream']) 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 + tk.Label(add_frame, text="Jumlah:", font=("Georgia", 10, "bold"), + bg=COLORS['soft_cream'], fg=COLORS['dark']).pack(side='left', padx=5) + + # Initialize qty variable + if not hasattr(self, 'order_qty_var'): + self.order_qty_var = tk.StringVar(value="1") + + # Frame untuk tombol +/- + qty_frame = tk.Frame(add_frame, bg=COLORS['soft_cream']) + qty_frame.pack(side='left', padx=5) + + # Tombol MINUS + btn_minus = tk.Button(qty_frame, text="βž–", width=3, + command=self.decrease_qty, + bg=COLORS['danger'], fg='white', + font=('Arial', 10, 'bold'), + relief='flat', cursor='hand2', + activebackground='#C1666B') + btn_minus.pack(side='left', padx=2) + + # Entry QUANTITY + qty_entry = tk.Entry(qty_frame, textvariable=self.order_qty_var, + width=6, justify='center', + font=('Georgia', 11, 'bold'), + bg='white', fg=COLORS['dark']) + qty_entry.pack(side='left', padx=3) + + # Tombol PLUS + btn_plus = tk.Button(qty_frame, text="βž•", width=3, + command=self.increase_qty, + bg=COLORS['success'], fg='white', + font=('Arial', 10, 'bold'), + relief='flat', cursor='hand2', + activebackground='#7BA05B') + btn_plus.pack(side='left', padx=2) + + # Tombol TAMBAH KE KERANJANG + btn_add = tk.Button(add_frame, text="πŸ›’ Tambah ke Keranjang", + command=self.add_to_cart, + bg=COLORS['primary'], fg='white', + font=('Georgia', 10, 'bold'), + relief='flat', cursor='hand2', + padx=15, pady=5, + activebackground=COLORS['leaf_green']) + btn_add.pack(side='left', padx=10) + + # ========== RIGHT SIDE: KERANJANG ========== right = ttk.Frame(container, relief='solid', borderwidth=1) right.pack(side='right', fill='both', padx=(5, 0)) - right.config(width=350) + right.config(width=380) - ttk.Label(right, text="πŸ›’ Keranjang Belanja", font=("Arial", 14, "bold")).pack(pady=8) + # Header keranjang + ttk.Label(right, text="🧺 Keranjang Pesanan", + font=("Georgia", 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) + 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 + w = 130 if c == "Menu" else 75 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 + ttk.Button(cart_btn_frm, text="πŸ—‘οΈ Hapus Item", + command=self.remove_from_cart, + style='Danger.TButton').pack(side='left', padx=3) + ttk.Button(cart_btn_frm, text="πŸ“ Update Qty", + command=self.update_cart_qty, + style='Primary.TButton').pack(side='left', padx=3) + + # Label 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) + tk.Label(right, textvariable=self.cart_total_var, + font=("Georgia", 13, "bold"), + bg=COLORS['soft_cream'], fg=COLORS['primary'], + pady=8).pack(pady=5, fill='x') # Form nomor meja - meja_frm = ttk.Frame(right) + meja_frm = tk.Frame(right, bg=COLORS['soft_cream'], pady=10) meja_frm.pack(fill='x', padx=10, pady=8) - ttk.Label(meja_frm, text="Nomor Meja:", font=("Arial", 10)).pack(side='left', padx=5) + + tk.Label(meja_frm, text="πŸ“ Nomor Meja:", + font=("Georgia", 10, "bold"), + bg=COLORS['soft_cream'], fg=COLORS['dark']).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) + tk.Entry(meja_frm, textvariable=self.nomor_meja_var, + width=10, font=('Arial', 11), justify='center').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') + # Tombol PESAN SEKARANG + btn_order = tk.Button(right, text="πŸ“‹ PESAN SEKARANG", + command=self.submit_order, + bg=COLORS['warning'], fg=COLORS['dark'], + font=('Georgia', 12, 'bold'), + relief='flat', cursor='hand2', + pady=12, + activebackground='#E8B86D') + btn_order.pack(pady=10, padx=10, fill='x') + # Debug info + print("πŸ”§ Order tab initialized successfully") + + # Load menu self.reload_order_menu() - + def reload_order_menu(self): """Reload daftar menu untuk order""" + if not hasattr(self, 'order_menu_tree'): + return + 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 "" + search = "" + if hasattr(self, 'order_search_var'): + search = self.order_search_var.get().strip() + 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)) - + 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("") + """Reset pencarian menu""" + if hasattr(self, 'order_search_var'): + self.order_search_var.set("") self.reload_order_menu() - + + def increase_qty(self): + """Tingkatkan jumlah item""" + try: + current = int(self.order_qty_var.get()) + self.order_qty_var.set(str(current + 1)) + except: + self.order_qty_var.set("1") + + def decrease_qty(self): + """Kurangi jumlah item""" + try: + current = int(self.order_qty_var.get()) + if current > 1: + self.order_qty_var.set(str(current - 1)) + except: + self.order_qty_var.set("1") + def add_to_cart(self): """Tambah item ke keranjang""" sel = self.order_menu_tree.selection() @@ -1213,20 +1408,40 @@ class App: 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) + harga_display = str(item_vals[3]) + + # Parse harga - hapus semua kecuali angka + harga_clean = "" + for char in harga_display: + if char.isdigit(): + harga_clean += char + + try: + harga = float(harga_clean) + except: + messagebox.showerror("Error", f"Format harga salah: {harga_display}") + return + stok = int(item_vals[4]) if qty > stok: messagebox.showerror("Stok Habis", f"Stok hanya tersedia {stok}") return + if not hasattr(self, 'cart'): + self.cart = [] + # 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'] + new_qty = cart_item['qty'] + qty + if new_qty > stok: + messagebox.showerror("Stok Habis", + f"Total pesanan ({new_qty}) melebihi stok ({stok})") + return + cart_item['qty'] = new_qty + cart_item['subtotal'] = cart_item['harga'] * new_qty found = True break @@ -1241,9 +1456,12 @@ class App: self.update_cart_display() messagebox.showinfo("Berhasil", f"βœ… {nama} x{qty} ditambahkan ke keranjang!") - + def update_cart_display(self): """Update tampilan keranjang""" + if not hasattr(self, 'cart_tree'): + return + for item in self.cart_tree.get_children(): self.cart_tree.delete(item) @@ -1257,8 +1475,9 @@ class App: )) total += item['subtotal'] - self.cart_total_var.set(f"Total: Rp {total:,.0f}") - + if hasattr(self, 'cart_total_var'): + self.cart_total_var.set(f"Total: Rp {total:,.0f}") + def remove_from_cart(self): """Hapus item dari keranjang""" sel = self.cart_tree.selection() @@ -1270,7 +1489,7 @@ class App: 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() @@ -1281,20 +1500,72 @@ class App: 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() - + dialog = tk.Toplevel(self.root) + dialog.title("Update Jumlah") + dialog.geometry("320x180") + dialog.transient(self.root) + dialog.grab_set() + dialog.configure(bg=COLORS['soft_cream']) + + tk.Label(dialog, text=f"🍽️ {item['nama']}", + font=("Georgia", 12, "bold"), + bg=COLORS['soft_cream'], fg=COLORS['dark']).pack(pady=15) + + qty_frame = tk.Frame(dialog, bg=COLORS['soft_cream']) + qty_frame.pack(pady=15) + + qty_var = tk.StringVar(value=str(item['qty'])) + + def decrease(): + try: + val = int(qty_var.get()) + if val > 1: + qty_var.set(str(val - 1)) + except: + pass + + def increase(): + try: + val = int(qty_var.get()) + qty_var.set(str(val + 1)) + except: + pass + + tk.Button(qty_frame, text="βž–", width=3, command=decrease, + bg=COLORS['danger'], fg='white', + font=('Arial', 11, 'bold'), relief='flat').pack(side='left', padx=5) + + tk.Entry(qty_frame, textvariable=qty_var, width=8, + justify='center', font=('Georgia', 12, 'bold')).pack(side='left', padx=5) + + tk.Button(qty_frame, text="βž•", width=3, command=increase, + bg=COLORS['success'], fg='white', + font=('Arial', 11, 'bold'), relief='flat').pack(side='left', padx=5) + + def save(): + try: + new_qty = int(qty_var.get()) + if new_qty > 0: + item['qty'] = new_qty + item['subtotal'] = item['harga'] * new_qty + self.update_cart_display() + dialog.destroy() + messagebox.showinfo("Berhasil", f"βœ… {item['nama']} diupdate!") + else: + messagebox.showerror("Error", "Jumlah harus lebih dari 0!") + except: + messagebox.showerror("Error", "Input salah!") + + tk.Button(dialog, text="βœ… Simpan", command=save, + bg=COLORS['primary'], fg='white', + font=('Georgia', 11, 'bold'), + relief='flat', padx=30, pady=8).pack(pady=15) + def submit_order(self): """Submit pesanan""" - if not self.cart: - messagebox.showwarning("Keranjang Kosong", "Tambahkan menu ke keranjang terlebih dahulu!") + if not self.cart or len(self.cart) == 0: + messagebox.showwarning("Keranjang Kosong", + "Tambahkan menu ke keranjang terlebih dahulu!") return nomor_meja = self.nomor_meja_var.get().strip() @@ -1302,22 +1573,21 @@ class App: messagebox.showwarning("Nomor Meja", "Masukkan nomor meja!") return + total = sum(i['subtotal'] for i in self.cart) + 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!") + f"βœ… Pesanan berhasil dibuat!\n\n" + f"πŸ†” ID Transaksi: {trans_id}\n" + f"πŸ“ Nomor Meja: {nomor_meja}\n" + f"πŸ’° Total: Rp {total:,.0f}\n\n" + f"⏳ Status: Menunggu") - # Reset self.cart = [] self.nomor_meja_var.set("") self.order_qty_var.set("1") @@ -1325,7 +1595,7 @@ class App: self.reload_order_menu() except Exception as e: - messagebox.showerror("Error", f"Gagal membuat pesanan: {e}") + messagebox.showerror("Error", f"Gagal membuat pesanan:\n{str(e)}") # ========== TAB: WAITER DASHBOARD ========== def build_waiter_tab(self, parent): @@ -1333,8 +1603,8 @@ class App: for w in parent.winfo_children(): w.destroy() - ttk.Label(parent, text="πŸ“‹ Dashboard Waiter - Kelola Pesanan", - font=("Arial", 16, "bold")).pack(pady=10) + ttk.Label(parent, text="🐾 Totoro's Kitchen - Kelola Pesanan", + font=("Georgia", 16, "bold")).pack(pady=10) # Filter status filter_frm = ttk.Frame(parent) @@ -1450,11 +1720,11 @@ class App: 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) + ttk.Label(parent, text="⭐ Menu Favorit Totoro", + font=("Georgia", 16, "bold")).pack(pady=10) + + ttk.Label(parent, text="πŸƒ Menu yang paling sering Anda pesan dari hutan kami", + font=("Georgia", 10, "italic")).pack(pady=5) # Treeview favorites cols = ("Nama Menu", "Kategori", "Harga", "Sering Dipesan") @@ -1482,25 +1752,33 @@ class App: """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),)) + try: + favs = get_user_favorites(self.session['id'], limit=10) + + if not favs: + # Tampilkan pesan kosong + item_id = self.fav_tree.insert("", tk.END, values=( + "πŸƒ Belum ada menu favorit. Pesan sekarang!", "-", "-", "0" + )) + self.fav_tree.item(item_id, tags=('empty',)) + return + + for fav in favs: + menu = fav['menu'] + mid, nama, kategori, harga, stok, foto, tersedia, disc = menu + self.fav_tree.insert("", tk.END, values=( + f"⭐ {nama}", + kategori, + f"Rp {harga:,.0f}", + f"πŸ”₯ {fav['count']}x" + ), tags=(str(mid),)) + except Exception as e: + print(f"Error reload favorites: {e}") + self.fav_tree.insert("", tk.END, values=( + "❌ Error memuat favorit", "-", "-", "0" + )) + def quick_order_from_fav(self): """Pesan langsung dari menu favorit""" sel = self.fav_tree.selection() @@ -1508,10 +1786,14 @@ class App: 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": + # Cek jika kosong + tags = self.fav_tree.item(sel)['tags'] + if 'empty' in tags: + messagebox.showinfo("Info", "Belum ada menu favorit. Silakan pesan menu terlebih dahulu!") return + item_vals = self.fav_tree.item(sel)['values'] + # Ambil menu_id dari tags menu_id = int(self.fav_tree.item(sel)['tags'][0]) menu = menu_get(menu_id) @@ -1568,62 +1850,6 @@ class App: 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) - - - - # Done