@@ -1159,6 +1159,7 @@ class App:
self . root . title ( " Totoro Cafe " )
self . session = None
self . img_cache = { }
self . login_mode = ' customer '
self . setup_ui ( )
def create_hover_button ( self , parent , text , command , style = ' primary ' , width = None ) :
@@ -1278,248 +1279,176 @@ class App:
for w in self . root . winfo_children ( ) :
w . destroy ( )
# ========================================
# TOTORO LOGIN - LANDSCAPE LAYOUT (FIXED)
# ========================================
# Main container dengan background
# Main container
main_container = tk . Frame ( self . root , bg = COLORS [ ' bg_main ' ] )
main_container . pack ( fill = ' both ' , expand = True )
# Center frame untuk positioning
center_frame = tk . Frame ( main_container , bg = COLORS [ ' bg_main ' ] )
center_frame . pack ( expand = True , pady = 50 )
# ========================================
# MAIN CARD - LANDSCAPE (TANPA SHADOW DULU)
# ========================================
card = tk . Frame (
center_frame ,
bg = ' white ' ,
relief = ' ridge ' ,
borderwidth = 3 ,
padx = 0 ,
pady = 0
)
card = tk . Frame ( center_frame , bg = ' white ' , relief = ' ridge ' , borderwidth = 3 )
card . pack ( )
# ========================================
# CONTAINER HORIZONTAL (2 PANEL)
# ========================================
container = tk . Frame ( card , bg = ' white ' )
container . pack ( )
# ========================================
# LEFT PANEL - TOTORO ILLUSTRATION
# ========================================
# --- LEFT PANEL (GAMBAR) ---
left_panel = tk . Frame ( container , bg = COLORS [ ' accent ' ] , width = 350 )
left_panel . pack ( side = ' left ' , fill = ' y ' , padx = 0 , pady = 0 )
# Spacer atas
tk . Label ( left_panel , text = " " , bg = COLORS [ ' accent ' ] , height = 2 ) . pack ( )
# Totoro illustration
try :
totoro_img = Image . open ( " img/totoro_sitting.png " )
totoro_img = totoro_img . resize ( ( 250 , 250 ) , Image . Resampling . LANCZOS )
totoro_photo = ImageTk . PhotoImage ( totoro_img )
self . totoro_login_img = totoro_photo
tk . Label ( left_panel , image = totoro_photo , bg = COLORS [ ' accent ' ] ) . pack ( pady = 20 )
except :
tk . Label ( left_panel , text = " 🌿 " , font = ( ' Arial ' , 50 ) , bg = COLORS [ ' accent ' ] ) . pack ( pady = 40 )
img_label = tk . Label ( left_panel , image = totoro_photo , bg = COLORS [ ' accent ' ] )
img_label . pack ( pady = 20 )
except Exception as e :
print ( f " ⚠️ Gambar tidak ditemukan: { e } " )
tk . Label (
left_panel ,
text = " 🌿 \n \n 🍃 \n \n 🌳 " ,
font = ( ' Arial ' , 50 ) ,
bg = COLORS [ ' accent ' ] ,
fg = COLORS [ ' primary_dark ' ]
) . pack ( pady = 40 )
welcome_text = " Welcome to Totoro Café " if self . login_mode == ' customer ' else " Staff Login Area "
tk . Label ( left_panel , text = welcome_text , font = ( ' Comic Sans MS ' , 11 , ' italic ' ) , bg = COLORS [ ' accent ' ] , fg = COLORS [ ' brown ' ] ) . pack ( pady = ( 10 , 30 ) )
# Welcome text
tk . Label (
left_panel ,
text = " Welome to Totoro Café \n Totoro is waiting for you! " ,
font = ( ' Comic Sans MS ' , 11 , ' italic ' ) ,
bg = COLORS [ ' accent ' ] ,
fg = COLORS [ ' brown ' ]
) . pack ( pady = ( 10 , 30 ) )
# ========================================
# RIGHT PANEL - LOGIN FORM
# ========================================
# --- RIGHT PANEL (FORM) ---
right_panel = tk . Frame ( container , bg = ' white ' , padx = 40 , pady = 30 )
right_panel . pack ( side = ' right ' , fill = ' both ' )
# Header
tk . Label (
right_panel ,
text = " 🌿 TOTORO CAFÉ 🌿 " ,
font = ( ' Comic Sans MS ' , 22 , ' bold ' ) ,
bg = ' white ' ,
fg = COLORS [ ' primary ' ]
) . pack ( pady = ( 10 , 5 ) )
# Header text
tk . Label ( right_panel , text = " 🌿 TOTORO CAFÉ 🌿 " , font = ( ' Comic Sans MS ' , 22 , ' bold ' ) , bg = ' white ' , fg = COLORS [ ' primary ' ] ) . pack ( pady = ( 10 , 5 ) )
tk . Label (
right_panel ,
text = " Café Management System " ,
font = ( ' Segoe UI ' , 9 ) ,
bg = ' white ' ,
fg = COLORS [ ' text_light ' ]
) . pack ( pady = ( 0 , 20 ) )
mode_title = " Login Pelanggan " if self . login_mode == ' customer ' else " Login Staff / Admin "
tk . Label ( right_panel , text = mode_title , font = ( ' Segoe UI ' , 10 , ' bold ' ) , bg = ' white ' , fg = COLORS [ ' text_light ' ] ) . pack ( pady = ( 0 , 20 ) )
# Form container
form_frame = tk . Frame ( right_panel , bg = ' white ' )
form_frame . pack ( pady = 10 )
# Username
tk . Label (
form_frame ,
text = " Username: " ,
font = ( ' Segoe UI ' , 10 , ' bold ' ) ,
bg = ' white ' ,
fg = COLORS [ ' text_dark ' ]
) . grid ( row = 0 , column = 0 , sticky = ' w ' , pady = ( 10 , 5 ) , padx = 5 )
# ==========================================
# FORM LOGIC BERDASARKAN MODE
# ==========================================
self . username_var = tk . StringVar ( )
username_entry = tk . Entry (
form_frame ,
textvariable = self . username_var ,
width = 32 ,
font = ( ' Segoe UI ' , 10 ) ,
relief = ' solid ' ,
borderwidth = 1
)
username_entry . grid ( row = 1 , column = 0 , pady = ( 0 , 15 ) , ipady = 7 , padx = 5 )
if self . login_mode == ' customer ' :
# --- FORM PELANGGAN (Hanya Nama & Meja) ---
# Password
tk . Label (
form_frame ,
text = " Password: " ,
font = ( ' Segoe UI ' , 10 , ' bold ' ) ,
bg = ' white ' ,
fg = COLORS [ ' text_dark ' ]
) . grid ( row = 2 , column = 0 , sticky = ' w ' , pady = ( 5 , 5 ) , padx = 5 )
# Nama Lengkap
tk . Label ( form_frame , text = " Nama Lengkap: " , font = ( ' Segoe UI ' , 9 ) , bg = ' white ' ) . grid ( row = 0 , column = 0 , sticky = ' w ' , pady = ( 5 , 2 ) )
self . customer_name_var = tk . StringVar ( )
entry_name = tk . Entry ( form_frame , textvariable = self . customer_name_var , width = 32 , font = ( ' Segoe UI ' , 10 ) , relief = ' solid ' , borderwidth = 1 )
entry_name . grid ( row = 1 , column = 0 , pady = ( 0 , 10 ) , ipady = 5 )
self . password_var = tk . StringVar ( )
password_entry = tk . Entry (
form_frame ,
textvariable = self . password_var ,
show = " ● " ,
width = 32 ,
font = ( ' Segoe UI ' , 10 ) ,
relief = ' solid ' ,
borderwidth = 1
)
password_entry . grid ( row = 3 , column = 0 , pady = ( 0 , 15 ) , ipady = 7 , padx = 5 )
# Nomor Meja
tk . Label ( form_frame , text = " Nomor Meja (1-10): " , font = ( ' Segoe UI ' , 9 ) , bg = ' white ' ) . grid ( row = 2 , column = 0 , sticky = ' w ' , pady = ( 5 , 2 ) )
self . customer_meja_var = tk . StringVar ( )
entry_meja = tk . Entry ( form_frame , textvariable = self . customer_meja_var , width = 32 , font = ( ' Segoe UI ' , 10 ) , relief = ' solid ' , borderwidth = 1 )
entry_meja . grid ( row = 3 , column = 0 , pady = ( 0 , 20 ) , ipady = 5 )
# Separator
separator = tk . Frame ( form_frame , bg = COLORS [ ' border ' ] , height = 2 )
separator . grid ( row = 4 , column = 0 , sticky = ' ew ' , pady = 12 , padx = 5 )
# Button Masuk
btn_text = " MASUK SEBAGAI PELANGGAN "
cmd = self . handle_customer_login
# Info box
info_frame = tk . Frame (
form_frame ,
bg = ' #FFF9E6 ' ,
relief = ' solid ' ,
borderwidth = 1
)
info_frame . grid ( row = 5 , column = 0 , pady = 8 , sticky = ' ew ' , padx = 5 )
else :
# --- FORM STAFF (Username & Password) ---
tk . Label (
info_frame ,
text = " ℹ ️ Khusus untuk Pembeli" ,
font = ( ' Segoe UI ' , 9 , ' bold ' ) ,
bg = ' #FFF9E6 ' ,
fg = COLORS [ ' brown ' ]
) . pack ( anchor = ' w ' , padx = 10 , pady = ( 8 , 3 ) )
# Username
tk . Label ( form_frame , text = " Username: " , font = ( ' Segoe UI ' , 9 , ' bold ' ) , bg = ' white ' ) . grid ( row = 0 , column = 0 , sticky = ' w ' , pady = ( 5 , 2 ) )
self . username_var = tk . StringVar ( )
entry_user = tk . Entry ( form_frame , textvariable = self . username_var , width = 32 , font = ( ' Segoe UI ' , 10 ) , relief = ' solid ' , borderwidth = 1 )
entry_user . grid ( row = 1 , column = 0 , pady = ( 0 , 10 ) , ipady = 5 )
tk . Label (
info_frame ,
text = " Silakan isi Nama & Nomor Meja " ,
font = ( ' Segoe UI ' , 8 ) ,
bg = ' #FFF9E6 ' ,
fg = COLORS [ ' text_light ' ]
) . pack ( anchor = ' w ' , padx = 10 , pady = ( 0 , 8 ) )
# Password
tk . Label ( form_frame , text = " Password: " , font = ( ' Segoe UI ' , 9 , ' bold ' ) , bg = ' white ' ) . grid ( row = 2 , column = 0 , sticky = ' w ' , pady = ( 5 , 2 ) )
self . password_var = tk . StringVar ( )
entry_pass = tk . Entry ( form_frame , textvariable = self . password_var , show = " ● " , width = 32 , font = ( ' Segoe UI ' , 10 ) , relief = ' solid ' , borderwidth = 1 )
entry_pass . grid ( row = 3 , column = 0 , pady = ( 0 , 20 ) , ipady = 5 )
# Nama Lengkap
tk . Label (
form_frame ,
text = " Nama Lengkap: " ,
font = ( ' Segoe UI ' , 9 ) ,
bg = ' white ' ,
fg = COLORS [ ' text_light ' ]
) . grid ( row = 6 , column = 0 , sticky = ' w ' , pady = ( 10 , 3 ) , padx = 5 )
# Button Login
btn_text = " LOGIN STAFF "
cmd = self . handle_staff_login
self . customer_name_var = tk . StringVar ( )
customer_entry = tk . Entry (
form_frame ,
textvariable = self . customer_name_var ,
width = 32 ,
font = ( ' Segoe UI ' , 9 ) ,
relief = ' solid ' ,
borderwidth = 1
)
customer_entry . grid ( row = 7 , column = 0 , pady = ( 0 , 10 ) , ipady = 6 , padx = 5 )
# Nomor Meja
tk . Label (
form_frame ,
text = " Nomor Meja (1-10): " ,
font = ( ' Segoe UI ' , 9 ) ,
bg = ' white ' ,
fg = COLORS [ ' text_light ' ]
) . grid ( row = 8 , column = 0 , sticky = ' w ' , pady = ( 5 , 3 ) , padx = 5 )
self . customer_meja_var = tk . StringVar ( )
meja_entry = tk . Entry (
form_frame ,
textvariable = self . customer_meja_var ,
width = 32 ,
font = ( ' Segoe UI ' , 9 ) ,
relief = ' solid ' ,
borderwidth = 1
)
meja_entry . grid ( row = 9 , column = 0 , pady = ( 0 , 20 ) , ipady = 6 , padx = 5 )
# Login button
# Render Main Button
login_btn = tk . Button (
form_frame ,
text = " LOGIN " ,
command = self . handle_login ,
bg = COLORS [ ' primary ' ] ,
fg = ' white ' ,
font = ( ' Segoe UI ' , 11 , ' bold ' ) ,
relief = ' flat ' ,
cursor = ' hand2 ' ,
width = 28 ,
height = 2 ,
borderwidth = 0
form_frame , text = btn_text , command = cmd ,
bg = COLORS [ ' primary ' ] , fg = ' white ' , font = ( ' Segoe UI ' , 10 , ' bold ' ) ,
relief = ' flat ' , cursor = ' hand2 ' , width = 28 , height = 2
)
login_btn . grid ( row = 10 , column = 0 , pady = ( 10 , 15 ) , padx = 5 )
login_btn . grid ( row = 4 , column = 0 , pady = ( 10 , 10 ) )
def on_enter ( e ) :
login_btn [ ' background ' ] = COLORS [ ' primary_dark ' ]
def on_leave ( e ) :
login_btn [ ' background ' ] = COLORS [ ' primary ' ]
# Hover effect
login_btn . bind ( " <Enter> " , lambda e : login_btn . config ( bg = COLORS [ ' primary_dark ' ] ) )
login_btn . bind ( " <Leave> " , lambda e : login_btn . config ( bg = COLORS [ ' primary ' ] ) )
login_btn . bind ( " <Enter> " , on_enter )
login_btn . bind ( " <Leave> " , on_leave )
# --- SWITCH MODE LINK ---
switch_frame = tk . Frame ( right_panel , bg = ' white ' )
switch_frame . pack ( side = ' bottom ' , pady = ( 10 , 10 ) )
# Footer
tk . Label (
right_panel ,
text = " © 2025 Café Totoro - UAS Informatika " ,
font = ( ' Segoe UI ' , 7 ) ,
bg = ' white ' ,
fg = COLORS [ ' text_light ' ]
) . pack ( side = ' bottom ' , pady = ( 20 , 10 ) )
if self . login_mode == ' customer ' :
lbl = tk . Label ( switch_frame , text = " Anda karyawan? " , bg = ' white ' , fg = ' gray ' , font = ( ' Segoe UI ' , 8 ) )
lbl . pack ( side = ' left ' )
btn = tk . Button ( switch_frame , text = " Login Staff di sini " , command = self . toggle_login_mode ,
bg = ' white ' , fg = COLORS [ ' primary_dark ' ] , font = ( ' Segoe UI ' , 8 , ' bold ' , ' underline ' ) ,
relief = ' flat ' , cursor = ' hand2 ' , bd = 0 , activebackground = ' white ' )
btn . pack ( side = ' left ' , padx = 5 )
else :
lbl = tk . Label ( switch_frame , text = " Kembali ke " , bg = ' white ' , fg = ' gray ' , font = ( ' Segoe UI ' , 8 ) )
lbl . pack ( side = ' left ' )
btn = tk . Button ( switch_frame , text = " Halaman Pelanggan " , command = self . toggle_login_mode ,
bg = ' white ' , fg = COLORS [ ' primary_dark ' ] , font = ( ' Segoe UI ' , 8 , ' bold ' , ' underline ' ) ,
relief = ' flat ' , cursor = ' hand2 ' , bd = 0 , activebackground = ' white ' )
btn . pack ( side = ' left ' , padx = 5 )
def handle_login ( self ) :
def handle_customer_login ( self ) :
""" Login khusus pembeli tanpa password (auto-auth user default) """
customer_name = self . customer_name_var . get ( ) . strip ( )
customer_meja = self . customer_meja_var . get ( ) . strip ( )
# Validasi Input
if not customer_name :
messagebox . showwarning ( " Input Kurang " , " Silakan isi Nama Lengkap Anda " )
return
if not customer_meja :
messagebox . showwarning ( " Input Kurang " , " Silakan isi Nomor Meja " )
return
try :
nomor_meja = int ( customer_meja )
if nomor_meja < 1 or nomor_meja > 10 :
messagebox . showwarning ( " Meja Invalid " , " Nomor meja tersedia: 1 - 10 " )
return
except ValueError :
messagebox . showerror ( " Input Error " , " Nomor meja harus angka! " )
return
# CEK KETERSEDIAAN MEJA
meja_data = meja_get ( nomor_meja )
if meja_data :
status_meja = meja_data [ 1 ]
if status_meja == ' terisi ' :
messagebox . showwarning ( " Meja Penuh " , f " Maaf, Meja { nomor_meja } sedang terisi. \n Silakan pilih meja lain. " )
return
# AUTHENTICATE SECARA OTOMATIS MENGGUNAKAN AKUN DEFAULT 'user'
user = authenticate ( " user " , " user123 " )
if not user :
# Failsafe jika user default tidak sengaja terhapus
user = { ' id ' : 999 , ' username ' : ' user ' , ' role ' : ' pembeli ' }
# Simpan detail tamu ke session
user [ ' customer_name ' ] = customer_name
user [ ' nomor_meja ' ] = nomor_meja
# Update status meja jadi terisi
meja_update_status ( nomor_meja , ' terisi ' , ' ' )
self . session = user
messagebox . showinfo ( " Selamat Datang " , f " Halo, { customer_name } ! \n Selamat datang di Totoro Café. " )
self . dashboard_frame ( )
def handle_staff_login ( self ) :
""" Login konvensional untuk staff (butuh password) """
u = self . username_var . get ( ) . strip ( )
p = self . password_var . get ( ) . strip ( )
@@ -1532,65 +1461,25 @@ class App:
messagebox . showerror ( " Gagal " , " Username atau password salah " )
return
# VALIDASI UNTUK PEMBELI
# Cegah pembeli login lewat jalur staff
if user [ ' role ' ] in [ ' pembeli ' , ' user ' ] :
customer_name = self . customer_name_var . get ( ) . strip ( )
customer_meja = self . customer_meja_var . get ( ) . strip ( )
# Validasi nama
if not customer_name :
messagebox . showwarning ( " Nama Pembeli " , " Pembeli harus mengisi Nama Lengkap! " )
return
# Validasi nomor meja
if not customer_meja :
messagebox . showwarning ( " Nomor Meja " , " Pembeli harus mengisi Nomor Meja! " )
return
# Validasi format nomor meja
try :
nomor_meja = int ( customer_meja )
if nomor_meja < 1 or nomor_meja > 10 :
messagebox . showwarning ( " Nomor Meja Invalid " , " Nomor meja harus antara 1-10 " )
return
except ValueError :
messagebox . showerror ( " Input Error " , " Nomor meja harus berupa angka! " )
return
# CEK APAKAH MEJA TERSEDIA
meja_data = meja_get ( nomor_meja )
if meja_data :
status_meja = meja_data [ 1 ]
if status_meja == ' terisi ' :
messagebox . showwarning (
" Meja Tidak Tersedia " ,
f " ❌ Maaf, Meja { nomor_meja } sedang terisi. \n \n "
f " Silakan pilih nomor meja lain. "
)
return
# Simpan nama dan nomor meja di session
user [ ' customer_name ' ] = customer_name
user [ ' nomor_meja ' ] = nomor_meja
# RESERVASI MEJA (set status terisi tanpa transaksi_id dulu)
# Nanti saat checkout baru dikasih transaksi_id
meja_update_status ( nomor_meja , ' terisi ' , ' ' )
# Pesan selamat datang khusus pembeli
welcome_msg = f " ✅ Selamat datang, { customer_name } ! \n \n "
welcome_msg + = f " 📍 Anda duduk di Meja { nomor_meja } \n \n "
welcome_msg + = f " 🍽️ Silakan pilih menu dan pesan makanan Anda. \n "
welcome_msg + = f " Selamat menikmati! "
messagebox . showinfo ( " Login Berhasil " , welcome_msg )
else :
# Pesan untuk role lain (admin, kasir, waiter, pemilik)
messagebox . showinfo ( " Sukses " , f " Login berhasil sebagai { user [ ' role ' ] } " )
self . login_mode = ' customer '
self . login_frame ( )
messagebox . showinfo ( " Info " , " Silakan login melalui Halaman Pelanggan " )
return
self . session = user
messagebox . showinfo ( " Sukses " , f " Login berhasil sebagai { user [ ' role ' ] . title ( ) } " )
self . dashboard_frame ( )
def toggle_login_mode ( self ) :
""" Ganti mode antara customer dan staff """
if self . login_mode == ' customer ' :
self . login_mode = ' staff '
else :
self . login_mode = ' customer '
self . login_frame ( )
def logout ( self ) :
self . session = None