@@ -2185,17 +2185,25 @@ class App:
ttk . Button ( search_frame , text = " Cari " , command = self . reload_order_menu_cards ) . pack ( side = ' left ' , padx = 3 )
ttk . Button ( search_frame , text = " Reset " , command = self . reset_order_search ) . pack ( side = ' left ' , padx = 3 )
# Scrollable frame untuk cards (DENGAN BACKGROUND PUTIH )
canvas = tk . Canvas ( left , bg = ' #f5f5f5 ' , height = 450 , highlightthickness = 0 )
# Scrollable frame untuk cards (FULL WIDTH FIX )
canvas = tk . Canvas ( left , bg = ' #f5f5f5 ' , highlightthickness = 0 )
scrollbar = ttk . Scrollbar ( left , orient = " vertical " , command = canvas . yview )
self . menu_cards_frame = ttk . Frame ( canvas )
self . menu_cards_frame . bind (
" <Configure> " ,
lambda e : canvas . configure ( scrollregion = canvas . bbox ( " all " ) )
)
# 1. Simpan ID Window saat create_window
frame_window_id = canvas . create_window ( ( 0 , 0 ) , window = self . menu_cards_frame , anchor = " nw " )
# 2. Fungsi agar scroll region update saat konten bertambah
def configure_scroll_region ( event ) :
canvas . configure ( scrollregion = canvas . bbox ( " all " ) )
self . menu_cards_frame . bind ( " <Configure> " , configure_scroll_region )
# 3. FUNGSI KUNCI: Paksa frame selebar canvas (Full Width)
def configure_frame_width ( event ) :
canvas . itemconfig ( frame_window_id , width = event . width )
canvas . bind ( " <Configure> " , configure_frame_width )
canvas . create_window ( ( 0 , 0 ) , window = self . menu_cards_frame , anchor = " nw " )
canvas . configure ( yscrollcommand = scrollbar . set )
canvas . pack ( side = " left " , fill = " both " , expand = True )
@@ -2286,23 +2294,23 @@ class App:
def reload_order_menu_cards ( self ) :
""" Load menu dalam bentuk cards dengan gambar + tombol +/- """
""" Load menu dalam bentuk cards 4 kolom FULL WIDTH """
# Clear existing cards
for widget in self . menu_cards_frame . winfo_children ( ) :
widget . destroy ( )
# Get menu data
search = self . order_search_var . get ( ) . strip ( ) or None
results = menu_list ( search_text = search , available_only = True )
# Buat dict untuk qty di cart
cart_dict = { }
for cart_item in self . cart_items :
cart_dict [ cart_item [ ' menu_id ' ] ] = cart_item [ ' qty ' ]
cart_dict = { item [ ' menu_id ' ] : item [ ' qty ' ] for item in self . cart_items }
# Configure grid columns (2 kolom sama lebar)
self . menu_cards_frame . columnconfigure ( 0 , weight = 1 )
self . menu_cards_frame . columnconfigure ( 1 , weight = 1 )
# === KONFIGURASI 4 KOLOM AGAR RATA (UNIFORM) ===
total_columns = 4
# 'uniform' membuat lebar setiap kolom identik/sama persis
for i in range ( total_columns ) :
self . menu_cards_frame . columnconfigure ( i , weight = 1 , uniform = ' menu_col ' )
# ===============================================
row = 0
col = 0
@@ -2310,153 +2318,73 @@ class App:
for menu_data in results :
mid , nama , kategori , harga , stok , foto , tersedia , item_disc = menu_data
# Buat Card (HAPUS width/height fix agar responsif)
card = tk . Frame (
self . menu_cards_frame ,
relief = ' solid ' ,
borderwidth = 1 ,
bg = ' white ' ,
padx = 10 ,
pady = 10 ,
width = 200 ,
height = 320
padx = 5 , pady = 5
)
card . grid_propagate ( False )
card . grid ( row = row , column = col , padx = 8 , pady = 8 , sticky = ' nsew ' )
# Gambar
# sticky='nsew' MEMAKSA card memenuhi kotak grid-nya
card . grid ( row = row , column = col , padx = 5 , pady = 5 , sticky = ' nsew ' )
# --- Bagian Gambar ---
if foto and os . path . exists ( foto ) :
try :
img = Image . open ( foto )
img = img . resize ( ( 150 , 100 ) , Image . Resampling . LANCZOS )
# Resize agak kecil agar muat 4 kolom
img = img . resize ( ( 120 , 85 ) , Image . Resampling . LANCZOS )
photo = ImageTk . PhotoImage ( img )
img_label = tk . Label ( card , image = photo , bg = ' white ' )
img_label . image = photo
img_label . pack ( pady = ( 5 , 5 ) )
lbl = tk . Label ( card , image = photo , bg = ' white ' )
lbl . image = photo
lbl . pack ( pady = 5 )
except :
tk . Label (
card ,
text = " [No Image] " ,
bg = ' #e0e0e0 ' ,
width = 20 ,
height = 6
) . pack ( )
tk . Label ( card , text = " [No Image] " , bg = ' #eee ' , height = 4 ) . pack ( fill = ' x ' )
else :
tk . Label (
card ,
text = " [No Image] " ,
bg = ' #e0e0e0 ' ,
width = 18 ,
height = 5
) . pack ( )
tk . Label ( card , text = " [No Image] " , bg = ' #eee ' , height = 4 ) . pack ( fill = ' x ' )
# Nama menu
tk . Label (
card ,
text = nama ,
font = ( " Arial " , 10 , " bold " ) ,
bg = ' white ' ,
wraplength = 140 ,
justify = ' center ' ,
height = 2
) . pack ( pady = ( 3 , 2 ) )
# --- Bagian Info ---
tk . Label ( card , text = nama , font = ( " Arial " , 10 , " bold " ) , bg = ' white ' , wraplength = 110 ) . pack ( )
tk . Label ( card , text = kategori , font = ( " Arial " , 8 ) , fg = ' gray ' , bg = ' white ' ) . pack ( )
# Kategori
tk . Label (
card ,
text = kategori ,
font = ( " Arial " , 8 ) ,
fg = ' gray ' ,
bg = ' white '
) . pack ( pady = 1 )
# Harga
harga_text = f " Rp { harga : ,.0f } "
txt_harga = f " Rp { harga : ,.0f } "
if item_disc > 0 :
harga_text + = f " \n (- { item_disc } %) "
tk . Label (
card ,
text = harga_text ,
font = ( " Arial " , 9 , " bold " ) ,
fg = ' #4CAF50 ' ,
bg = ' white ' ,
justify = ' center '
) . pack ( pady = 2 )
txt_harga + = f " \n (- { item_disc } %) "
fg = ' #4CAF50 '
else :
tk . Label (
card ,
text = harga_text ,
font = ( " Arial " , 9 , " bold " ) ,
fg = ' green ' ,
bg = ' white '
) . pack ( pady = 2 )
fg = ' green '
tk . Label ( card , text = txt_harga , font = ( " Arial " , 9 , " bold " ) , fg = fg , bg = ' white ' ) . pack ( pady = 2 )
# Stok
stok_color = ' blue ' if stok > 0 else ' red '
tk . Label (
card ,
text = f " Stok: { stok } " ,
font = ( " Arial " , 8 ) ,
fg = stok_color ,
bg = ' white '
) . pack ( pady = 2 )
color = ' blue ' if stok > 0 else ' red '
tk . Label ( card , text = f " Stok: { stok } " , font = ( " Arial " , 8 ) , fg = color , bg = ' white ' ) . pack ( )
qty_in_cart = cart_dict . get ( mid , 0 )
# --- Bagian Tombol ---
qty = cart_dict . get ( mid , 0 )
act_frame = tk . Frame ( card , bg = ' white ' )
# Pack side bottom agar tombol selalu di bawah card
act_frame . pack ( pady = 5 , fill = ' x ' , side = ' bottom ' )
btn_frame = tk . Frame ( card , bg = ' white ' )
btn_frame . pack ( pady = ( 5 , 5 ) , fill = ' x ' )
if qty_in_cart > 0 :
tk . Button (
btn_frame ,
text = " ➖ " ,
font = ( " Arial " , 10 , " bold " ) ,
bg = ' #FF5722 ' ,
fg = ' white ' ,
width = 3 ,
borderwidth = 0 ,
cursor = ' hand2 ' ,
command = lambda m = mid : self . decrease_from_card ( m )
) . pack ( side = ' left ' , padx = 2 )
tk . Label (
btn_frame ,
text = str ( qty_in_cart ) ,
font = ( " Arial " , 10 , " bold " ) ,
bg = ' white ' ,
width = 4
) . pack ( side = ' left ' , padx = 3 )
tk . Button (
btn_frame ,
text = " ➕ " ,
font = ( " Arial " , 10 , " bold " ) ,
bg = ' #4CAF50 ' ,
fg = ' white ' ,
width = 3 ,
borderwidth = 0 ,
cursor = ' hand2 ' ,
command = lambda m = mid , s = stok : self . increase_from_card ( m , s )
) . pack ( side = ' left ' , padx = 2 )
if qty > 0 :
tk . Button ( act_frame , text = " - " , bg = ' #FF5722 ' , fg = ' white ' , width = 2 , borderwidth = 0 ,
command = lambda m = mid : self . decrease_from_card ( m ) ) . pack ( side = ' left ' , padx = 2 )
tk . Label ( act_frame , text = str ( qty ) , bg = ' white ' ) . pack ( side = ' left ' , expand = True )
tk . Button ( act_frame , text = " + " , bg = ' #4CAF50 ' , fg = ' white ' , width = 2 , borderwidth = 0 ,
command = lambda m = mid , s = stok : self . increase_from_card ( m , s ) ) . pack ( side = ' right ' , padx = 2 )
else :
tk . Button (
btn_frame ,
text = " ➕ Tambah" ,
font = ( " Arial " , 9 , " bold " ) ,
bg = ' #4CAF50 ' ,
fg = ' white ' ,
borderwidth = 0 ,
cursor = ' hand2 ' ,
command = lambda m = mid , s = stok : self . increase_from_card ( m , s )
) . pack ( fill = ' x ' , padx = 2 )
tk . Button ( act_frame , text = " + Tambah " , bg = ' #4CAF50 ' , fg = ' white ' , borderwidth = 0 ,
command = lambda m = mid , s = stok : self . increase_from_card ( m , s ) ) . pack ( fill = ' x ' )
# --- Logika Pindah Baris ---
col + = 1
if col > = 2 :
if col > = total_columns :
col = 0
row + = 1
# Reset row weight
for i in range ( row + 1 ) :
self . menu_cards_frame . rowconfigure ( i , weight = 1 )
self . menu_cards_frame . rowconfigure ( i , weight = 0 )
def reset_order_search ( self ) :