Revisi Users Order System

This commit is contained in:
Jevinca Marvella 2025-12-06 21:50:05 +07:00
parent f616da6fa0
commit fb42d12435

668
main.py
View File

@ -9,7 +9,7 @@
import os import os
import csv import csv
import tkinter as tk import tkinter as tk
from tkinter import ttk, messagebox, filedialog from tkinter import ttk, messagebox, filedialog, simpledialog
from PIL import Image, ImageTk from PIL import Image, ImageTk
from datetime import datetime from datetime import datetime
@ -22,19 +22,20 @@ FAVORITES_CSV = "favorites.csv"
IMG_PREVIEW_SIZE = (120, 80) IMG_PREVIEW_SIZE = (120, 80)
COLORS = { COLORS = {
'primary': '#2C3E50', # Dark Blue # Totoro Forest Theme
'secondary': '#E67E22', # Orange 'primary': '#5D8A66', # Forest Green (warna daun)
'success': '#27AE60', # Green 'secondary': '#8B7355', # Wood Brown (warna pohon)
'danger': '#E74C3C', # Red 'success': '#7BA05B', # Soft Green
'warning': '#F39C12', # Yellow 'danger': '#C1666B', # Soft Red
'info': '#3498DB', # Light Blue 'warning': '#E8B86D', # Warm Yellow
'light': '#ECF0F1', # Light Gray 'info': '#6B9AC4', # Sky Blue
'dark': '#34495E', # Dark Gray 'light': '#F5F3E7', # Cream (kertas antik)
'bg_gradient_start': '#667eea', # Purple 'dark': '#3E4E3A', # Dark Forest
'bg_gradient_end': '#764ba2', # Dark Purple 'totoro_gray': '#9CA3AF', # Abu-abu Totoro
'cafe_brown': '#8B4513', # Saddle Brown 'totoro_belly': '#E8DCC4', # Perut Totoro (cream)
'cafe_cream': '#F5DEB3', # Wheat 'leaf_green': '#A8D5A3', # Hijau muda daun
'cafe_green': '#228B22', # Forest Green 'tree_brown': '#6B4423', # Cokelat tua kayu
'soft_cream': '#FAF8F1', # Background lembut
} }
@ -385,17 +386,6 @@ def promo_get(code):
# 19 juta lapangan badmin # 19 juta lapangan badmin
# Buat logika diskon + promok # Buat logika diskon + promok
@ -596,13 +586,18 @@ def get_user_favorites(user_id, limit=5):
# Wilayah dikuasai UI # 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: 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 - Forest Coffee & Treats 🐾")
self.root.configure(bg=COLORS['tree_brown'])
self.session = None self.session = None
self.img_cache = {} self.img_cache = {}
self.cart = [] self.cart = []
@ -613,24 +608,58 @@ class App:
style = ttk.Style() style = ttk.Style()
style.theme_use('clam') style.theme_use('clam')
# Button styles # Totoro Theme Colors
style.configure('Primary.TButton', background=COLORS['secondary'], style.configure('TFrame', background=COLORS['soft_cream'])
foreground='white', font=('Arial', 10, 'bold')) style.configure('TLabel', background=COLORS['soft_cream'],
style.map('Primary.TButton', background=[('active', COLORS['warning'])]) 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'], # Button styles - Totoro Theme
foreground='white', font=('Arial', 10, 'bold')) 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'], style.configure('Success.TButton',
foreground='white', font=('Arial', 10, 'bold')) background=COLORS['success'],
foreground='white',
font=('Georgia', 10, 'bold'))
style.map('Success.TButton',
background=[('active', COLORS['leaf_green'])])
# Treeview style style.configure('Danger.TButton',
style.configure('Treeview', background=COLORS['light'], background=COLORS['danger'],
foreground=COLORS['dark'], font=('Arial', 9)) foreground='white',
style.configure('Treeview.Heading', background=COLORS['primary'], font=('Georgia', 10, 'bold'))
foreground='white', font=('Arial', 10, 'bold'))
style.map('Treeview', background=[('selected', COLORS['info'])])
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): def setup_ui(self):
self.root.geometry("1000x650") self.root.geometry("1000x650")
self.root.resizable(False, False) self.root.resizable(False, False)
@ -641,39 +670,42 @@ class App:
w.destroy() w.destroy()
# Main container dengan background cafe # 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) main_frame.pack(fill='both', expand=True)
# Center frame # Center frame
center = tk.Frame(main_frame, bg='white', relief='raised', bd=3) 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) 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') header_frame.pack(fill='x')
# 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", 48), tk.Label(header_frame, text="ㅤ🐾", font=("Arial", 40),
bg=COLORS['cafe_green'], fg='white').pack(pady=5) bg=COLORS['primary'], fg=COLORS['totoro_gray']).pack(pady=5)
tk.Label(header_frame, text="CAFE TOTORO MANIA", tk.Label(header_frame, text="🌿", font=("Arial", 28),
font=("Arial", 24, "bold"), bg=COLORS['cafe_green'], bg=COLORS['primary'], fg=COLORS['leaf_green']).pack(side='right', padx=10, pady=10)
fg='white').pack()
tk.Label(header_frame, text="~ Your Cozy Coffee Corner ~", tk.Label(header_frame, text="CAFE TOTORO",
font=("Arial", 11, "italic"), bg=COLORS['cafe_green'], font=("Georgia", 28, "bold"), bg=COLORS['primary'],
fg=COLORS['cafe_cream']).pack(pady=2) 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 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) form_frame.pack(fill='both', expand=True)
tk.Label(form_frame, text="Selamat Datang! 👋", tk.Label(form_frame, text="Mari masuk ke hutan ajaib kami",
font=("Arial", 18, "bold"), bg='white', font=("Georgia", 10, "italic"), bg=COLORS['soft_cream'],
fg=COLORS['cafe_brown']).pack(pady=(0,10)) fg=COLORS['totoro_gray']).pack(pady=(0,25))
tk.Label(form_frame, text="Silakan login untuk melanjutkan",
font=("Arial", 10), bg='white',
fg=COLORS['dark']).pack(pady=(0,25))
# Username # Username
tk.Label(form_frame, text="👤 Username", font=("Arial", 11, "bold"), tk.Label(form_frame, text="👤 Username", font=("Arial", 11, "bold"),
@ -681,34 +713,37 @@ class App:
self.username_var = tk.StringVar() self.username_var = tk.StringVar()
username_entry = tk.Entry(form_frame, textvariable=self.username_var, 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) username_entry.pack(fill='x', ipady=8)
# Password # Password
tk.Label(form_frame, text="🔒 Password", font=("Arial", 11, "bold"), tk.Label(form_frame, text="🔒 Password", font=("Georgia", 11, "bold"),
bg='white', fg=COLORS['dark']).pack(anchor='w', pady=(15,5)) bg=COLORS['soft_cream'], fg=COLORS['dark']).pack(anchor='w', pady=(15,5))
self.password_var = tk.StringVar() self.password_var = tk.StringVar()
password_entry = tk.Entry(form_frame, textvariable=self.password_var, 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) password_entry.pack(fill='x', ipady=8)
# Login Button # Login Button
login_btn = tk.Button(form_frame, text="🔐 LOGIN", command=self.handle_login, login_btn = tk.Button(form_frame, text="🌿 MASUK KE HUTAN", command=self.handle_login,
font=("Arial", 13, "bold"), bg=COLORS['cafe_green'], font=("Georgia", 13, "bold"), bg=COLORS['primary'],
fg='white', relief='flat', cursor='hand2', bd=0) fg='white', relief='flat', cursor='hand2', bd=0,
activebackground=COLORS['success'])
login_btn.pack(fill='x', pady=(25,10), ipady=10) login_btn.pack(fill='x', pady=(25,10), ipady=10)
# Hover effect # Hover effect
login_btn.bind("<Enter>", lambda e: login_btn.config(bg=COLORS['success'])) login_btn.bind("<Enter>", lambda e: login_btn.config(bg=COLORS['leaf_green']))
login_btn.bind("<Leave>", lambda e: login_btn.config(bg=COLORS['cafe_green'])) login_btn.bind("<Leave>", lambda e: login_btn.config(bg=COLORS['primary']))
# Info text # 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') info_frame.pack(fill='x', side='bottom')
tk.Label(info_frame, text="💡 Tip: Gunakan user/user123 untuk pembeli", tk.Label(info_frame, text="🍃 Tip: Gunakan user/user123 untuk role pembeli",
font=("Arial", 9), bg=COLORS['light'], font=("Georgia", 9), bg=COLORS['totoro_belly'],
fg=COLORS['dark']).pack(pady=15) fg=COLORS['dark']).pack(pady=15)
def handle_login(self): def handle_login(self):
@ -734,28 +769,79 @@ class App:
def dashboard_frame(self): def dashboard_frame(self):
for w in self.root.winfo_children(): for w in self.root.winfo_children():
w.destroy() w.destroy()
top = ttk.Frame(self.root)
# Header
top = tk.Frame(self.root, bg=COLORS['primary'], height=50)
top.pack(fill='x') 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) welcome_text = f"🐾 Halo, {self.session['username']}{self.session['role'].upper()}"
ttk.Button(top, text="Logout", command=self.logout).pack(side='right', padx=10) 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("<Enter>", lambda e: logout_btn.config(bg=COLORS['danger']))
logout_btn.bind("<Leave>", lambda e: logout_btn.config(bg=COLORS['secondary']))
# Notebook tabs
main = ttk.Notebook(self.root) main = ttk.Notebook(self.root)
main.pack(fill='both', expand=True, padx=10, pady=8) main.pack(fill='both', expand=True, padx=10, pady=10)
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") role = self.session['role']
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) # Setup tabs berdasarkan role
if self.session['role'] == 'admin': 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_menu_manage_tab(self.tab_menu_manage)
self.build_promo_tab(self.tab_promo) 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): def build_menu_view_tab(self, parent):
for w in parent.winfo_children(): for w in parent.winfo_children():
@ -1098,9 +1184,16 @@ class App:
self.reload_promo_table() self.reload_promo_table()
# ========== TAB: ORDER SYSTEM (PEMBELI) ==========
# ========== TAB: ORDER SYSTEM (PEMBELI) ========== # ========== TAB: ORDER SYSTEM (PEMBELI) ==========
def build_order_tab(self, parent): def build_order_tab(self, parent):
"""Tab pemesanan untuk pembeli""" """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(): for w in parent.winfo_children():
w.destroy() w.destroy()
@ -1108,93 +1201,195 @@ class App:
container = ttk.Frame(parent) container = ttk.Frame(parent)
container.pack(fill='both', expand=True, padx=10, pady=10) 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 = ttk.Frame(container, relief='solid', borderwidth=1)
left.pack(side='left', fill='both', expand=True, padx=(0, 5)) 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 pencarian
filter_frm = ttk.Frame(left) filter_frm = ttk.Frame(left)
filter_frm.pack(fill='x', padx=10, pady=5) filter_frm.pack(fill='x', padx=10, pady=5)
ttk.Label(filter_frm, text="Cari Menu:").pack(side='left', padx=5) ttk.Label(filter_frm, text="Cari Menu:").pack(side='left', padx=5)
self.order_search_var = tk.StringVar() self.order_search_var = tk.StringVar()
ttk.Entry(filter_frm, textvariable=self.order_search_var, width=20).pack(side='left', padx=5) ttk.Entry(filter_frm, textvariable=self.order_search_var,
ttk.Button(filter_frm, text="🔍 Cari", command=self.reload_order_menu).pack(side='left', padx=3) width=20).pack(side='left', padx=5)
ttk.Button(filter_frm, text="🔄 Reset", command=self.reset_order_search).pack(side='left', padx=3) 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 # Treeview menu
cols = ("ID", "Nama", "Kategori", "Harga", "Stok") 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: for c in cols:
self.order_menu_tree.heading(c, text=c) self.order_menu_tree.heading(c, text=c)
w = 50 if c == "ID" else (180 if c == "Nama" else 100) w = 50 if c == "ID" else (180 if c == "Nama" else 100)
self.order_menu_tree.column(c, width=w) self.order_menu_tree.column(c, width=w)
self.order_menu_tree.pack(fill='both', expand=True, padx=10, pady=5) self.order_menu_tree.pack(fill='both', expand=True, padx=10, pady=5)
# Tombol tambah ke keranjang # ========== SECTION: TAMBAH KE KERANJANG ==========
add_frame = ttk.Frame(left) add_frame = tk.Frame(left, bg=COLORS['soft_cream'])
add_frame.pack(fill='x', padx=10, pady=8) 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 = ttk.Frame(container, relief='solid', borderwidth=1)
right.pack(side='right', fill='both', padx=(5, 0)) 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 # Treeview keranjang
cart_cols = ("Menu", "Harga", "Qty", "Subtotal") 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: for c in cart_cols:
self.cart_tree.heading(c, text=c) 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.column(c, width=w)
self.cart_tree.pack(fill='both', padx=10, pady=5) self.cart_tree.pack(fill='both', padx=10, pady=5)
# Tombol update & hapus # Tombol update & hapus
cart_btn_frm = ttk.Frame(right) cart_btn_frm = ttk.Frame(right)
cart_btn_frm.pack(fill='x', padx=10, pady=5) 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") 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 # 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) 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() 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 # Tombol PESAN SEKARANG
ttk.Button(right, text="📋 PESAN SEKARANG", command=self.submit_order, btn_order = tk.Button(right, text="📋 PESAN SEKARANG",
style="Accent.TButton").pack(pady=10, padx=10, fill='x') 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() self.reload_order_menu()
def reload_order_menu(self): def reload_order_menu(self):
"""Reload daftar menu untuk order""" """Reload daftar menu untuk order"""
if not hasattr(self, 'order_menu_tree'):
return
for item in self.order_menu_tree.get_children(): for item in self.order_menu_tree.get_children():
self.order_menu_tree.delete(item) 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) menus = menu_list(available_only=True, search_text=search or None)
for m in menus: for m in menus:
mid, nama, kategori, harga, stok, foto, tersedia, disc = m 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): 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() 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): def add_to_cart(self):
"""Tambah item ke keranjang""" """Tambah item ke keranjang"""
sel = self.order_menu_tree.selection() sel = self.order_menu_tree.selection()
@ -1213,20 +1408,40 @@ class App:
item_vals = self.order_menu_tree.item(sel)['values'] item_vals = self.order_menu_tree.item(sel)['values']
menu_id = item_vals[0] menu_id = item_vals[0]
nama = item_vals[1] nama = item_vals[1]
harga_str = item_vals[3].replace("Rp ", "").replace(",", "") harga_display = str(item_vals[3])
harga = float(harga_str)
# 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]) stok = int(item_vals[4])
if qty > stok: if qty > stok:
messagebox.showerror("Stok Habis", f"Stok hanya tersedia {stok}") messagebox.showerror("Stok Habis", f"Stok hanya tersedia {stok}")
return return
if not hasattr(self, 'cart'):
self.cart = []
# Cek apakah sudah ada di cart # Cek apakah sudah ada di cart
found = False found = False
for cart_item in self.cart: for cart_item in self.cart:
if cart_item['menu_id'] == menu_id: if cart_item['menu_id'] == menu_id:
cart_item['qty'] += qty new_qty = cart_item['qty'] + qty
cart_item['subtotal'] = cart_item['harga'] * cart_item['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 found = True
break break
@ -1244,6 +1459,9 @@ class App:
def update_cart_display(self): def update_cart_display(self):
"""Update tampilan keranjang""" """Update tampilan keranjang"""
if not hasattr(self, 'cart_tree'):
return
for item in self.cart_tree.get_children(): for item in self.cart_tree.get_children():
self.cart_tree.delete(item) self.cart_tree.delete(item)
@ -1257,7 +1475,8 @@ class App:
)) ))
total += item['subtotal'] 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): def remove_from_cart(self):
"""Hapus item dari keranjang""" """Hapus item dari keranjang"""
@ -1281,20 +1500,72 @@ class App:
idx = self.cart_tree.index(sel) idx = self.cart_tree.index(sel)
item = self.cart[idx] item = self.cart[idx]
# Dialog input qty baru dialog = tk.Toplevel(self.root)
new_qty = tk.simpledialog.askinteger("Update Jumlah", dialog.title("Update Jumlah")
f"Jumlah baru untuk {item['nama']}:", dialog.geometry("320x180")
initialvalue=item['qty'], dialog.transient(self.root)
minvalue=1) dialog.grab_set()
if new_qty: dialog.configure(bg=COLORS['soft_cream'])
item['qty'] = new_qty
item['subtotal'] = item['harga'] * new_qty tk.Label(dialog, text=f"🍽️ {item['nama']}",
self.update_cart_display() 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): def submit_order(self):
"""Submit pesanan""" """Submit pesanan"""
if not self.cart: if not self.cart or len(self.cart) == 0:
messagebox.showwarning("Keranjang Kosong", "Tambahkan menu ke keranjang terlebih dahulu!") messagebox.showwarning("Keranjang Kosong",
"Tambahkan menu ke keranjang terlebih dahulu!")
return return
nomor_meja = self.nomor_meja_var.get().strip() nomor_meja = self.nomor_meja_var.get().strip()
@ -1302,22 +1573,21 @@ class App:
messagebox.showwarning("Nomor Meja", "Masukkan nomor meja!") messagebox.showwarning("Nomor Meja", "Masukkan nomor meja!")
return return
total = sum(i['subtotal'] for i in self.cart)
try: try:
trans_id = create_transaksi(self.session['id'], nomor_meja, self.cart) trans_id = create_transaksi(self.session['id'], nomor_meja, self.cart)
# Tambah ke favorites
for item in self.cart: for item in self.cart:
add_to_favorites(self.session['id'], item['menu_id']) add_to_favorites(self.session['id'], item['menu_id'])
messagebox.showinfo("Berhasil", messagebox.showinfo("Berhasil",
f"✅ Pesanan berhasil dibuat!\n\n" f"✅ Pesanan berhasil dibuat!\n\n"
f"ID Transaksi: {trans_id}\n" f"🆔 ID Transaksi: {trans_id}\n"
f"Nomor Meja: {nomor_meja}\n" f"📍 Nomor Meja: {nomor_meja}\n"
f"Total: Rp {sum(i['subtotal'] for i in self.cart):,.0f}\n\n" f"💰 Total: Rp {total:,.0f}\n\n"
f"Status: Menunggu\n" f"⏳ Status: Menunggu")
f"Pesanan Anda akan segera diproses oleh waiter!")
# Reset
self.cart = [] self.cart = []
self.nomor_meja_var.set("") self.nomor_meja_var.set("")
self.order_qty_var.set("1") self.order_qty_var.set("1")
@ -1325,7 +1595,7 @@ class App:
self.reload_order_menu() self.reload_order_menu()
except Exception as e: 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 ========== # ========== TAB: WAITER DASHBOARD ==========
def build_waiter_tab(self, parent): def build_waiter_tab(self, parent):
@ -1333,8 +1603,8 @@ class App:
for w in parent.winfo_children(): for w in parent.winfo_children():
w.destroy() w.destroy()
ttk.Label(parent, text="📋 Dashboard Waiter - Kelola Pesanan", ttk.Label(parent, text="🐾 Totoro's Kitchen - Kelola Pesanan",
font=("Arial", 16, "bold")).pack(pady=10) font=("Georgia", 16, "bold")).pack(pady=10)
# Filter status # Filter status
filter_frm = ttk.Frame(parent) filter_frm = ttk.Frame(parent)
@ -1450,11 +1720,11 @@ class App:
for w in parent.winfo_children(): for w in parent.winfo_children():
w.destroy() w.destroy()
ttk.Label(parent, text="⭐ Menu Favorit Saya", ttk.Label(parent, text="⭐ Menu Favorit Totoro",
font=("Arial", 16, "bold")).pack(pady=10) font=("Georgia", 16, "bold")).pack(pady=10)
ttk.Label(parent, text="Menu yang paling sering Anda pesan", ttk.Label(parent, text="🍃 Menu yang paling sering Anda pesan dari hutan kami",
font=("Arial", 10)).pack(pady=5) font=("Georgia", 10, "italic")).pack(pady=5)
# Treeview favorites # Treeview favorites
cols = ("Nama Menu", "Kategori", "Harga", "Sering Dipesan") cols = ("Nama Menu", "Kategori", "Harga", "Sering Dipesan")
@ -1483,23 +1753,31 @@ class App:
for item in self.fav_tree.get_children(): for item in self.fav_tree.get_children():
self.fav_tree.delete(item) self.fav_tree.delete(item)
favs = get_user_favorites(self.session['id'], limit=10) try:
favs = get_user_favorites(self.session['id'], limit=10)
if not favs: if not favs:
self.fav_tree.insert("", tk.END, values=( # Tampilkan pesan kosong
"Belum ada menu favorit", "-", "-", "0" item_id = self.fav_tree.insert("", tk.END, values=(
"🍃 Belum ada menu favorit. Pesan sekarang!", "-", "-", "0"
)) ))
return self.fav_tree.item(item_id, tags=('empty',))
return
for fav in favs: for fav in favs:
menu = fav['menu'] menu = fav['menu']
mid, nama, kategori, harga, stok, foto, tersedia, disc = 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=( self.fav_tree.insert("", tk.END, values=(
nama, "❌ Error memuat favorit", "-", "-", "0"
kategori, ))
f"Rp {harga:,.0f}",
f"{fav['count']}x"
), tags=(str(mid),))
def quick_order_from_fav(self): def quick_order_from_fav(self):
"""Pesan langsung dari menu favorit""" """Pesan langsung dari menu favorit"""
@ -1508,10 +1786,14 @@ class App:
messagebox.showwarning("Pilih Menu", "Pilih menu favorit terlebih dahulu!") messagebox.showwarning("Pilih Menu", "Pilih menu favorit terlebih dahulu!")
return return
item_vals = self.fav_tree.item(sel)['values'] # Cek jika kosong
if item_vals[0] == "Belum ada menu favorit": tags = self.fav_tree.item(sel)['tags']
if 'empty' in tags:
messagebox.showinfo("Info", "Belum ada menu favorit. Silakan pesan menu terlebih dahulu!")
return return
item_vals = self.fav_tree.item(sel)['values']
# Ambil menu_id dari tags # Ambil menu_id dari tags
menu_id = int(self.fav_tree.item(sel)['tags'][0]) menu_id = int(self.fav_tree.item(sel)['tags'][0])
menu = menu_get(menu_id) menu = menu_get(menu_id)
@ -1568,62 +1850,6 @@ class App:
except Exception as e: except Exception as e:
messagebox.showerror("Error", f"Gagal membuat pesanan: {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 # Done