This commit is contained in:
Bluwww 2025-12-15 10:39:05 +07:00
parent f1aea178a2
commit 07ba59822e
7 changed files with 187 additions and 264 deletions

View File

@ -27,3 +27,5 @@ id,transaksi_id,menu_id,qty,harga_satuan,subtotal_item
26,15,7,1,21000.0,21000.0
27,16,7,1,21000.0,21000.0
28,16,8,1,25000.0,25000.0
29,17,3,1,30000.0,30000.0
30,17,4,1,25000.0,25000.0

1 id transaksi_id menu_id qty harga_satuan subtotal_item
27 26 15 7 1 21000.0 21000.0
28 27 16 7 1 21000.0 21000.0
29 28 16 8 1 25000.0 25000.0
30 29 17 3 1 30000.0 30000.0
31 30 17 4 1 25000.0 25000.0

View File

@ -4,8 +4,8 @@ user_id,menu_id,order_count,last_ordered
1,4,1,2025-12-13 20:12:35
1,6,1,2025-12-13 20:12:35
4,2,3,2025-12-14 21:14:46
4,4,3,2025-12-14 10:35:35
4,3,5,2025-12-14 22:09:34
4,4,4,2025-12-15 10:36:38
4,3,6,2025-12-15 10:36:38
4,11,1,2025-12-14 01:44:46
4,7,5,2025-12-15 09:35:44
4,8,3,2025-12-15 09:35:45

1 user_id menu_id order_count last_ordered
4 1 4 1 2025-12-13 20:12:35
5 1 6 1 2025-12-13 20:12:35
6 4 2 3 2025-12-14 21:14:46
7 4 4 3 4 2025-12-14 10:35:35 2025-12-15 10:36:38
8 4 3 5 6 2025-12-14 22:09:34 2025-12-15 10:36:38
9 4 11 1 2025-12-14 01:44:46
10 4 7 5 2025-12-15 09:35:44
11 4 8 3 2025-12-15 09:35:45

385
main.py
View File

@ -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é\nTotoro 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)
# 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)
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)
# Separator
separator = tk.Frame(form_frame, bg=COLORS['border'], height=2)
separator.grid(row=4, column=0, sticky='ew', pady=12, padx=5)
# 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)
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))
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))
if self.login_mode == 'customer':
# --- FORM PELANGGAN (Hanya Nama & Meja) ---
# 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)
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()
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)
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)
# 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)
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()
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)
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)
# Login button
# Button Masuk
btn_text = "MASUK SEBAGAI PELANGGAN"
cmd = self.handle_customer_login
else:
# --- FORM STAFF (Username & Password) ---
# 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)
# 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)
# Button Login
btn_text = "LOGIN STAFF"
cmd = self.handle_staff_login
# 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.\nSilakan 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}!\nSelamat 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!")
self.login_mode = 'customer'
self.login_frame()
messagebox.showinfo("Info", "Silakan login melalui Halaman Pelanggan")
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.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

View File

@ -1,8 +1,8 @@
id,nama,kategori,harga,stok,foto,tersedia,item_discount_pct
1,Americano,Minuman,20000.0,7,img/americano.jpg,1,0.0
2,Latte,Minuman,25000.0,3,img/latte.jpg,1,10.0
3,Banana Cake,Dessert,30000.0,6,img/banana_cake.jpg,1,4.0
4,Nasi Goreng,Makanan,25000.0,9,img/nasi_goreng.jpg,1,0.0
3,Banana Cake,Dessert,30000.0,5,img/banana_cake.jpg,1,4.0
4,Nasi Goreng,Makanan,25000.0,8,img/nasi_goreng.jpg,1,0.0
5,Nasi Kuning,Makanan,18000.0,7,img/Nasi-Kuning.jpg,1,5.0
6,Strawberry Milkshake,Minuman,19000.0,5,img/strawberry_milkshake.jpg,1,0.0
7,Coconut Matcha,Minuman,21000.0,9,img/coconut_matcha.jpg,1,5.0

1 id nama kategori harga stok foto tersedia item_discount_pct
2 1 Americano Minuman 20000.0 7 img/americano.jpg 1 0.0
3 2 Latte Minuman 25000.0 3 img/latte.jpg 1 10.0
4 3 Banana Cake Dessert 30000.0 6 5 img/banana_cake.jpg 1 4.0
5 4 Nasi Goreng Makanan 25000.0 9 8 img/nasi_goreng.jpg 1 0.0
6 5 Nasi Kuning Makanan 18000.0 7 img/Nasi-Kuning.jpg 1 5.0
7 6 Strawberry Milkshake Minuman 19000.0 5 img/strawberry_milkshake.jpg 1 0.0
8 7 Coconut Matcha Minuman 21000.0 9 img/coconut_matcha.jpg 1 5.0

View File

@ -9,3 +9,4 @@ id,transaksi_id,metode_pembayaran,jumlah_bayar,status_pembayaran,tanggal_bayar,s
8,14,cash,100000.0,sukses,2025-12-14 21:46:20,
9,15,qris,46312.5,sukses,2025-12-15 08:52:51,
10,16,qris,24172.5,sukses,2025-12-15 09:39:21,
11,17,cash,40000.0,sukses,2025-12-15 10:37:50,

1 id transaksi_id metode_pembayaran jumlah_bayar status_pembayaran tanggal_bayar struk
9 8 14 cash 100000.0 sukses 2025-12-14 21:46:20
10 9 15 qris 46312.5 sukses 2025-12-15 08:52:51
11 10 16 qris 24172.5 sukses 2025-12-15 09:39:21
12 11 17 cash 40000.0 sukses 2025-12-15 10:37:50

View File

@ -0,0 +1,30 @@
═════════════════════════════════════════
TOTORO CAFE
Jl. Raya Totoro No. 123, Surabaya
Telp: 031-123456
═════════════════════════════════════════
No. Struk : STR-17-20251215
Tanggal : 15/12/2025 10:36
Meja : 1
Kasir : kasir
Pembayaran : CASH
─────────────────────────────────────────
ITEM PESANAN:
─────────────────────────────────────────
Banana Cake
1 x Rp 30,000 Rp 30,000
Nasi Goreng
1 x Rp 25,000 Rp 25,000
─────────────────────────────────────────
Subtotal : Rp 55,000
Diskon Item : Rp 1,200
Diskon Promo : Rp 24,210
─────────────────────────────────────────
TOTAL BAYAR : Rp 29,590
Bayar : Rp 40,000
Kembalian : Rp 10,410
═════════════════════════════════════════
TERIMA KASIH ATAS KUNJUNGAN ANDA
SAMPAI JUMPA LAGI!
═════════════════════════════════════════

View File

@ -15,3 +15,4 @@ id,user_id,nomor_meja,total,status,promo_code,subtotal,item_discount,promo_disco
14,4,9,18400.0,dibayar,,20000.0,1600.0,0.0,2025-12-14 21:43:51
15,4,4,46312.5,dibayar,ANGIN,51000.0,2250.0,2437.5,2025-12-14 22:09:34
16,4,2,24172.5,dibayar,MERDEKA,46000.0,2050.0,19777.5,2025-12-15 09:35:44
17,4,1,29590.0,dibayar,MERDEKA,55000.0,1200.0,24210.0,2025-12-15 10:36:38

1 id user_id nomor_meja total status promo_code subtotal item_discount promo_discount tanggal
15 14 4 9 18400.0 dibayar 20000.0 1600.0 0.0 2025-12-14 21:43:51
16 15 4 4 46312.5 dibayar ANGIN 51000.0 2250.0 2437.5 2025-12-14 22:09:34
17 16 4 2 24172.5 dibayar MERDEKA 46000.0 2050.0 19777.5 2025-12-15 09:35:44
18 17 4 1 29590.0 dibayar MERDEKA 55000.0 1200.0 24210.0 2025-12-15 10:36:38