diff --git a/detail_transaksi.csv b/detail_transaksi.csv index 31c47ac..66e70c8 100644 --- a/detail_transaksi.csv +++ b/detail_transaksi.csv @@ -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 diff --git a/favorite.csv b/favorite.csv index d924f2f..c70fa02 100644 --- a/favorite.csv +++ b/favorite.csv @@ -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 diff --git a/main.py b/main.py index 815d9f8..d55e1b2 100644 --- a/main.py +++ b/main.py @@ -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,319 +1279,207 @@ 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 - - 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) + 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) - # 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)) + 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)) - # ======================================== - # 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) + + # ========================================== + # FORM LOGIC BERDASARKAN MODE + # ========================================== - # 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) - - 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)) - - # 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) - - 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 + 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').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) + + # 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) + + # 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("", lambda e: login_btn.config(bg=COLORS['primary_dark'])) + login_btn.bind("", lambda e: login_btn.config(bg=COLORS['primary'])) - login_btn.bind("", on_enter) - login_btn.bind("", 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() if not u or not p: messagebox.showwarning("Input", "Masukkan username & password") return - + user = authenticate(u, p) if not user: 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 diff --git a/menu.csv b/menu.csv index e22bec2..8dbcad6 100644 --- a/menu.csv +++ b/menu.csv @@ -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 diff --git a/pembayaran.csv b/pembayaran.csv index 840d7dd..728a0da 100644 --- a/pembayaran.csv +++ b/pembayaran.csv @@ -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, diff --git a/struk/STR-17-20251215_103750.txt b/struk/STR-17-20251215_103750.txt new file mode 100644 index 0000000..02f63a3 --- /dev/null +++ b/struk/STR-17-20251215_103750.txt @@ -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! +═════════════════════════════════════════ diff --git a/transaksi.csv b/transaksi.csv index bab8a7c..ee932c6 100644 --- a/transaksi.csv +++ b/transaksi.csv @@ -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