"""akun default buat login :
    - admin / admin123  (role admin)
    - kasir / kasir123 (role kasir)
    - waiter / waiter123 (role waiter)
    - user / user123 (role pembeli)
    - owner / owner123 (role pemilik)
"""

import os
import csv
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, simpledialog
from PIL import Image, ImageTk



# ========================================
# UI STYLING - TOTORO THEME COLORS + FONTS
# ========================================
COLORS = {
    'bg_main': '#F5F5DC',           # Beige/Krem lembut (background utama)
    'bg_secondary': '#FFFFFF',      # Putih untuk cards
    'primary': '#8FBFA8',           # Hijau Totoro (medium)
    'primary_dark': '#6B9080',      # Hijau tua
    'accent': '#A8D5BA',            # Hijau pastel lembut
    'brown': '#8B7355',             # Coklat kayu
    'text_dark': '#3D3D3D',         # Text gelap
    'text_light': '#666666',        # Text abu-abu
    'success': '#6B9080',           # Hijau untuk success
    'warning': '#D4A574',           # Coklat muda untuk warning
    'danger': '#C9797A',            # Merah lembut untuk danger
    'border': '#D4C5B9',            # Border coklat muda
    'shadow': '#D0D0D0',            # Shadow abu-abu lembut
}

# Font configuration untuk tema Totoro
FONTS = {
    'header': ('Comic Sans MS', 16, 'bold'),      
    'subheader': ('Segoe UI', 12, 'bold'),       
    'body': ('Segoe UI', 10),                     
    'body_bold': ('Segoe UI', 10, 'bold'),        
    'small': ('Segoe UI', 9),                     
    'tiny': ('Segoe UI', 8),                      
    'button': ('Segoe UI', 11, 'bold'),           
    'mono': ('Consolas', 9),                      
}

USERS_CSV = "users.csv"
MENU_CSV = "menu.csv"
PROMO_CSV = "promo.csv"
TRANSAKSI_CSV = "transaksi.csv"
DETAIL_TRANSAKSI_CSV = "detail_transaksi.csv"
FAVORITE_CSV = "favorite.csv"
MEJA_CSV = "meja.csv"
PEMBAYARAN_CSV = "pembayaran.csv"
IMG_PREVIEW_SIZE = (120, 80)

def create_placeholder_image():
    """Buat placeholder jika gambar belum ada"""
    if not os.path.exists('img'):
        os.makedirs('img')
    
    if not os.path.exists('img/totoro_sitting.png'):
        try:
            from PIL import Image, ImageDraw
            
            # Buat gambar placeholder 300x300
            img = Image.new('RGB', (300, 300), color=COLORS['accent'])
            draw = ImageDraw.Draw(img)
            
            # Tulis text
            draw.text((50, 140), "TOTORO\nPLACEHOLDER", fill=COLORS['brown'])
            
            img.save('img/totoro_sitting.png')
        except:
            pass

# Panggil saat init
create_placeholder_image()

def ensure_file(path, fieldnames):
    if not os.path.exists(path):
        with open(path, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=fieldnames)
            writer.writeheader()


def read_all(path):
    if not os.path.exists(path):
        return []
    with open(path, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        return list(reader)


def write_all(path, fieldnames, rows):
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(rows)


def next_int_id(rows, id_field="id"):
    max_id = 0
    for r in rows:
        try:
            v = int(r.get(id_field, 0) or 0)
            if v > max_id:
                max_id = v
        except:
            continue
    return str(max_id + 1)



def init_db_csv():
    ensure_file(USERS_CSV, ["id", "username", "password", "role"])
    ensure_file(MENU_CSV, ["id", "nama", "kategori", "harga", "stok", "foto", "tersedia", "item_discount_pct"])
    ensure_file(PROMO_CSV, ["code", "type", "value", "min_total"])
    ensure_file(TRANSAKSI_CSV, ["id", "user_id", "nomor_meja", "total", "status", "promo_code", "subtotal", "item_discount", "promo_discount", "tanggal"])
    ensure_file(DETAIL_TRANSAKSI_CSV, ["id", "transaksi_id", "menu_id", "qty", "harga_satuan", "subtotal_item"])
    ensure_file(FAVORITE_CSV, ["user_id", "menu_id", "order_count", "last_ordered"])
    ensure_file(MEJA_CSV, ["nomor_meja", "status", "transaksi_id"])
    ensure_file(PEMBAYARAN_CSV, ["id", "transaksi_id", "metode_pembayaran", "jumlah_bayar", "status_pembayaran", "tanggal_bayar", "struk"])

    seed_defaults()






# buat masukin data/sample ke database csv 

def seed_defaults():
    users = read_all(USERS_CSV)
    if not users:
        defaults = [
            ('admin','admin123','admin'),
            ('kasir','kasir123','kasir'),
            ('waiter','waiter123','waiter'),
            ('user','user123','pembeli'),
            ('owner','owner123','pemilik'),
        ]
        rows = []
        for i,(u,p,r) in enumerate(defaults, start=1):
            rows.append({"id": str(i), "username": u, "password": p, "role": r})
        write_all(USERS_CSV, ["id","username","password","role"], rows)

    menu_rows = read_all(MENU_CSV)
    if not menu_rows:
        sample = [
            ('Americano','Minuman',20000,10,None,1,0),
            ('Latte','Minuman',25000,5,None,1,10),
            ('Banana Cake','Dessert',30000,2,None,1,0),
            ('Nasi Goreng','Makanan',35000,0,None,0,0),
        ]
        rows = []
        for i,(name,kategori,harga,stok,foto,tersedia,disc) in enumerate(sample, start=1):
            rows.append({
                "id": str(i),
                "nama": name,
                "kategori": kategori,
                "harga": str(harga),
                "stok": str(stok),
                "foto": foto or "",
                "tersedia": str(tersedia),
                "item_discount_pct": str(disc)
            })
        write_all(MENU_CSV, ["id","nama","kategori","harga","stok","foto","tersedia","item_discount_pct"], rows)

    promo_rows = read_all(PROMO_CSV)
    if not promo_rows:
        promos = [
            ('OPENING','percent',10,0),
            ('ANNIVERSARY','fixed',5000,20000),
        ]
        rows = []
        for code,ptype,val,min_total in promos:
            rows.append({
                "code": code,
                "type": ptype,
                "value": str(val),
                "min_total": str(min_total)
            })
        write_all(PROMO_CSV, ["code","type","value","min_total"], rows)


    # Seed meja (10 meja default)
    meja_rows = read_all(MEJA_CSV)
    if not meja_rows:
        rows = []
        for i in range(1, 11):  # Meja 1-10
            rows.append({
                "nomor_meja": str(i),
                "status": "kosong",
                "transaksi_id": ""
            })
        write_all(MEJA_CSV, ["nomor_meja", "status", "transaksi_id"], rows)










def authenticate(username, password):
    rows = read_all(USERS_CSV)
    for r in rows:
        if r.get("username") == username and r.get("password") == password:
            return {'id': int(r.get("id")), 'username': r.get("username"), 'role': r.get("role")}
    return None










# Wilayah dikuasai Menu


def menu_add(nama, kategori, harga, stok, foto, item_discount_pct=0):
    rows = read_all(MENU_CSV)
    new_id = next_int_id(rows, "id")
    tersedia = "1" if int(stok) > 0 else "0"
    rows.append({
        "id": new_id,
        "nama": nama,
        "kategori": kategori,
        "harga": str(float(harga)),
        "stok": str(int(stok)),
        "foto": foto or "",
        "tersedia": tersedia,
        "item_discount_pct": str(float(item_discount_pct))
    })
    write_all(MENU_CSV, ["id","nama","kategori","harga","stok","foto","tersedia","item_discount_pct"], rows)


def menu_update(menu_id, nama, kategori, harga, stok, foto, item_discount_pct=0):
    rows = read_all(MENU_CSV)
    found = False
    for r in rows:
        if r.get("id") == str(menu_id):
            r["nama"] = nama
            r["kategori"] = kategori
            r["harga"] = str(float(harga))
            r["stok"] = str(int(stok))
            r["foto"] = foto or ""
            r["tersedia"] = "1" if int(stok) > 0 else "0"
            r["item_discount_pct"] = str(float(item_discount_pct))
            found = True
            break
    if found:
        write_all(MENU_CSV, ["id","nama","kategori","harga","stok","foto","tersedia","item_discount_pct"], rows)
    else:
        raise ValueError("Menu id tidak ditemukan")


def menu_delete(menu_id):
    rows = read_all(MENU_CSV)
    newrows = [r for r in rows if r.get("id") != str(menu_id)]
    write_all(MENU_CSV, ["id","nama","kategori","harga","stok","foto","tersedia","item_discount_pct"], newrows)


def menu_list(kategori=None, available_only=False, search_text=None):
    rows = read_all(MENU_CSV)
    out = []
    for r in rows:
        if kategori and r.get("kategori") != kategori:
            continue
        if available_only and r.get("tersedia") != "1":
            continue
        if search_text:
            s = search_text.lower()
            if s not in (r.get("nama","").lower() or "") and s not in (r.get("kategori","").lower() or ""):
                continue
        try:
            mid = int(r.get("id") or 0)
        except:
            mid = r.get("id")
        try:
            harga = float(r.get("harga") or 0.0)
        except:
            harga = 0.0
        try:
            stok = int(float(r.get("stok") or 0))
        except:
            stok = 0
        foto = r.get("foto") or None
        tersedia = 1 if r.get("tersedia") == "1" else 0
        try:
            item_disc = float(r.get("item_discount_pct") or 0.0)
        except:
            item_disc = 0.0
        out.append((mid, r.get("nama"), r.get("kategori"), harga, stok, foto, tersedia, item_disc))
    out.sort(key=lambda x: int(x[0]))
    return out


def menu_get(menu_id):
    rows = read_all(MENU_CSV)
    for r in rows:
        if r.get("id") == str(menu_id):
            try:
                mid = int(r.get("id") or 0)
            except:
                mid = r.get("id")
            try:
                harga = float(r.get("harga") or 0.0)
            except:
                harga = 0.0
            try:
                stok = int(float(r.get("stok") or 0))
            except:
                stok = 0
            foto = r.get("foto") or None
            tersedia = 1 if r.get("tersedia") == "1" else 0
            try:
                item_disc = float(r.get("item_discount_pct") or 0.0)
            except:
                item_disc = 0.0
            return (mid, r.get("nama"), r.get("kategori"), harga, stok, foto, tersedia, item_disc)
    return None


def menu_decrease_stock(menu_id, qty):
    rows = read_all(MENU_CSV)
    found = False
    for r in rows:
        if r.get("id") == str(menu_id):
            found = True
            try:
                stok = int(float(r.get("stok") or 0))
            except:
                stok = 0
            if stok < qty:
                return False, "Stok tidak cukup"
            newstok = stok - qty
            r["stok"] = str(newstok)
            r["tersedia"] = "1" if newstok > 0 else "0"
            break
    if not found:
        return False, "Menu tidak ditemukan"
    write_all(MENU_CSV, ["id","nama","kategori","harga","stok","foto","tersedia","item_discount_pct"], rows)
    return True, newstok















# Reza balap liar

# wilayah dikuasai promo 



def promo_add(code, ptype, value, min_total=0):
    rows = read_all(PROMO_CSV)
    for r in rows:
        if r.get("code") == code:
            raise ValueError("Kode promo sudah ada")
    rows.append({
        "code": code,
        "type": ptype,
        "value": str(float(value)),
        "min_total": str(float(min_total))
    })
    write_all(PROMO_CSV, ["code","type","value","min_total"], rows)


def promo_update(code, ptype, value, min_total=0):
    rows = read_all(PROMO_CSV)
    found = False
    for r in rows:
        if r.get("code") == code:
            r["type"] = ptype
            r["value"] = str(float(value))
            r["min_total"] = str(float(min_total))
            found = True
            break
    if not found:
        raise ValueError("Promo tidak ditemukan")
    write_all(PROMO_CSV, ["code","type","value","min_total"], rows)


def promo_delete(code):
    rows = read_all(PROMO_CSV)
    newrows = [r for r in rows if r.get("code") != code]
    write_all(PROMO_CSV, ["code","type","value","min_total"], newrows)


def promo_list():
    rows = read_all(PROMO_CSV)
    out = []
    for r in rows:
        try:
            val = float(r.get("value") or 0.0)
        except:
            val = 0.0
        try:
            mt = float(r.get("min_total") or 0.0)
        except:
            mt = 0.0
        out.append((r.get("code"), r.get("type"), val, mt))
    out.sort(key=lambda x: x[0] or "")
    return out


def promo_get(code):
    rows = read_all(PROMO_CSV)
    for r in rows:
        if r.get("code") == code:
            try:
                val = float(r.get("value") or 0.0)
            except:
                val = 0.0
            try:
                mt = float(r.get("min_total") or 0.0)
            except:
                mt = 0.0
            return (r.get("code"), r.get("type"), val, mt)
    return None


# Wilayah dikuasai Transaksi


def transaksi_add(user_id, nomor_meja, cart_items, promo_code=None):
    """Simpan transaksi baru dengan status 'pending'"""
    from datetime import datetime
    
    if not cart_items:
        return False, "Keranjang kosong"
    
    # Hitung diskon dan total
    calc = apply_discounts_and_promo(cart_items, promo_code)
    
    # Buat transaksi
    rows = read_all(TRANSAKSI_CSV)
    transaksi_id = next_int_id(rows, "id")
    tanggal = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    transaksi_row = {
        "id": transaksi_id,
        "user_id": str(user_id),
        "nomor_meja": str(nomor_meja),
        "total": str(calc['total']),
        "status": "pending",
        "promo_code": calc.get('promo_code') or "",
        "subtotal": str(calc['subtotal']),
        "item_discount": str(calc['item_discount']),
        "promo_discount": str(calc['promo_discount']),
        "tanggal": tanggal
    }
    rows.append(transaksi_row)
    write_all(TRANSAKSI_CSV, ["id", "user_id", "nomor_meja", "total", "status", "promo_code", "subtotal", "item_discount", "promo_discount", "tanggal"], rows)
    
    # Simpan detail transaksi
    detail_rows = read_all(DETAIL_TRANSAKSI_CSV)
    for item in cart_items:
        detail_id = next_int_id(detail_rows, "id")
        menu_data = menu_get(item['menu_id'])
        if not menu_data:
            continue
        _, nama, kategori, harga, stok, foto, tersedia, item_disc = menu_data
        
        qty = int(item['qty'])
        subtotal_item = harga * qty
        
        detail_rows.append({
            "id": detail_id,
            "transaksi_id": transaksi_id,
            "menu_id": str(item['menu_id']),
            "qty": str(qty),
            "harga_satuan": str(harga),
            "subtotal_item": str(subtotal_item)
        })
        
        # Kurangi stok
        success, msg = menu_decrease_stock(item['menu_id'], qty)
        if not success:
            return False, f"Gagal mengurangi stok menu ID {item['menu_id']}: {msg}"
        
        # Update favorite count
        favorite_update(user_id, item['menu_id'])
    
    write_all(DETAIL_TRANSAKSI_CSV, ["id", "transaksi_id", "menu_id", "qty", "harga_satuan", "subtotal_item"], detail_rows)
    
    return True, transaksi_id


def transaksi_list(status=None, user_id=None):
    """Ambil daftar transaksi, bisa filter by status atau user_id"""
    rows = read_all(TRANSAKSI_CSV)
    out = []
    for r in rows:
        if status and r.get("status") != status:
            continue
        if user_id and r.get("user_id") != str(user_id):
            continue
        
        try:
            tid = int(r.get("id") or 0)
        except:
            tid = r.get("id")
        try:
            uid = int(r.get("user_id") or 0)
        except:
            uid = r.get("user_id")
        try:
            meja = int(r.get("nomor_meja") or 0)
        except:
            meja = r.get("nomor_meja")
        try:
            total = float(r.get("total") or 0.0)
        except:
            total = 0.0
        
        out.append((tid, uid, meja, total, r.get("status"), r.get("promo_code"), r.get("tanggal")))
    
    out.sort(key=lambda x: int(x[0]), reverse=True)
    return out


def transaksi_get(transaksi_id):
    """Ambil detail transaksi by ID"""
    rows = read_all(TRANSAKSI_CSV)
    for r in rows:
        if r.get("id") == str(transaksi_id):
            try:
                tid = int(r.get("id") or 0)
            except:
                tid = r.get("id")
            try:
                uid = int(r.get("user_id") or 0)
            except:
                uid = r.get("user_id")
            try:
                meja = int(r.get("nomor_meja") or 0)
            except:
                meja = r.get("nomor_meja")
            try:
                total = float(r.get("total") or 0.0)
            except:
                total = 0.0
            try:
                subtotal = float(r.get("subtotal") or 0.0)
            except:
                subtotal = 0.0
            try:
                item_disc = float(r.get("item_discount") or 0.0)
            except:
                item_disc = 0.0
            try:
                promo_disc = float(r.get("promo_discount") or 0.0)
            except:
                promo_disc = 0.0
            
            return (tid, uid, meja, total, r.get("status"), r.get("promo_code"), subtotal, item_disc, promo_disc, r.get("tanggal"))
    return None


def transaksi_update_status(transaksi_id, new_status):
    """Update status transaksi"""
    rows = read_all(TRANSAKSI_CSV)
    found = False
    for r in rows:
        if r.get("id") == str(transaksi_id):
            r["status"] = new_status
            found = True
            break
    
    if found:
        write_all(TRANSAKSI_CSV, ["id", "user_id", "nomor_meja", "total", "status", "promo_code", "subtotal", "item_discount", "promo_discount", "tanggal"], rows)
        return True
    return False

# ========================================
# TAMBAHAN FUNCTIONS - MEJA MODULE
# ========================================

def meja_get_all_with_transaksi():
    """Ambil semua meja dengan detail transaksi terkait"""
    rows = read_all(MEJA_CSV)
    out = []
    
    for r in rows:
        nomor = int(r.get("nomor_meja", 0))
        status = r.get("status", "kosong")
        transaksi_id = r.get("transaksi_id", "")
        
        transaksi_info = None
        if transaksi_id:
            try:
                transaksi_data = transaksi_get(int(transaksi_id))
                if transaksi_data:
                    tid, uid, meja_num, total, trx_status, promo, subtotal, item_disc, promo_disc, tanggal = transaksi_data
                    transaksi_info = {
                        'id': tid,
                        'total': total,
                        'status': trx_status,
                        'tanggal': tanggal
                    }
            except:
                pass
        
        out.append({
            'nomor': nomor,
            'status': status,
            'transaksi_id': transaksi_id,
            'transaksi_info': transaksi_info
        })
    
    return sorted(out, key=lambda x: x['nomor'])


def meja_get_status_summary():
    """Dapatkan ringkasan status meja"""
    all_meja = meja_get_all_with_transaksi()
    
    summary = {
        'total': len(all_meja),
        'kosong': sum(1 for m in all_meja if m['status'] == 'kosong'),
        'terisi': sum(1 for m in all_meja if m['status'] == 'terisi'),
        'meja_list': all_meja
    }
    
    return summary


def meja_reset_all_kosong():
    """Reset semua meja menjadi kosong (untuk admin/maintenance)"""
    rows = read_all(MEJA_CSV)
    for r in rows:
        r['status'] = 'kosong'
        r['transaksi_id'] = ''
    
    write_all(MEJA_CSV, ["nomor_meja", "status", "transaksi_id"], rows)
    return True


# ========================================
# TAMBAHAN FUNCTIONS - PAYMENT MODULE
# ========================================

def pembayaran_list_by_date_range(start_date, end_date, status_filter=None):
    """Ambil pembayaran dalam rentang tanggal"""
    from datetime import datetime
    
    rows = read_all(PEMBAYARAN_CSV)
    out = []
    
    for r in rows:
        try:
            payment_date = datetime.strptime(r.get("tanggal_bayar"), "%Y-%m-%d %H:%M:%S")
        except:
            continue
        
        if payment_date < start_date or payment_date > end_date:
            continue
        
        if status_filter and r.get("status_pembayaran") != status_filter:
            continue
        
        try:
            pid = int(r.get("id", 0))
            tid = int(r.get("transaksi_id", 0))
            jumlah = float(r.get("jumlah_bayar", 0.0))
        except:
            continue
        
        out.append((
            pid,
            tid,
            r.get("metode_pembayaran"),
            jumlah,
            r.get("status_pembayaran"),
            r.get("tanggal_bayar")
        ))
    
    return sorted(out, key=lambda x: x[5], reverse=True)


def pembayaran_get_summary(start_date, end_date, status_filter=None, method_filter=None):
    """Hitung ringkasan pembayaran - DENGAN FILTER METODE"""
    
    payments = pembayaran_list_by_date_range(start_date, end_date, 
                                             status_filter=status_filter)
    
    total_income = 0
    total_count = 0
    
    metode_breakdown = {}
    
    for p in payments:
        pid, tid, metode, jumlah, status, tanggal = p
        
        # FILTER BY METODE
        if method_filter and method_filter != 'semua':
            # Normalize metode untuk comparison
            metode_clean = metode.lower().replace(' ', '')
            filter_clean = method_filter.lower().replace(' ', '')
            
            if metode_clean != filter_clean:
                continue  # Skip jika tidak sesuai filter
        
        total_income += jumlah
        total_count += 1
        
        metode = p[2]
        metode_breakdown[metode] = metode_breakdown.get(metode, 0) + 1
    
    return {
        'total_income': total_income,
        'total_count': total_count,
        'avg_per_transaction': total_income / total_count if total_count > 0 else 0,
        'metode_breakdown': metode_breakdown,
        'payments': payments
    }


def pembayaran_validate_cash(jumlah_bayar, total_transaksi):
    """Validasi pembayaran cash"""
    if jumlah_bayar < total_transaksi:
        return False, f"Uang kurang! Kurang: Rp {total_transaksi - jumlah_bayar:,.0f}"
    return True, "Valid"


def pembayaran_calculate_change(jumlah_bayar, total_transaksi):
    """Hitung kembalian"""
    if jumlah_bayar < total_transaksi:
        return None
    return jumlah_bayar - total_transaksi


# ========================================
# TAMBAHAN FUNCTIONS - REPORT MODULE
# ========================================

def report_get_daily_summary(target_date=None, method_filter=None):
    """Dapatkan ringkasan penjualan harian - DENGAN FILTER METODE"""
    from datetime import datetime
    
    if target_date is None:
        target_date = datetime.now().date()
    
    start_datetime = datetime.combine(target_date, datetime.min.time())
    end_datetime = datetime.combine(target_date, datetime.max.time())
    
    # PASS METHOD_FILTER KE FUNCTION INI
    summary = pembayaran_get_summary(
        start_datetime, 
        end_datetime,
        status_filter='sukses',
        method_filter=method_filter  # ← TAMBAH INI
    )
    
    return {
        'tanggal': target_date.strftime("%Y-%m-%d"),
        'total_transaksi': summary['total_count'],
        'total_pendapatan': summary['total_income'],
        'rata_rata': summary['avg_per_transaction'],
        'metode_breakdown': summary['metode_breakdown'],
        'details': summary['payments']
    }


def report_get_weekly_summary(week_start=None, method_filter=None):
    """Dapatkan ringkasan penjualan mingguan - DENGAN FILTER METODE"""
    from datetime import datetime, timedelta
    
    if week_start is None:
        today = datetime.now()
        week_start = today - timedelta(days=today.weekday())
    
    week_start_dt = datetime.combine(week_start.date(), datetime.min.time())
    week_end_dt = week_start_dt + timedelta(days=7)
    
    # PASS METHOD_FILTER
    summary = pembayaran_get_summary(
        week_start_dt, 
        week_end_dt,
        status_filter='sukses',
        method_filter=method_filter  # ← TAMBAH INI
    )
    
    return {
        'minggu_mulai': week_start.strftime("%Y-%m-%d"),
        'total_transaksi': summary['total_count'],
        'total_pendapatan': summary['total_income'],
        'rata_rata': summary['avg_per_transaction'],
        'metode_breakdown': summary['metode_breakdown'],
        'details': summary['payments']
    }


def report_get_monthly_summary(year=None, month=None, method_filter=None):
    """Dapatkan ringkasan penjualan bulanan - DENGAN FILTER METODE"""
    from datetime import datetime, date
    import calendar
    
    if year is None:
        year = datetime.now().year
    if month is None:
        month = datetime.now().month
    
    first_day = date(year, month, 1)
    last_day = date(year, month, calendar.monthrange(year, month)[1])
    
    start_datetime = datetime.combine(first_day, datetime.min.time())
    end_datetime = datetime.combine(last_day, datetime.max.time())
    
    # PASS METHOD_FILTER
    summary = pembayaran_get_summary(
        start_datetime, 
        end_datetime,
        status_filter='sukses',
        method_filter=method_filter  # ← TAMBAH INI
    )
    
    return {
        'bulan': f"{first_day.strftime('%B %Y')}",
        'total_transaksi': summary['total_count'],
        'total_pendapatan': summary['total_income'],
        'rata_rata': summary['avg_per_transaction'],
        'metode_breakdown': summary['metode_breakdown'],
        'details': summary['payments']
    }


def report_export_to_text(report_data, report_type='daily'):
    """Export laporan ke format text"""
    text = "=" * 70 + "\n"
    text += "LAPORAN PENJUALAN CAFÉ TOTORO \n"
    text += "=" * 70 + "\n\n"
    
    if report_type == 'daily':
        text += f"PERIODE: {report_data['tanggal']}\n"
        text += f"TIPE: Laporan Harian\n"
    elif report_type == 'weekly':
        text += f"PERIODE: Minggu dimulai {report_data['minggu_mulai']}\n"
        text += f"TIPE: Laporan Mingguan\n"
    else:
        text += f"PERIODE: {report_data.get('bulan', 'Unknown')}\n"
        text += f"TIPE: Laporan Bulanan\n"
    
    text += "=" * 70 + "\n\n"
    
    text += "RINGKASAN PENJUALAN:\n"
    text += "-" * 70 + "\n"
    text += f"Total Transaksi  : {report_data['total_transaksi']} transaksi\n"
    text += f"Total Pendapatan : Rp {report_data['total_pendapatan']:,.2f}\n"
    text += f"Rata-rata        : Rp {report_data['rata_rata']:,.2f} per transaksi\n"
    text += "\n"
    
    text += "BREAKDOWN METODE PEMBAYARAN:\n"
    text += "-" * 70 + "\n"
    for metode, count in sorted(report_data['metode_breakdown'].items()):
        percentage = (count / report_data['total_transaksi'] * 100) if report_data['total_transaksi'] > 0 else 0
        text += f"{metode.upper().ljust(20)} : {str(count).rjust(3)} transaksi ({percentage:.1f}%)\n"
    
    text += "=" * 70 + "\n"
    
    return text


# ========================================
# HELPER FUNCTIONS - FORMATTING
# ========================================

def format_currency(amount):
    """Format nilai menjadi Rupiah"""
    return f"Rp {amount:,.0f}"


def format_datetime(dt_string):
    """Format datetime string menjadi readable"""
    try:
        from datetime import datetime
        dt = datetime.strptime(dt_string, "%Y-%m-%d %H:%M:%S")
        return dt.strftime("%d/%m/%Y %H:%M")
    except:
        return dt_string


def validate_meja_number(nomor_meja):
    """Validasi nomor meja"""
    try:
        nomor = int(nomor_meja)
        if nomor < 1 or nomor > 99:
            return False, "Nomor meja harus 1-99"
        return True, "Valid"
    except:
        return False, "Nomor meja harus berupa angka"

def detail_transaksi_list(transaksi_id):
    """Ambil semua detail item dari transaksi tertentu"""
    rows = read_all(DETAIL_TRANSAKSI_CSV)
    out = []
    for r in rows:
        if r.get("transaksi_id") == str(transaksi_id):
            try:
                did = int(r.get("id") or 0)
            except:
                did = r.get("id")
            try:
                mid = int(r.get("menu_id") or 0)
            except:
                mid = r.get("menu_id")
            try:
                qty = int(r.get("qty") or 0)
            except:
                qty = 0
            try:
                harga = float(r.get("harga_satuan") or 0.0)
            except:
                harga = 0.0
            try:
                subtotal = float(r.get("subtotal_item") or 0.0)
            except:
                subtotal = 0.0
            
            out.append((did, mid, qty, harga, subtotal))
    
    return out













# 19 juta lapangan badmin

# Buat logika diskon + promok


def apply_discounts_and_promo(cart_items, promo_code=None):
    subtotal = 0.0
    item_discount_total = 0.0
    menu_rows = read_all(MENU_CSV)
    menu_dict = {r["id"]: r for r in menu_rows}

    for it in cart_items:
        mid = str(it.get('menu_id'))
        r = menu_dict.get(mid)
        if not r:
            continue
        try:
            price = float(r.get("harga") or 0.0)
        except:
            price = 0.0
        try:
            item_disc_pct = float(r.get("item_discount_pct") or 0.0)
        except:
            item_disc_pct = 0.0
        qty = int(it.get('qty', 1))
        line = price * qty
        subtotal += line
        if item_disc_pct and item_disc_pct > 0:
            item_discount_total += (price * qty) * (item_disc_pct / 100.0)

    promo_discount = 0.0
    promo_applied = None
    if promo_code:
        p = promo_get(promo_code)
        if p:
            _, ptype, val, min_total = p
            if subtotal >= (min_total or 0.0):
                if ptype == 'percent':
                    promo_discount = (subtotal - item_discount_total) * (val / 100.0)
                else:
                    promo_discount = val
                promo_applied = promo_code

    total = subtotal - item_discount_total - promo_discount
    if total < 0:
        total = 0.0

    return {
        'subtotal': round(subtotal, 2),
        'item_discount': round(item_discount_total, 2),
        'promo_code': promo_applied,
        'promo_discount': round(promo_discount, 2),
        'total': round(total, 2)
    }



# === FUNGSI FAVORITE ===

def favorite_update(user_id, menu_id):
    """Update atau tambah favorite count untuk user tertentu"""
    from datetime import datetime
    
    rows = read_all(FAVORITE_CSV)
    found = False
    
    for r in rows:
        if r.get("user_id") == str(user_id) and r.get("menu_id") == str(menu_id):
            try:
                count = int(r.get("order_count") or 0)
            except:
                count = 0
            r["order_count"] = str(count + 1)
            r["last_ordered"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            found = True
            break
    
    if not found:
        rows.append({
            "user_id": str(user_id),
            "menu_id": str(menu_id),
            "order_count": "1",
            "last_ordered": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        })
    
    write_all(FAVORITE_CSV, ["user_id", "menu_id", "order_count", "last_ordered"], rows)


def favorite_list(user_id, limit=5):
    """Ambil menu favorit user, sorted by order_count descending"""
    rows = read_all(FAVORITE_CSV)
    out = []
    
    for r in rows:
        if r.get("user_id") == str(user_id):
            try:
                mid = int(r.get("menu_id") or 0)
            except:
                mid = r.get("menu_id")
            try:
                count = int(r.get("order_count") or 0)
            except:
                count = 0
            
            last_ordered = r.get("last_ordered")
            out.append((mid, count, last_ordered))
    
    out.sort(key=lambda x: x[1], reverse=True)
    
    if limit:
        out = out[:limit]
    
    return out


# === FUNGSI PEMBAYARAN ===

def pembayaran_add(transaksi_id, metode_pembayaran, jumlah_bayar, status_pembayaran='sukses', struk=''):
    """Simpan data pembayaran baru"""
    from datetime import datetime
    
    rows = read_all(PEMBAYARAN_CSV)
    new_id = next_int_id(rows, "id")
    tanggal_bayar = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    
    rows.append({
        "id": new_id,
        "transaksi_id": str(transaksi_id),
        "metode_pembayaran": metode_pembayaran,
        "jumlah_bayar": str(float(jumlah_bayar)),
        "status_pembayaran": status_pembayaran,
        "tanggal_bayar": tanggal_bayar,
        "struk": struk
    })
    
    write_all(PEMBAYARAN_CSV, ["id", "transaksi_id", "metode_pembayaran", "jumlah_bayar", "status_pembayaran", "tanggal_bayar", "struk"], rows)
    return new_id


def pembayaran_get_by_transaksi(transaksi_id):
    """Ambil data pembayaran berdasarkan transaksi_id"""
    rows = read_all(PEMBAYARAN_CSV)
    for r in rows:
        if r.get("transaksi_id") == str(transaksi_id):
            try:
                pid = int(r.get("id") or 0)
            except:
                pid = r.get("id")
            try:
                jumlah = float(r.get("jumlah_bayar") or 0.0)
            except:
                jumlah = 0.0
            
            return (pid, r.get("metode_pembayaran"), jumlah, r.get("status_pembayaran"), r.get("tanggal_bayar"), r.get("struk"))
    return None


# === FUNGSI MEJA ===

def meja_update_status(nomor_meja, new_status, transaksi_id=""):
    """Update status meja (kosong/terisi) dan link ke transaksi"""
    rows = read_all(MEJA_CSV)
    found = False
    
    for r in rows:
        if r.get("nomor_meja") == str(nomor_meja):
            r["status"] = new_status
            r["transaksi_id"] = str(transaksi_id) if transaksi_id else ""
            found = True
            break
    
    if found:
        write_all(MEJA_CSV, ["nomor_meja", "status", "transaksi_id"], rows)
        return True
    return False


def meja_tutup(nomor_meja):
    """Tutup meja (set ke kosong)"""
    return meja_update_status(nomor_meja, "kosong", "")

def meja_get(nomor_meja):
    """Ambil data meja berdasarkan nomor"""
    rows = read_all(MEJA_CSV)
    for r in rows:
        if r.get("nomor_meja") == str(nomor_meja):
            try:
                nomor = int(r.get("nomor_meja") or 0)
            except:
                nomor = r.get("nomor_meja")
            
            transaksi_id = r.get("transaksi_id") or ""
            return (nomor, r.get("status"), transaksi_id)
    return None


# Wilayah dikuasai UI



class App:
    def __init__(self, root):
        self.root = root
        self.root.title("Totoro Cafe")
        self.session = None
        self.img_cache = {}
        self.setup_ui()

    def create_hover_button(self, parent, text, command, style='primary', width=None):
        """Helper untuk membuat button dengan hover effect"""
        
        bg_colors = {
            'primary': (COLORS['primary'], COLORS['primary_dark']),
            'success': (COLORS['success'], COLORS['primary_dark']),
            'danger': (COLORS['danger'], '#B85555'),
            'brown': (COLORS['brown'], '#6B5D48')
        }
        
        bg_normal, bg_hover = bg_colors.get(style, bg_colors['primary'])
        
        btn = tk.Button(
            parent,
            text=text,
            command=command,
            bg=bg_normal,
            fg='white',
            font=FONTS['button'],
            relief='flat',
            cursor='hand2',
            borderwidth=0,
            padx=20,
            pady=10
        )
        
        if width:
            btn.config(width=width)
        
        def on_enter(e):
            btn['background'] = bg_hover
        
        def on_leave(e):
            btn['background'] = bg_normal
        
        btn.bind('<Enter>', on_enter)
        btn.bind('<Leave>', on_leave)
        
        return btn


    def setup_ui(self):
        # UI STYLING - Window setup dengan tema Totoro
        self.root.geometry("1200x750")
        self.root.resizable(True, True)  # Bisa maximize
        self.root.configure(bg=COLORS['bg_main'])
        
        # UI STYLING - Custom styles untuk tema Totoro
        style = ttk.Style()
        style.theme_use('clam')
        
        # Background colors
        style.configure(".", background=COLORS['bg_main'], foreground=COLORS['text_dark'])
        
        # Frame styles
        style.configure("TFrame", background=COLORS['bg_main'])
        style.configure("TLabelframe", background=COLORS['bg_main'], bordercolor=COLORS['border'])
        style.configure("TLabelframe.Label", background=COLORS['bg_main'], foreground=COLORS['brown'], font=("Arial", 10, "bold"))
        
        # Button styles
        style.configure("TButton", 
                        background=COLORS['primary'], 
                        foreground="white",
                        borderwidth=0,
                        focuscolor='none',
                        font=("Arial", 10),
                        padding=10)
        style.map("TButton",
                  background=[('active', COLORS['primary_dark']), ('pressed', COLORS['primary_dark'])],
                  foreground=[('active', 'white')])
        
        style.configure("Accent.TButton", 
                        background=COLORS['brown'], 
                        foreground="white",
                        font=("Arial", 11, "bold"),
                        padding=12)
        style.map("Accent.TButton",
                  background=[('active', '#6B5D48'), ('pressed', '#6B5D48')])
        
        # Label styles
        style.configure("TLabel", background=COLORS['bg_main'], foreground=COLORS['text_dark'], font=("Arial", 10))
        
        # Entry styles
        style.configure("TEntry", fieldbackground="white", bordercolor=COLORS['border'])
        
        # Treeview styles
        style.configure("Treeview", 
                        background="white",
                        foreground=COLORS['text_dark'],
                        fieldbackground="white",
                        borderwidth=0,
                        font=("Arial", 9))
        style.configure("Treeview.Heading", 
                        background=COLORS['primary'],
                        foreground="white",
                        font=("Arial", 10, "bold"))
        style.map("Treeview.Heading",
                  background=[('active', COLORS['primary_dark'])])
        
        # Notebook (tabs) styles
        style.configure("TNotebook", background=COLORS['bg_main'], borderwidth=0)
        style.configure("TNotebook.Tab", 
                        background=COLORS['accent'],
                        foreground=COLORS['text_dark'],
                        padding=[20, 10],
                        font=("Arial", 10))
        style.map("TNotebook.Tab",
                  background=[('selected', COLORS['primary'])],
                  foreground=[('selected', 'white')],
                  expand=[('selected', [1, 1, 1, 0])])
        
        self.login_frame()

    def login_frame(self):
        for w in self.root.winfo_children():
            w.destroy()
        
        # ========================================
        # TOTORO LOGIN - LANDSCAPE LAYOUT (FIXED)
        # ========================================
        
        # Main container dengan background
        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.pack()
        
        # ========================================
        # CONTAINER HORIZONTAL (2 PANEL)
        # ========================================
        container = tk.Frame(card, bg='white')
        container.pack()
        
        # ========================================
        # LEFT PANEL - TOTORO ILLUSTRATION
        # ========================================
        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)
        
        # 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 = 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))
        
        tk.Label(
            right_panel,
            text="Café Management System",
            font=('Segoe UI', 9),
            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)
        
        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
        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
        )
        login_btn.grid(row=10, column=0, pady=(10, 15), padx=5)
        
        def on_enter(e):
            login_btn['background'] = COLORS['primary_dark']
        def on_leave(e):
            login_btn['background'] = COLORS['primary']
        
        login_btn.bind("<Enter>", on_enter)
        login_btn.bind("<Leave>", on_leave)
        
        # 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))



    def handle_login(self):
        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
        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.session = user
        self.dashboard_frame()


    def logout(self):
        self.session = None
        self.img_cache.clear()
        self.notification_running = False
        self.login_frame()

    def dashboard_frame(self):
        for w in self.root.winfo_children():
            w.destroy()
        
        # ========================================
        # DASHBOARD HEADER - IMPROVED
        # ========================================
        top = tk.Frame(self.root, bg=COLORS['primary'], height=70)
        top.pack(fill='x')
        
        # Left side - Logo + Title
        left_side = tk.Frame(top, bg=COLORS['primary'])
        left_side.pack(side='left', padx=20, pady=10)
        
        # Logo
        try:
            totoro_header = Image.open("img/totoro_logo.png")
            totoro_header = totoro_header.resize((45, 45), Image.Resampling.LANCZOS)
            totoro_header_photo = ImageTk.PhotoImage(totoro_header)
            
            self.totoro_header_logo = totoro_header_photo
            
            tk.Label(left_side, image=totoro_header_photo, bg=COLORS['primary']).pack(side='left', padx=(0, 10))
        except:
            tk.Label(left_side, text="🌿", font=('Arial', 28), bg=COLORS['primary']).pack(side='left', padx=(0, 10))
        
        # Title + Subtitle
        title_frame = tk.Frame(left_side, bg=COLORS['primary'])
        title_frame.pack(side='left')
        
        tk.Label(
            title_frame,
            text="TOTORO CAFÉ",
            font=('Comic Sans MS', 16, 'bold'),
            bg=COLORS['primary'],
            fg='white'
        ).pack(anchor='w')
        
        tk.Label(
            title_frame,
            text="Your Cozy Corner for Every Order",
            font=FONTS['small'],
            bg=COLORS['primary'],
            fg='white'
        ).pack(anchor='w')
        
        # Center - User info (IMPROVED)
        center_info = tk.Frame(top, bg=COLORS['primary'])
        center_info.pack(side='left', expand=True, padx=20)
        
        if self.session['role'] in ['pembeli', 'user'] and 'customer_name' in self.session:
            user_text = f"👤 {self.session['customer_name']}"
            role_text = f"🪑 Meja {self.session.get('nomor_meja', '-')} • {self.session['role'].title()}"
        else:
            user_text = f"👤 {self.session['username']}"
            role_text = f"🎭 Role: {self.session['role'].title()}"
        
        tk.Label(
            center_info,
            text=user_text,
            font=FONTS['subheader'],
            bg=COLORS['primary'],
            fg='white'
        ).pack()
        
        tk.Label(
            center_info,
            text=role_text,
            font=FONTS['small'],
            bg=COLORS['primary'],
            fg='white'
        ).pack()
        
        # Right side - Logout button dengan shadow
        right_side = tk.Frame(top, bg=COLORS['primary'])
        right_side.pack(side='right', padx=20, pady=10)
        
        logout_btn = tk.Button(
            right_side,
            text="LOGOUT",
            command=self.logout,
            bg=COLORS['brown'],
            fg='white',
            font=FONTS['button'],
            relief='flat',
            cursor='hand2',
            padx=25,
            pady=10,
            borderwidth=0
        )
        logout_btn.pack()
        
        # Hover effect
        def on_enter(e):
            logout_btn['background'] = '#6B5D48'
        def on_leave(e):
            logout_btn['background'] = COLORS['brown']
        logout_btn.bind("<Enter>", on_enter)
        logout_btn.bind("<Leave>", on_leave)
        
        # Tab container dengan styling
        tab_container = tk.Frame(self.root, bg=COLORS['bg_main'])
        tab_container.pack(fill='both', expand=True, padx=15, pady=10)
        
        main = ttk.Notebook(tab_container)
        main.pack(fill='both', expand=True)
        

        self.tab_menu_manage = ttk.Frame(main)
        self.tab_menu_view = ttk.Frame(main)
        self.tab_promo = ttk.Frame(main)
        self.tab_order = ttk.Frame(main)
        self.tab_waiter = ttk.Frame(main)
        self.tab_favorite = ttk.Frame(main)
        
        # ========================================
        # KONDISI TAB BERDASARKAN ROLE 
        # ========================================

        # Tab untuk SEMUA role
        main.add(self.tab_menu_view, text="📖 Menu - View")

        # ==========================================
        # ROLE: PEMBELI / USER
        # ==========================================
        if self.session['role'] in ['pembeli', 'user']:
            main.add(self.tab_order, text="🛒 Order Menu")
            main.add(self.tab_favorite, text="⭐ Favorit")

        # ==========================================
        # ROLE: WAITER
        # ==========================================
        if self.session['role'] == 'waiter':
            main.add(self.tab_waiter, text="🍽️ Kelola Pesanan")

            self.tab_meja = ttk.Frame(main)
            main.add(self.tab_meja, text="🪑 Kelola Meja")

        # ==========================================
        # ROLE: KASIR (Order + Transaksi SAJA)
        # ==========================================
        if self.session['role'] == 'kasir':
            self.tab_payment = ttk.Frame(main)
            main.add(self.tab_payment, text="💰 Transaksi")

            self.tab_meja = ttk.Frame(main)
            main.add(self.tab_meja, text="🪑 Kelola Meja")

            self.tab_report = ttk.Frame(main)
            main.add(self.tab_report, text="📊 Laporan")

        # ==========================================
        # ROLE: PEMILIK (Laporan SAJA)
        # ==========================================
        if self.session['role'] == 'pemilik':
            self.tab_report = ttk.Frame(main)
            main.add(self.tab_report, text="📊 Laporan")


        # ==========================================
        # ROLE: ADMIN (Full Setup + Monitoring Read-Only)
        # ==========================================
        if self.session['role'] == 'admin':
            
            # FULL ACCESS (CRUD)
            main.add(self.tab_menu_manage, text="⚙️ Kelola Menu")
            main.add(self.tab_promo, text="🎁 Kelola Promo")
            
            self.tab_user_manage = ttk.Frame(main)
            main.add(self.tab_user_manage, text="👥 Kelola User")
            
            self.tab_meja = ttk.Frame(main)
            main.add(self.tab_meja, text="🪑 Kelola Meja")
            
            # READ-ONLY MONITORING
            main.add(self.tab_waiter, text="👁️ Monitor Pesanan")  # ← READ-ONLY
            
            self.tab_payment = ttk.Frame(main)
            main.add(self.tab_payment, text="👁️ Monitor Transaksi")  # ← READ-ONLY
            
            self.tab_report = ttk.Frame(main)
            main.add(self.tab_report, text="📊 Laporan")


        # ========================================
        # BUILD TAB BERDASARKAN ROLE
        # ========================================

        # Menu View - untuk SEMUA role
        self.build_menu_view_tab(self.tab_menu_view)

        # Pembeli/User
        if self.session['role'] in ['pembeli', 'user']:
            self.build_order_tab(self.tab_order)
            self.build_favorite_tab(self.tab_favorite)

        # Waiter
        if self.session['role'] == 'waiter':
            self.build_waiter_tab(self.tab_waiter)
            self.build_meja_tab(self.tab_meja)

        # Kasir
        if self.session['role'] == 'kasir':
            self.build_order_tab(self.tab_order)
            self.build_payment_tab(self.tab_payment)
            self.build_meja_tab(self.tab_meja)
            self.build_report_tab(self.tab_report)

        # Pemilik
        if self.session['role'] == 'pemilik':
            self.build_report_tab(self.tab_report)

        # Admin - PERBAIKAN DI SINI!
        if self.session['role'] == 'admin':
            # Master Data (Full CRUD)
            self.build_menu_manage_tab(self.tab_menu_manage)
            self.build_promo_tab(self.tab_promo)
            self.build_user_manage_tab(self.tab_user_manage)
            self.build_meja_tab(self.tab_meja)
            
            # Monitoring (Read-Only)
            self.build_waiter_tab(self.tab_waiter, readonly=True)     
            self.build_payment_tab(self.tab_payment, readonly=True)    
            self.build_report_tab(self.tab_report)                     




    def build_menu_view_tab(self, parent):
        for w in parent.winfo_children():
            w.destroy()
        left = ttk.Frame(parent, width=600)
        right = ttk.Frame(parent, width=380)
        left.pack(side='left', fill='both', expand=True, padx=6, pady=6)
        right.pack(side='right', fill='y', padx=6, pady=6)

        filter_frame = ttk.Frame(left)
        filter_frame.pack(fill='x', pady=6)
        ttk.Label(filter_frame, text="Cari / Nama atau Kategori:").pack(side='left', padx=3)
        self.view_search_var = tk.StringVar()
        ttk.Entry(filter_frame, textvariable=self.view_search_var, width=30).pack(side='left', padx=3)
        ttk.Button(filter_frame, text="Cari", command=self.reload_view_table).pack(side='left', padx=3)
        ttk.Button(filter_frame, text="Reset", command=self.reset_view_filters).pack(side='left', padx=3)
        ttk.Button(filter_frame, text="Hanya Tersedia", command=lambda: self.reload_view_table(available_only=True)).pack(side='left', padx=6)

        cols = ("ID","Nama","Kategori","Harga","Stok","Tersedia","ItemDisc%")
        self.view_tree = ttk.Treeview(left, columns=cols, show='headings', height=18)
        for c in cols:
            self.view_tree.heading(c, text=c)
            self.view_tree.column(c, width=90 if c!="Nama" else 200)
        self.view_tree.pack(fill='both', expand=True)
        self.view_tree.bind("<<TreeviewSelect>>", self.on_view_select)

        ttk.Label(right, text="Preview Item", font=("Arial", 12, "bold")).pack(pady=6)
        self.preview_label = ttk.Label(right, text="Pilih menu di kiri")
        self.preview_label.pack()
        self.preview_img_label = ttk.Label(right)
        self.preview_img_label.pack(pady=6)
        self.preview_detail = tk.Text(right, width=45, height=12)
        self.preview_detail.pack()

        self.reload_view_table()

    def reload_view_table(self, available_only=False):
        s = self.view_search_var.get().strip() if hasattr(self, 'view_search_var') else ""
        results = menu_list(search_text=s or None, available_only=available_only)
        for r in self.view_tree.get_children():
            self.view_tree.delete(r)
        for row in results:
            mid,nama,kategori,harga,stok,foto,tersedia,item_disc = row
            self.view_tree.insert("", tk.END, values=(mid,nama,kategori,harga,stok, "Yes" if tersedia else "No", item_disc))

    def reset_view_filters(self):
        self.view_search_var.set("")
        self.reload_view_table()

    def on_view_select(self, event):
        sel = self.view_tree.selection()
        if not sel:
            return
        item = self.view_tree.item(sel)['values']
        menu_id = item[0]
        data = menu_get(menu_id)
        if not data:
            return
        mid,nama,kategori,harga,stok,foto,tersedia,item_disc = data
        self.preview_detail.delete('1.0', tk.END)
        txt = f"ID: {mid}\nNama: {nama}\nKategori: {kategori}\nHarga: {harga}\nStok: {stok}\nTersedia: {'Yes' if tersedia else 'No'}\nItem Discount: {item_disc}%\nFoto path: {foto}\n"
        self.preview_detail.insert(tk.END, txt)
        if foto and os.path.exists(foto):
            try:
                img = Image.open(foto)
                img.thumbnail(IMG_PREVIEW_SIZE)
                tkimg = ImageTk.PhotoImage(img)
                self.img_cache['preview'] = tkimg
                self.preview_img_label.config(image=tkimg)
            except Exception as e:
                self.preview_img_label.config(image='')
        else:
            self.preview_img_label.config(image='')

    def build_menu_manage_tab(self, parent):
        for w in parent.winfo_children():
            w.destroy()
        topfrm = ttk.Frame(parent)
        topfrm.pack(fill='x', padx=6, pady=6)
        ttk.Label(topfrm, text="Kelola Menu", font=("Arial", 14, "bold")).pack(side='left')
        ttk.Button(topfrm, text="Tambah Menu", command=self.open_add_menu_window).pack(side='right', padx=6)
        cols = ("ID","Nama","Kategori","Harga","Stok","Tersedia","ItemDisc%")
        self.manage_tree = ttk.Treeview(parent, columns=cols, show='headings', height=18)
        for c in cols:
            self.manage_tree.heading(c, text=c)
            self.manage_tree.column(c, width=100 if c!="Nama" else 220)
        self.manage_tree.pack(fill='both', padx=6, pady=6)
        btnfrm = ttk.Frame(parent)
        btnfrm.pack(pady=6)
        ttk.Button(btnfrm, text="Edit Terpilih", command=self.open_edit_menu_window).pack(side='left', padx=6)
        ttk.Button(btnfrm, text="Hapus Terpilih", command=self.delete_selected_menu).pack(side='left', padx=6)
        ttk.Button(btnfrm, text="Reload", command=self.reload_manage_table).pack(side='left', padx=6)
        self.reload_manage_table()

    def reload_manage_table(self):
        for r in self.manage_tree.get_children():
            self.manage_tree.delete(r)
        rows = menu_list()
        for row in rows:
            mid,nama,kategori,harga,stok,foto,tersedia,item_disc = row
            self.manage_tree.insert("", tk.END, values=(mid,nama,kategori,harga,stok,"Yes" if tersedia else "No", item_disc))

    def open_add_menu_window(self):
        w = tk.Toplevel(self.root)
        w.title("Tambah Menu")
        frm = ttk.Frame(w,padding=10)
        frm.pack()
        labels = ["Nama","Kategori","Harga","Stok","Foto path","Item Discount (%)"]
        vars = {}
        for i,lab in enumerate(labels):
            ttk.Label(frm, text=lab).grid(row=i, column=0, sticky='e', pady=4)
            vars[lab] = tk.StringVar()
            ttk.Entry(frm, textvariable=vars[lab], width=40).grid(row=i, column=1, pady=4)
        ttk.Button(frm, text="Pilih Foto", command=lambda: self.select_file(vars["Foto path"])).grid(row=4, column=2, padx=6)
        def save():
            try:
                nama = vars["Nama"].get().strip()
                kategori = vars["Kategori"].get().strip()
                harga = float(vars["Harga"].get())
                stok = int(vars["Stok"].get())
                foto = vars["Foto path"].get().strip() or None
                item_disc = float(vars["Item Discount (%)"].get() or 0)
            except Exception as e:
                messagebox.showerror("Input error", "Periksa kembali input (Harga/Stok harus angka)")
                return
            menu_add(nama,kategori,harga,stok,foto,item_disc)
            messagebox.showinfo("Sukses","Menu ditambahkan")
            w.destroy()
            self.reload_manage_table()
            self.reload_view_table()
        ttk.Button(frm, text="Simpan", command=save).grid(row=len(labels), column=1, pady=8)

    def open_edit_menu_window(self):
        sel = self.manage_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih", "Pilih menu terlebih dahulu")
            return
        item = self.manage_tree.item(sel)['values']
        menu_id = item[0]
        data = menu_get(menu_id)
        if not data:
            messagebox.showerror("Error", "Data menu tidak ditemukan")
            return
        mid,nama,kategori,harga,stok,foto,tersedia,item_disc = data
        w = tk.Toplevel(self.root)
        w.title("Edit Menu")
        frm = ttk.Frame(w,padding=10)
        frm.pack()
        labels = ["Nama","Kategori","Harga","Stok","Foto path","Item Discount (%)"]
        vars = {}
        defaults = [nama,kategori,str(harga),str(stok),foto or "",str(item_disc or 0)]
        for i,lab in enumerate(labels):
            ttk.Label(frm, text=lab).grid(row=i, column=0, sticky='e', pady=4)
            vars[lab] = tk.StringVar(value=defaults[i])
            ttk.Entry(frm, textvariable=vars[lab], width=40).grid(row=i, column=1, pady=4)
        ttk.Button(frm, text="Pilih Foto", command=lambda: self.select_file(vars["Foto path"])).grid(row=4, column=2, padx=6)
        def save():
            try:
                nama = vars["Nama"].get().strip()
                kategori = vars["Kategori"].get().strip()
                harga = float(vars["Harga"].get())
                stok = int(vars["Stok"].get())
                foto = vars["Foto path"].get().strip() or None
                item_disc = float(vars["Item Discount (%)"].get() or 0)
            except:
                messagebox.showerror("Input error", "Periksa input")
                return
            menu_update(menu_id, nama, kategori, harga, stok, foto, item_disc)
            messagebox.showinfo("Sukses","Menu diperbarui")
            w.destroy()
            self.reload_manage_table()
            self.reload_view_table()
        ttk.Button(frm, text="Update", command=save).grid(row=len(labels), column=1, pady=8)

    def delete_selected_menu(self):
        sel = self.manage_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih", "Pilih menu untuk dihapus")
            return
        item = self.manage_tree.item(sel)['values']
        menu_id = item[0]
        if messagebox.askyesno("Konfirmasi", "Hapus menu terpilih?"):
            menu_delete(menu_id)
            messagebox.showinfo("Dihapus", "Menu berhasil dihapus")
            self.reload_manage_table()
            self.reload_view_table()

    def select_file(self, var):
        p = filedialog.askopenfilename(title="Pilih file gambar",
        filetypes=[("Image files","*.png;*.jpg;*.jpeg;*.gif;*.bmp"),("All files","*.*")])
        if p:
            var.set(p)

    def build_promo_tab(self, parent):
        for w in parent.winfo_children():
            w.destroy()
        top = ttk.Frame(parent)
        top.pack(fill='x', pady=6)
        ttk.Label(top, text="Promo Codes", font=("Arial", 14, "bold")).pack(side='left', padx=6)
        ttk.Button(top, text="Tambah Promo", command=self.open_add_promo).pack(side='right', padx=6)
        cols = ("Code","Type","Value","MinTotal")
        self.promo_tree = ttk.Treeview(parent, columns=cols, show='headings', height=12)
        for c in cols:
            self.promo_tree.heading(c, text=c)
            self.promo_tree.column(c, width=120)
        self.promo_tree.pack(fill='x', padx=6, pady=6)
        btnfrm = ttk.Frame(parent)
        btnfrm.pack(pady=6)
        ttk.Button(btnfrm, text="Edit Promo", command=self.open_edit_promo).pack(side='left', padx=6)
        ttk.Button(btnfrm, text="Hapus Promo", command=self.delete_selected_promo).pack(side='left', padx=6)
        ttk.Button(btnfrm, text="Reload", command=self.reload_promo_table).pack(side='left', padx=6)
        self.reload_promo_table()

    def reload_promo_table(self):
        for r in self.promo_tree.get_children():
            self.promo_tree.delete(r)
        for p in promo_list():
            self.promo_tree.insert("", tk.END, values=p)

    def open_add_promo(self):
        w = tk.Toplevel(self.root)
        w.title("Tambah Promo")
        w.geometry("350x230")
        w.transient(self.root)
        w.grab_set()

        frm = ttk.Frame(w, padding=15)
        frm.pack(fill="both", expand=True)

        vars = {
            'code': tk.StringVar(),
            'type': tk.StringVar(value='percent'),
            'value': tk.StringVar(),
            'min_total': tk.StringVar(value='0')
        }

        ttk.Label(frm, text="Code:").grid(row=0, column=0, sticky='e', pady=5)
        ttk.Entry(frm, textvariable=vars['code'], width=20).grid(row=0, column=1)

        ttk.Label(frm, text="Type (percent/fixed):").grid(row=1, column=0, sticky='e', pady=5)
        ttk.Entry(frm, textvariable=vars['type'], width=20).grid(row=1, column=1)

        ttk.Label(frm, text="Value:").grid(row=2, column=0, sticky='e', pady=5)
        ttk.Entry(frm, textvariable=vars['value'], width=20).grid(row=2, column=1)

        ttk.Label(frm, text="Min Total:").grid(row=3, column=0, sticky='e', pady=5)
        ttk.Entry(frm, textvariable=vars['min_total'], width=20).grid(row=3, column=1)

        def save():
            try:
                code = vars['code'].get().strip().upper()
                ptype = vars['type'].get().strip()
                val = float(vars['value'].get())
                mt = float(vars['min_total'].get() or 0)

                if ptype not in ('percent', 'fixed'):
                    raise ValueError("type harus 'percent' atau 'fixed'")
            except Exception as e:
                messagebox.showerror("Error", f"Input salah: {e}")
                return

            try:
                promo_add(code, ptype, val, mt)
                messagebox.showinfo("Sukses", "Promo ditambahkan")
                w.destroy()
                self.reload_promo_table()
            except Exception as e:
                messagebox.showerror("Error", f"Kode promo sudah ada atau error: {e}")

        ttk.Button(frm, text="Simpan", command=save).grid(row=4, column=1, pady=12)

    def open_edit_promo(self):
        sel = self.promo_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih", "Pilih promo untuk diedit")
            return

        code = self.promo_tree.item(sel)['values'][0]
        row = promo_get(code)
        if not row:
            messagebox.showerror("Error", "Promo tidak ditemukan")
            return

        code, ptype, val, min_total = row

        w = tk.Toplevel(self.root)
        w.title("Edit Promo")
        w.geometry("350x230")
        w.transient(self.root)
        w.grab_set()

        frm = ttk.Frame(w, padding=15)
        frm.pack(fill="both", expand=True)

        vars = {
            'type': tk.StringVar(value=ptype),
            'value': tk.StringVar(value=str(val)),
            'min_total': tk.StringVar(value=str(min_total))
        }

        ttk.Label(frm, text=f"Code: {code}", font=("Arial", 10, "bold")).grid(row=0, column=0, columnspan=2, pady=5)

        ttk.Label(frm, text="Type (percent/fixed):").grid(row=1, column=0, sticky='e', pady=5)
        ttk.Entry(frm, textvariable=vars['type'], width=20).grid(row=1, column=1)

        ttk.Label(frm, text="Value:").grid(row=2, column=0, sticky='e', pady=5)
        ttk.Entry(frm, textvariable=vars['value'], width=20).grid(row=2, column=1)

        ttk.Label(frm, text="Min Total:").grid(row=3, column=0, sticky='e', pady=5)
        ttk.Entry(frm, textvariable=vars['min_total'], width=20).grid(row=3, column=1)

        def save():
            try:
                ptype = vars['type'].get().strip()
                val = float(vars['value'].get())
                mt = float(vars['min_total'].get() or 0)

                if ptype not in ('percent', 'fixed'):
                    raise ValueError("type harus 'percent' atau 'fixed'")
            except Exception as e:
                messagebox.showerror("Error", f"Input salah: {e}")
                return

            promo_update(code, ptype, val, mt)
            messagebox.showinfo("Sukses", "Promo diperbarui")
            w.destroy()
            self.reload_promo_table()

        ttk.Button(frm, text="Update", command=save).grid(row=4, column=1, pady=12)

    def delete_selected_promo(self):
        sel = self.promo_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih", "Pilih promo")
            return
        code = self.promo_tree.item(sel)['values'][0]
        if messagebox.askyesno("Konfirmasi", f"Hapus promo {code}?"):
            promo_delete(code)
            messagebox.showinfo("Dihapus","Promo terhapus")
            self.reload_promo_table()
    
    def build_order_tab(self, parent):
        """Tab untuk order menu dengan tampilan card seperti GrabFood/Gojek"""
        for w in parent.winfo_children():
            w.destroy()
        
        # Split jadi 2 panel: kiri = menu cards, kanan = cart
        left = ttk.Frame(parent, width=600)
        right = ttk.Frame(parent, width=380)
        left.pack(side='left', fill='both', expand=True, padx=6, pady=6)
        right.pack(side='right', fill='both', padx=6, pady=6)
        
        # === PANEL KIRI: Daftar Menu dengan Card ===
        ttk.Label(left, text="Daftar Menu", font=("Arial", 12, "bold")).pack(pady=4)
        
        # Filter search
        search_frame = ttk.Frame(left)
        search_frame.pack(fill='x', pady=4)
        ttk.Label(search_frame, text="Cari:").pack(side='left', padx=3)
        self.order_search_var = tk.StringVar()
        ttk.Entry(search_frame, textvariable=self.order_search_var, width=25).pack(side='left', padx=3)
        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 (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)
        
        # 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.configure(yscrollcommand=scrollbar.set)
        
        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
        
        # Bind mouse wheel untuk scroll
        def _on_mousewheel(event):
            canvas.yview_scroll(int(-1*(event.delta/120)), "units")
        canvas.bind_all("<MouseWheel>", _on_mousewheel)
        
        # === PANEL KANAN: Keranjang ===
        ttk.Label(right, text="Keranjang Belanja", font=("Arial", 12, "bold")).pack(pady=4)
        
        # Treeview cart
        cart_cols = ("Menu", "Qty", "Harga", "Subtotal")
        self.cart_tree = ttk.Treeview(right, columns=cart_cols, show='headings', height=8)
        for c in cart_cols:
            self.cart_tree.heading(c, text=c)
            if c == "Menu":
                self.cart_tree.column(c, width=150)
            else:
                self.cart_tree.column(c, width=70)
        self.cart_tree.pack(fill='x', pady=4)
        
        # Tombol hapus item atau kosongkan cart
        cart_btn_frame = ttk.Frame(right)
        cart_btn_frame.pack(pady=4)
        ttk.Button(cart_btn_frame, text="Hapus Item", command=self.remove_cart_item).pack(side='left', padx=3)
        ttk.Button(cart_btn_frame, text="Kosongkan", command=self.clear_cart).pack(side='left', padx=3)
        
        # Info total
        total_frame = ttk.Frame(right)
        total_frame.pack(fill='x', pady=4)
        
        self.cart_subtotal_label = ttk.Label(total_frame, text="Subtotal: Rp 0", font=("Arial", 9))
        self.cart_subtotal_label.pack()
        self.cart_discount_label = ttk.Label(total_frame, text="Diskon Item: Rp 0", font=("Arial", 9))
        self.cart_discount_label.pack()
        self.cart_promo_label = ttk.Label(total_frame, text="Diskon Promo: Rp 0", font=("Arial", 9))
        self.cart_promo_label.pack()
        self.cart_total_label = ttk.Label(total_frame, text="TOTAL: Rp 0", font=("Arial", 11, "bold"))
        self.cart_total_label.pack(pady=2)
        
        # Input nomor meja dan promo (LAYOUT RAPI)
        checkout_frame = ttk.Frame(right)
        checkout_frame.pack(fill='x', pady=6, padx=10)
        
        # Row 0: No. Meja (AUTO-FILLED untuk pembeli) ← DIPERBAIKI
        ttk.Label(checkout_frame, text="No. Meja:", font=("Arial", 9)).grid(row=0, column=0, sticky='w', padx=3, pady=3)
        self.order_meja_var = tk.StringVar()
        
        # ✅ AUTO-FILL jika user adalah pembeli
        if self.session['role'] in ['pembeli', 'user'] and 'nomor_meja' in self.session:
            self.order_meja_var.set(str(self.session['nomor_meja']))
            meja_entry = ttk.Entry(checkout_frame, textvariable=self.order_meja_var, width=20, state='disabled')
            # Tambah label info
            ttk.Label(checkout_frame, text="✓", font=("Arial", 10), foreground='green').grid(row=0, column=2, padx=3)
        else:
            meja_entry = ttk.Entry(checkout_frame, textvariable=self.order_meja_var, width=20)
        
        meja_entry.grid(row=0, column=1, pady=3, sticky='ew')
        
        # Row 1: Kode Promo
        ttk.Label(checkout_frame, text="Kode Promo:", font=("Arial", 9)).grid(row=1, column=0, sticky='w', padx=3, pady=3)
        self.order_promo_var = tk.StringVar()
        ttk.Entry(checkout_frame, textvariable=self.order_promo_var, width=12).grid(row=1, column=1, pady=3, sticky='ew')
        ttk.Button(checkout_frame, text="Terapkan", command=self.update_cart_display, width=10).grid(row=1, column=2, padx=3, sticky='e')
        
        # Configure columns
        checkout_frame.columnconfigure(1, weight=1)
        
        # Tombol checkout (PASTI KELIHATAN)
        checkout_btn_frame = ttk.Frame(right)
        checkout_btn_frame.pack(fill='x', pady=10, padx=20)
        
        ttk.Button(
            checkout_btn_frame, 
            text="🛒 CHECKOUT PESANAN", 
            command=self.checkout_order,
            width=30
        ).pack()
        
        # Init cart data
        self.cart_items = []
        
        # Load menu cards
        self.reload_order_menu_cards()


    def reload_order_menu_cards(self):
        """Load menu dalam bentuk cards 4 kolom FULL WIDTH"""
        # Clear existing cards
        for widget in self.menu_cards_frame.winfo_children():
            widget.destroy()

        search = self.order_search_var.get().strip() or None
        results = menu_list(search_text=search, available_only=True)

        cart_dict = {item['menu_id']: item['qty'] for item in self.cart_items}

        # === 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

        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=5, pady=5
            )
            
            # 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)
                    # Resize agak kecil agar muat 4 kolom
                    img = img.resize((120, 85), Image.Resampling.LANCZOS)
                    photo = ImageTk.PhotoImage(img)
                    lbl = tk.Label(card, image=photo, bg='white')
                    lbl.image = photo
                    lbl.pack(pady=5)
                except:
                    tk.Label(card, text="[No Image]", bg='#eee', height=4).pack(fill='x')
            else:
                tk.Label(card, text="[No Image]", bg='#eee', height=4).pack(fill='x')

            # --- 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()
            
            txt_harga = f"Rp {harga:,.0f}"
            if item_disc > 0:
                txt_harga += f"\n(-{item_disc}%)"
                fg = '#4CAF50'
            else:
                fg = 'green'
            tk.Label(card, text=txt_harga, font=("Arial", 9, "bold"), fg=fg, 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()

            # --- 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') 

            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(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 >= total_columns:
                col = 0
                row += 1

        # Reset row weight
        for i in range(row + 1):
            self.menu_cards_frame.rowconfigure(i, weight=0)


    def reset_order_search(self):
        self.order_search_var.set("")
        self.reload_order_menu_cards()

    def increase_from_card(self, menu_id, stok):
        """Tambah qty dari tombol + di card"""
        # Cek qty saat ini
        current_qty = 0
        cart_item_found = None
        for cart_item in self.cart_items:
            if cart_item['menu_id'] == menu_id:
                current_qty = cart_item['qty']
                cart_item_found = cart_item
                break
        
        # Cek stok
        if current_qty >= stok:
            messagebox.showwarning("Stok Habis", f"Stok hanya tersisa {stok}")
            return
        
        # Tambah qty
        if cart_item_found:
            cart_item_found['qty'] += 1
        else:
            self.cart_items.append({'menu_id': menu_id, 'qty': 1})
        
        # Update tampilan
        self.reload_order_menu_cards()
        self.update_cart_display()
    
    def decrease_from_card(self, menu_id):
        """Kurangi qty dari tombol - di card"""
        for i, cart_item in enumerate(self.cart_items):
            if cart_item['menu_id'] == menu_id:
                cart_item['qty'] -= 1
                
                # Kalau qty jadi 0, hapus dari cart
                if cart_item['qty'] <= 0:
                    del self.cart_items[i]
                
                # Update tampilan
                self.reload_order_menu_cards()
                self.update_cart_display()
                return
            
            
    # Wilayah dikuasai Favorite


    def build_favorite_tab(self, parent):
        """Tab untuk melihat menu favorit dan history pesanan"""
        for w in parent.winfo_children():
            w.destroy()
        
        # Header
        header = ttk.Frame(parent)
        header.pack(fill='x', padx=10, pady=8)
        ttk.Label(header, text="🌟 Menu Favorit & History", font=("Arial", 14, "bold")).pack(side='left')
        ttk.Button(header, text="🔄 Refresh", command=self.reload_favorite_tab).pack(side='right', padx=6)
        
        # Split 2 panel: kiri = favorit, kanan = history
        left = ttk.LabelFrame(parent, text="⭐ Menu Favorit (Top 5)", padding=10)
        left.pack(side='left', fill='both', expand=True, padx=10, pady=6)
        
        right = ttk.LabelFrame(parent, text="📜 History Pesanan Terakhir", padding=10)
        right.pack(side='right', fill='both', expand=True, padx=10, pady=6)
        
        # === PANEL KIRI: Menu Favorit ===
        
        # Treeview favorit
        fav_cols = ("Rank", "Menu", "Kategori", "Harga", "Dipesan", "Terakhir")
        self.favorite_tree = ttk.Treeview(left, columns=fav_cols, show='headings', height=12)
        
        self.favorite_tree.heading("Rank", text="#")
        self.favorite_tree.heading("Menu", text="Menu")
        self.favorite_tree.heading("Kategori", text="Kategori")
        self.favorite_tree.heading("Harga", text="Harga")
        self.favorite_tree.heading("Dipesan", text="Dipesan")
        self.favorite_tree.heading("Terakhir", text="Terakhir")
        
        self.favorite_tree.column("Rank", width=40)
        self.favorite_tree.column("Menu", width=150)
        self.favorite_tree.column("Kategori", width=90)
        self.favorite_tree.column("Harga", width=80)
        self.favorite_tree.column("Dipesan", width=70)
        self.favorite_tree.column("Terakhir", width=140)
        
        self.favorite_tree.pack(fill='both', expand=True, pady=6)
        
        # Tombol quick order
        fav_btn_frame = ttk.Frame(left)
        fav_btn_frame.pack(pady=6)
        ttk.Label(fav_btn_frame, text="Quick Order:").pack(side='left', padx=6)
        ttk.Button(fav_btn_frame, text="🛒 Pesan Lagi", command=self.quick_order_favorite).pack(side='left', padx=3)
        
        # === PANEL KANAN: History Transaksi ===
        
        # Treeview history
        hist_cols = ("ID", "Tanggal", "Meja", "Total", "Status")
        self.history_tree = ttk.Treeview(right, columns=hist_cols, show='headings', height=12)
        
        self.history_tree.heading("ID", text="ID")
        self.history_tree.heading("Tanggal", text="Tanggal")
        self.history_tree.heading("Meja", text="Meja")
        self.history_tree.heading("Total", text="Total")
        self.history_tree.heading("Status", text="Status")
        
        self.history_tree.column("ID", width=40)
        self.history_tree.column("Tanggal", width=140)
        self.history_tree.column("Meja", width=60)
        self.history_tree.column("Total", width=100)
        self.history_tree.column("Status", width=90)
        
        self.history_tree.pack(fill='both', expand=True, pady=6)
        
        # Bind event untuk lihat detail
        self.history_tree.bind("<<TreeviewSelect>>", self.on_history_select)
        
        # Detail history
        detail_frame = ttk.Frame(right)
        detail_frame.pack(fill='x', pady=6)
        
        self.history_detail_text = tk.Text(detail_frame, height=8, font=("Courier New", 8), wrap='word')
        hist_scroll = ttk.Scrollbar(detail_frame, orient='vertical', command=self.history_detail_text.yview)
        self.history_detail_text.configure(yscrollcommand=hist_scroll.set)
        
        self.history_detail_text.pack(side='left', fill='both', expand=True)
        hist_scroll.pack(side='right', fill='y')
        
        # Tombol history action
        hist_btn_frame = ttk.Frame(right)
        hist_btn_frame.pack(pady=6)
        ttk.Button(hist_btn_frame, text="🔁 Pesan Ulang", command=self.reorder_from_history).pack(side='left', padx=3)
        
        # Load data
        self.reload_favorite_tab()
    
    def reload_favorite_tab(self):
        """Load data favorit dan history"""
        # Clear trees
        for r in self.favorite_tree.get_children():
            self.favorite_tree.delete(r)
        for r in self.history_tree.get_children():
            self.history_tree.delete(r)
        
        # Load favorit
        favorites = favorite_list(self.session['id'], limit=5)
        rank = 1
        for fav in favorites:
            menu_id, count, last_ordered = fav
            menu_data = menu_get(menu_id)
            if not menu_data:
                continue
            
            _, nama, kategori, harga, stok, foto, tersedia, item_disc = menu_data
            
            self.favorite_tree.insert("", tk.END, values=(
                rank,
                nama,
                kategori,
                f"Rp {harga:,.0f}",
                f"{count}x",
                last_ordered
            ))
            rank += 1
        
        # Load history transaksi
        history = transaksi_list(user_id=self.session['id'])
        for h in history:
            tid, uid, meja, total, status, promo_code, tanggal = h
            self.history_tree.insert("", tk.END, values=(
                tid,
                tanggal,
                meja,
                f"Rp {total:,.0f}",
                status
            ))
    
    def on_history_select(self, event):
        """Tampilkan detail history saat dipilih"""
        sel = self.history_tree.selection()
        if not sel:
            return
        
        item = self.history_tree.item(sel)['values']
        transaksi_id = item[0]
        
        # Get detail transaksi
        transaksi_data = transaksi_get(transaksi_id)
        if not transaksi_data:
            return
        
        tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data
        detail_items = detail_transaksi_list(transaksi_id)
        
        # Format detail
        detail_text = f"TRANSAKSI #{tid} - {status.upper()}\n"
        detail_text += f"{'='*40}\n"
        detail_text += f"Tanggal: {tanggal}\n"
        detail_text += f"Meja: {meja}\n\n"
        detail_text += f"Item Pesanan:\n"
        detail_text += f"{'-'*40}\n"
        
        for detail in detail_items:
            did, mid, qty, harga, subtotal_item = detail
            menu_data = menu_get(mid)
            if menu_data:
                _, nama, kategori, _, _, _, _, _ = menu_data
                detail_text += f"• {nama}\n"
                detail_text += f"  {qty} x Rp {harga:,.0f} = Rp {subtotal_item:,.0f}\n"
        
        detail_text += f"{'-'*40}\n"
        detail_text += f"Subtotal: Rp {subtotal:,.0f}\n"
        detail_text += f"Diskon: Rp {item_disc + promo_disc:,.0f}\n"
        detail_text += f"TOTAL: Rp {total:,.0f}\n"
        
        self.history_detail_text.delete('1.0', tk.END)
        self.history_detail_text.insert('1.0', detail_text)
    
    def quick_order_favorite(self):
        """Pesan ulang dari menu favorit yang dipilih"""
        sel = self.favorite_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih Menu", "Pilih menu favorit terlebih dahulu")
            return
        
        item = self.favorite_tree.item(sel)['values']
        menu_name = item[1]
        
        # Cari menu_id dari nama
        all_menus = menu_list()
        menu_id = None
        for m in all_menus:
            if m[1] == menu_name:
                menu_id = m[0]
                break
        
        if not menu_id:
            messagebox.showerror("Error", "Menu tidak ditemukan")
            return
        
        # Tambah ke cart (qty 1)
        menu_data = menu_get(menu_id)
        if not menu_data:
            return
        
        _, nama, kategori, harga, stok, foto, tersedia, item_disc = menu_data
        
        if stok < 1:
            messagebox.showwarning("Stok Habis", f"Stok {nama} habis")
            return
        
        # Cek apakah sudah ada di cart
        found = False
        for cart_item in self.cart_items:
            if cart_item['menu_id'] == menu_id:
                if cart_item['qty'] < stok:
                    cart_item['qty'] += 1
                    found = True
                else:
                    messagebox.showwarning("Stok Habis", f"Stok {nama} hanya {stok}")
                    return
                break
        
        if not found:
            self.cart_items.append({'menu_id': menu_id, 'qty': 1})
        
        messagebox.showinfo("Ditambahkan", f"{nama} ditambahkan ke keranjang!\n\nSilakan ke tab 'Order Menu' untuk checkout.")
    
    def reorder_from_history(self):
        """Pesan ulang semua item dari history yang dipilih"""
        sel = self.history_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih History", "Pilih history pesanan terlebih dahulu")
            return
        
        item = self.history_tree.item(sel)['values']
        transaksi_id = item[0]
        
        # Get detail items
        detail_items = detail_transaksi_list(transaksi_id)
        
        if not detail_items:
            messagebox.showerror("Error", "Tidak ada detail pesanan")
            return
        
        # Tambah semua item ke cart
        added_count = 0
        for detail in detail_items:
            did, mid, qty, harga, subtotal_item = detail
            
            # Cek stok
            menu_data = menu_get(mid)
            if not menu_data:
                continue
            
            _, nama, kategori, harga_now, stok, foto, tersedia, item_disc = menu_data
            
            if stok < qty:
                messagebox.showwarning("Stok Kurang", f"Stok {nama} hanya {stok}, pesanan asli {qty}")
                qty = stok
            
            if qty <= 0:
                continue
            
            # Tambah ke cart
            found = False
            for cart_item in self.cart_items:
                if cart_item['menu_id'] == mid:
                    new_qty = cart_item['qty'] + qty
                    if new_qty <= stok:
                        cart_item['qty'] = new_qty
                        found = True
                        added_count += 1
                    break
            
            if not found:
                self.cart_items.append({'menu_id': mid, 'qty': qty})
                added_count += 1
        
        if added_count > 0:
            messagebox.showinfo("Berhasil", f"{added_count} item ditambahkan ke keranjang!\n\nSilakan ke tab 'Order Menu' untuk checkout.")
        else:
            messagebox.showwarning("Gagal", "Tidak ada item yang bisa ditambahkan (stok habis)")
    
    def update_cart_display(self):
        """Update tampilan keranjang dan hitung total"""
        
        # Clear tree
        for r in self.cart_tree.get_children():
            self.cart_tree.delete(r)
        
        # Tampilkan item
        for cart_item in self.cart_items:
            menu_data = menu_get(cart_item['menu_id'])
            if not menu_data:
                print(f"WARNING: Menu ID {cart_item['menu_id']} tidak ditemukan!")
                continue
            
            _, nama, kategori, harga, stok, foto, tersedia, item_disc = menu_data
            qty = cart_item['qty']
            subtotal = harga * qty
            
            # DEBUG
            print(f"Menambahkan ke tree: {nama} x{qty} = {subtotal}")
            
            self.cart_tree.insert("", tk.END, values=(nama, qty, f"{harga:,.0f}", f"{subtotal:,.0f}"))
        
        # Hitung total dengan diskon
        promo_code = self.order_promo_var.get().strip() or None
        
        if self.cart_items:  # PENTING: Hanya hitung kalau ada item
            calc = apply_discounts_and_promo(self.cart_items, promo_code)
            
            self.cart_subtotal_label.config(text=f"Subtotal: Rp {calc['subtotal']:,.0f}")
            self.cart_discount_label.config(text=f"Diskon Item: Rp {calc['item_discount']:,.0f}")
            self.cart_promo_label.config(text=f"Diskon Promo: Rp {calc['promo_discount']:,.0f}")
            self.cart_total_label.config(text=f"TOTAL: Rp {calc['total']:,.0f}")
        else:
            # Reset label kalau cart kosong
            self.cart_subtotal_label.config(text="Subtotal: Rp 0")
            self.cart_discount_label.config(text="Diskon Item: Rp 0")
            self.cart_promo_label.config(text="Diskon Promo: Rp 0")
            self.cart_total_label.config(text="TOTAL: Rp 0")
    
    
    def remove_cart_item(self):
        """Hapus item dari cart"""
        sel = self.cart_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih Item", "Pilih item di keranjang")
            return
        
        idx = self.cart_tree.index(sel)
        if idx >= len(self.cart_items):
            return
        
        del self.cart_items[idx]
        self.update_cart_display()

        self.reload_order_menu_cards()
    
    def clear_cart(self):
        """Kosongkan keranjang"""
        if not self.cart_items:
            return
        
        if messagebox.askyesno("Konfirmasi", "Kosongkan keranjang?"):
            self.cart_items = []
            self.update_cart_display()

            self.reload_order_menu_cards()
    
    def checkout_order(self):
        """Simpan pesanan ke database"""
        if not self.cart_items:
            messagebox.showwarning("Keranjang Kosong", "Tambahkan item terlebih dahulu")
            return
        
        nomor_meja = self.order_meja_var.get().strip()
        if not nomor_meja:
            messagebox.showwarning("Input Error", "Masukkan nomor meja")
            return
        
        try:
            nomor_meja = int(nomor_meja)
        except:
            messagebox.showerror("Input Error", "Nomor meja harus angka")
            return
        
        promo_code = self.order_promo_var.get().strip() or None
        
        # Validasi promo
        if promo_code:
            promo_data = promo_get(promo_code)
            if not promo_data:
                messagebox.showwarning("Promo Invalid", "Kode promo tidak ditemukan")
                return
        
        # Simpan transaksi
        success, result = transaksi_add(self.session['id'], nomor_meja, self.cart_items, promo_code)
        
        if success:
            meja_update_status(nomor_meja, "terisi", result)
            messagebox.showinfo("Sukses", f"Pesanan berhasil! ID Transaksi: {result}\nStatus: Pending")
            # Reset
            self.cart_items = []
            self.order_meja_var.set("")
            self.order_promo_var.set("")
            self.update_cart_display()
            self.reload_order_menu_cards()  # Refresh stok
        else:
            messagebox.showerror("Error", f"Gagal menyimpan pesanan: {result}")



            
    
    # Wilayah dikuasai Waiter


    def build_waiter_tab(self, parent, readonly=False):
        """Tab untuk waiter mengelola pesanan
        
        Status Flow yang Disederhanakan:
        pending → menunggu → selesai
        """
        for w in parent.winfo_children():
            w.destroy()
        
        # Header
        header = ttk.Frame(parent)
        header.pack(fill='x', padx=10, pady=6)
        
        if readonly:
            ttk.Label(
                header,
                text="👁️ Monitor Pesanan (Read-Only)",
                font=("Arial", 13, "bold"),
                foreground='orange'
            ).pack(side='left')
        else:
            ttk.Label(
                header,
                text="🍽️ Dashboard Waiter - Kelola Pesanan",
                font=("Arial", 13, "bold")
            ).pack(side='left')
        
        ttk.Button(
            header,
            text="🔄 Refresh",
            command=self.reload_waiter_orders
        ).pack(side='right', padx=6)
        
        # Filter status
        filter_frame = ttk.Frame(parent)
        filter_frame.pack(fill='x', padx=10, pady=4)
        ttk.Label(filter_frame, text="Filter Status:").pack(side='left', padx=6)
        ttk.Button(filter_frame, text="Semua", command=lambda: self.reload_waiter_orders(None), width=8).pack(side='left', padx=2)
        ttk.Button(filter_frame, text="Pending", command=lambda: self.reload_waiter_orders('pending'), width=8).pack(side='left', padx=2)
        ttk.Button(filter_frame, text="Diproses", command=lambda: self.reload_waiter_orders('menunggu'), width=10).pack(side='left', padx=2)
        ttk.Button(filter_frame, text="Selesai", command=lambda: self.reload_waiter_orders('selesai'), width=8).pack(side='left', padx=2)
        
        # Treeview pesanan
        tree_frame = ttk.Frame(parent)
        tree_frame.pack(fill='x', padx=10, pady=4)
        
        cols = ("ID", "User ID", "No.Meja", "Total", "Status", "Promo", "Tanggal")
        self.waiter_tree = ttk.Treeview(tree_frame, columns=cols, show='headings', height=8)
        
        self.waiter_tree.heading("ID", text="ID")
        self.waiter_tree.heading("User ID", text="User ID")
        self.waiter_tree.heading("No.Meja", text="No.Meja")
        self.waiter_tree.heading("Total", text="Total")
        self.waiter_tree.heading("Status", text="Status")
        self.waiter_tree.heading("Promo", text="Promo")
        self.waiter_tree.heading("Tanggal", text="Tanggal")
        
        self.waiter_tree.column("ID", width=40)
        self.waiter_tree.column("User ID", width=60)
        self.waiter_tree.column("No.Meja", width=70)
        self.waiter_tree.column("Total", width=100)
        self.waiter_tree.column("Status", width=90)
        self.waiter_tree.column("Promo", width=80)
        self.waiter_tree.column("Tanggal", width=140)
        
        tree_scroll = ttk.Scrollbar(tree_frame, orient='vertical', command=self.waiter_tree.yview)
        self.waiter_tree.configure(yscrollcommand=tree_scroll.set)
        
        self.waiter_tree.pack(side='left', fill='both', expand=True)
        tree_scroll.pack(side='right', fill='y')
        
        self.waiter_tree.bind("<<TreeviewSelect>>", self.on_waiter_select)
        
        # Detail pesanan
        detail_frame = ttk.LabelFrame(parent, text="📋 Detail Pesanan", padding=8)
        detail_frame.pack(fill='x', padx=10, pady=4)
        
        text_frame = ttk.Frame(detail_frame)
        text_frame.pack(fill='both', expand=True)
        
        self.waiter_detail_text = tk.Text(
            text_frame,
            height=6,
            width=100,
            font=("Courier New", 8),
            wrap='word'
        )
        detail_scroll = ttk.Scrollbar(text_frame, orient='vertical', command=self.waiter_detail_text.yview)
        self.waiter_detail_text.configure(yscrollcommand=detail_scroll.set)
        
        detail_scroll.pack(side='right', fill='y')
        self.waiter_detail_text.pack(side='left', fill='both', expand=True)
        
        # TOMBOL AKSI - DENGAN STATE MANAGEMENT
        action_frame = ttk.LabelFrame(parent, text="🎯 Update Status Pesanan", padding=12)
        action_frame.pack(fill='x', padx=10, pady=8)
        
        if readonly:
            warning_label = ttk.Label(
                action_frame,
                text="⚠️ MODE READ-ONLY: Anda hanya bisa melihat data, tidak bisa mengubah status.\n"
                     "Hanya Waiter yang bisa mengubah status pesanan.",
                font=("Arial", 9),
                foreground='red',
                justify='center'
            )
            warning_label.pack(pady=10)
        
        # Grid untuk 2 tombol
        btn_frame = ttk.Frame(action_frame)
        btn_frame.pack(fill='x', pady=10)
        
        self.waiter_btn_terima = tk.Button(
            btn_frame,
            text="✅ Terima Pesanan (Pending → Diproses)",
            command=lambda: self.update_order_status('menunggu') if not readonly else None,
            font=("Arial", 10, "bold"),
            relief='raised',
            cursor='hand2',
            state='disabled' if readonly else 'normal',
            bg='#4CAF50',
            fg='white',
            activebackground='#45a049',
            padx=10,
            pady=10
        )
        self.waiter_btn_terima.pack(fill='x', padx=5, pady=5)
        
        self.waiter_btn_proses = tk.Button(
            btn_frame,
            text="🍳 Pesanan Siap Disajikan (Diproses → Selesai)",
            command=lambda: self.update_order_status('selesai') if not readonly else None,
            font=("Arial", 10, "bold"),
            relief='raised',
            cursor='hand2',
            state='disabled' if readonly else 'normal',
            bg='#2196F3',
            fg='white',
            activebackground='#0b7dda',
            padx=10,
            pady=10
        )
        self.waiter_btn_proses.pack(fill='x', padx=5, pady=5)
        
        # ===== INFO BOX - STATUS FLOW =====
        info_box = tk.Frame(
            action_frame,
            bg='#E3F2FD',
            relief='solid',
            borderwidth=2,
            padx=15,
            pady=12
        )
        info_box.pack(fill='x', pady=10)
        
        tk.Label(
            info_box,
            text="📌 ALUR STATUS PESANAN",
            font=("Arial", 10, "bold"),
            bg='#E3F2FD',
            fg='#1565C0'
        ).pack(anchor='w', pady=(0, 8))
        
        tk.Label(
            info_box,
            text="Pending (Baru)  →  Diproses (Sedang Disiapkan)  →  Selesai (Siap Disajikan)",
            font=("Arial", 9, "bold"),
            bg='#E3F2FD',
            fg='#0D47A1',
            wraplength=500,
            justify='center'
        ).pack(anchor='w', pady=(0, 8))
        
        tk.Label(
            info_box,
            text="💡 Tombol akan otomatis berubah warna sesuai status pesanan.\n"
                 "Tombol berwarna cerah bisa diklik, yang abu-abu tidak.",
            font=("Arial", 8),
            bg='#E3F2FD',
            fg='#424242',
            justify='center',
            wraplength=500
        ).pack(anchor='w')
        
        # Load data
        self.reload_waiter_orders()


    def reload_waiter_orders(self, status_filter=None):
        """Load pesanan untuk waiter"""
        # Clear tree
        for r in self.waiter_tree.get_children():
            self.waiter_tree.delete(r)
        
        # Get transaksi
        if status_filter:
            orders = transaksi_list(status=status_filter)
        else:
            # Tampilkan semua kecuali yang sudah dibayar
            all_orders = transaksi_list()
            orders = [o for o in all_orders if o[4] != 'dibayar']
        
        # Insert ke tree
        for order in orders:
            tid, uid, meja, total, status, promo_code, tanggal = order
            promo_display = promo_code if promo_code else "-"
            self.waiter_tree.insert("", tk.END, values=(
                tid, uid, meja, f"Rp {total:,.0f}", status, promo_display, tanggal
            ))
    
    def on_waiter_select(self, event):
        """Tampilkan detail pesanan saat dipilih"""
        sel = self.waiter_tree.selection()
        if not sel:
            return
        
        item = self.waiter_tree.item(sel)['values']
        transaksi_id = item[0]
        
        # Get detail transaksi
        transaksi_data = transaksi_get(transaksi_id)
        if not transaksi_data:
            return
        
        tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data
        
        # Get detail items
        detail_items = detail_transaksi_list(transaksi_id)
        
        # Format detail
        detail_text = f"═══════════════════════════════════════════════\n"
        detail_text += f"TRANSAKSI ID: {tid}\n"
        detail_text += f"═══════════════════════════════════════════════\n\n"
        detail_text += f"User ID       : {uid}\n"
        detail_text += f"Nomor Meja    : {meja}\n"
        detail_text += f"Status        : {status.upper()}\n"
        detail_text += f"Tanggal       : {tanggal}\n"
        detail_text += f"Kode Promo    : {promo_code if promo_code else '-'}\n\n"
        
        detail_text += f"───────────────────────────────────────────────\n"
        detail_text += f"ITEM PESANAN:\n"
        detail_text += f"───────────────────────────────────────────────\n"
        
        for detail in detail_items:
            did, mid, qty, harga, subtotal_item = detail
            
            # Get nama menu
            menu_data = menu_get(mid)
            if menu_data:
                _, nama, kategori, _, _, _, _, _ = menu_data
                detail_text += f"• {nama} ({kategori})\n"
                detail_text += f"  {qty} x Rp {harga:,.0f} = Rp {subtotal_item:,.0f}\n\n"
        
        detail_text += f"───────────────────────────────────────────────\n"
        detail_text += f"Subtotal        : Rp {subtotal:,.0f}\n"
        detail_text += f"Diskon Item     : Rp {item_disc:,.0f}\n"
        detail_text += f"Diskon Promo    : Rp {promo_disc:,.0f}\n"
        detail_text += f"───────────────────────────────────────────────\n"
        detail_text += f"TOTAL BAYAR     : Rp {total:,.0f}\n"
        detail_text += f"═══════════════════════════════════════════════\n"
        
        # Tampilkan di text widget
        self.waiter_detail_text.delete('1.0', tk.END)
        self.waiter_detail_text.insert('1.0', detail_text)
    
    def update_order_status(self, new_status):
        """Update status pesanan dengan visual feedback
        
        Status Flow: pending → menunggu → selesai
        """
        sel = self.waiter_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih Pesanan", "Pilih pesanan terlebih dahulu")
            return
        
        item = self.waiter_tree.item(sel)['values']
        transaksi_id = item[0]
        current_status = item[4]
        
        # Validasi flow status
        valid_transitions = {
            'pending': ['menunggu'],      # Pending → Diproses
            'menunggu': ['selesai'],      # Diproses → Selesai
            'selesai': []                 # Selesai = FINAL
        }
        
        # Cek current status valid
        if current_status not in valid_transitions:
            messagebox.showerror(
                "Error",
                f"❌ Status '{current_status}' sudah final!\n\n"
                f"Pesanan ini sudah siap disajikan."
            )
            return
        
        # Cek status sudah final
        if not valid_transitions[current_status]:
            messagebox.showerror(
                "Status Final",
                f"❌ Pesanan sudah mencapai status 'SELESAI'\n\n"
                f"Tidak ada lagi yang perlu dilakukan waiter."
            )
            return
        
        # Cek new_status valid
        if new_status not in valid_transitions[current_status]:
            expected = ', '.join(valid_transitions[current_status])
            messagebox.showwarning(
                "Status Tidak Valid",
                f"❌ Tidak bisa ubah dari '{current_status}' ke '{new_status}'.\n\n"
                f"✅ Status yang benar setelah '{current_status}' adalah:\n"
                f"   → {expected}"
            )
            return
        
        # Mapping status untuk pesan
        status_messages = {
            'pending': 'Pesanan Baru',
            'menunggu': 'Sedang Disiapkan',
            'selesai': 'Siap Disajikan'
        }
        
        current_msg = status_messages.get(current_status, current_status.upper())
        new_msg = status_messages.get(new_status, new_status.upper())
        
        # Konfirmasi
        confirm_msg = f"Ubah status pesanan #{transaksi_id}?\n\n"
        confirm_msg += f"Dari: {current_msg}\n"
        confirm_msg += f"Ke: {new_msg}\n\n"
        
        if new_status == 'selesai':
            confirm_msg += "⚠️ Setelah ini, pesanan siap disajikan ke customer.\n"
            confirm_msg += "Customer dapat langsung diminta untuk bayar."
        elif new_status == 'menunggu':
            confirm_msg += "Pesanan akan dimasukkan ke daftar yang sedang disiapkan."
        
        confirm_msg += "\n\nLanjutkan?"
        
        if not messagebox.askyesno("Konfirmasi", confirm_msg):
            return
        
        # Update status di database
        success = transaksi_update_status(transaksi_id, new_status)
        
        if success:
            # Pesan sukses
            if new_status == 'menunggu':
                success_msg = (
                    f"✅ Pesanan #{transaksi_id} sedang disiapkan.\n\n"
                    f"Tim dapur: mulai siapkan pesanan ini!"
                )
            elif new_status == 'selesai':
                success_msg = (
                    f"✅ Pesanan #{transaksi_id} siap disajikan!\n\n"
                    f"Silakan bawa ke meja customer."
                )
            else:
                success_msg = f"✅ Status berhasil diubah menjadi '{new_status.upper()}'"
            
            messagebox.showinfo("Berhasil", success_msg)
            
            # ✅ RELOAD DAN UPDATE BUTTON STATE
            self.reload_waiter_orders()
            self.update_waiter_button_state()
            
            # Auto select row yang sama
            for item_id in self.waiter_tree.get_children():
                if self.waiter_tree.item(item_id)['values'][0] == transaksi_id:
                    self.waiter_tree.selection_set(item_id)
                    self.waiter_tree.see(item_id)
                    self.on_waiter_select(None)
                    break
        else:
            messagebox.showerror("❌ Gagal", "Gagal mengubah status pesanan")

    def update_waiter_button_state(self):
        """Update state tombol berdasarkan status pesanan yang dipilih
        
        Logika:
        - Pending: Tombol TERIMA aktif, tombol PROSES disabled
        - Diproses: Tombol TERIMA disabled, tombol PROSES aktif
        - Selesai: Semua tombol disabled (pesanan sudah final)
        """
        sel = self.waiter_tree.selection()
        
        if not sel:
            # Tidak ada pesanan dipilih: semua disabled
            self.waiter_btn_terima.config(state='disabled', bg='#cccccc', fg='#666666')
            self.waiter_btn_proses.config(state='disabled', bg='#cccccc', fg='#666666')
            return
        
        item = self.waiter_tree.item(sel)['values']
        current_status = item[4]  # Status adalah field ke-4 (index 4)
        
        # ===== LOGIC UNTUK SET STATE TOMBOL =====
        
        if current_status == 'pending':
            # Status PENDING: Tombol TERIMA aktif, PROSES disabled
            
            # Tombol TERIMA - AKTIF (Hijau)
            self.waiter_btn_terima.config(
                state='normal',
                bg='#4CAF50',
                fg='white',
                cursor='hand2',
                relief='raised',
                activebackground='#45a049'
            )
            
            # Tombol PROSES - DISABLED (Gelap)
            self.waiter_btn_proses.config(
                state='disabled',
                bg='#A9A9A9',
                fg='#555555',
                cursor='arrow',
                relief='sunken'
            )
        
        elif current_status == 'menunggu':
            # Status DIPROSES: Tombol TERIMA disabled, PROSES aktif
            
            # Tombol TERIMA - DISABLED
            self.waiter_btn_terima.config(
                state='disabled',
                bg='#A9A9A9',
                fg='#555555',
                cursor='arrow',
                relief='sunken'
            )
            
            # Tombol PROSES - AKTIF (Biru)
            self.waiter_btn_proses.config(
                state='normal',
                bg='#2196F3',
                fg='white',
                cursor='hand2',
                relief='raised',
                activebackground='#0b7dda'
            )
        
        elif current_status == 'selesai':
            # Status SELESAI: Semua tombol DISABLED (FINAL STATE)
            
            self.waiter_btn_terima.config(
                state='disabled',
                bg='#D3D3D3',
                fg='#777777',
                cursor='arrow',
                relief='sunken'
            )
            
            self.waiter_btn_proses.config(
                state='disabled',
                bg='#D3D3D3',
                fg='#777777',
                cursor='arrow',
                relief='sunken'
            )
    
    
    # ========================================
    # ON WAITER SELECT - CALL UPDATE BUTTON STATE
    # ========================================
    
    def on_waiter_select(self, event):
        """Tampilkan detail pesanan + UPDATE BUTTON STATE"""
        sel = self.waiter_tree.selection()
        if not sel:
            return
        
        item = self.waiter_tree.item(sel)['values']
        transaksi_id = item[0]
        
        # Get detail transaksi
        transaksi_data = transaksi_get(transaksi_id)
        if not transaksi_data:
            return
        
        tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data
        detail_items = detail_transaksi_list(transaksi_id)
        
        # Format detail
        detail_text = f"═══════════════════════════════════════════════\n"
        detail_text += f"TRANSAKSI ID: {tid}\n"
        detail_text += f"═══════════════════════════════════════════════\n\n"
        detail_text += f"User ID       : {uid}\n"
        detail_text += f"Nomor Meja    : {meja}\n"
        detail_text += f"Status        : {status.upper()}\n"
        detail_text += f"Tanggal       : {tanggal}\n"
        detail_text += f"Kode Promo    : {promo_code if promo_code else '-'}\n\n"
        
        detail_text += f"───────────────────────────────────────────────\n"
        detail_text += f"ITEM PESANAN:\n"
        detail_text += f"───────────────────────────────────────────────\n"
        
        for detail in detail_items:
            did, mid, qty, harga, subtotal_item = detail
            menu_data = menu_get(mid)
            if menu_data:
                _, nama, kategori, _, _, _, _, _ = menu_data
                detail_text += f"• {nama} ({kategori})\n"
                detail_text += f"  {qty} x Rp {harga:,.0f} = Rp {subtotal_item:,.0f}\n\n"
        
        detail_text += f"───────────────────────────────────────────────\n"
        detail_text += f"Subtotal        : Rp {subtotal:,.0f}\n"
        detail_text += f"Diskon Item     : Rp {item_disc:,.0f}\n"
        detail_text += f"Diskon Promo    : Rp {promo_disc:,.0f}\n"
        detail_text += f"───────────────────────────────────────────────\n"
        detail_text += f"TOTAL BAYAR     : Rp {total:,.0f}\n"
        detail_text += f"═══════════════════════════════════════════════\n"
        
        # Tampilkan di text widget
        self.waiter_detail_text.delete('1.0', tk.END)
        self.waiter_detail_text.insert('1.0', detail_text)
        
        # ✅ UPDATE BUTTON STATE SESUAI STATUS PESANAN
        self.update_waiter_button_state()



    # Wilayah dikuasai Favorite


    def favorite_update(user_id, menu_id):
        """Update atau tambah favorite count untuk user tertentu"""
        from datetime import datetime
        
        rows = read_all(FAVORITE_CSV)
        found = False
        
        for r in rows:
            if r.get("user_id") == str(user_id) and r.get("menu_id") == str(menu_id):
                # Update count
                try:
                    count = int(r.get("order_count") or 0)
                except:
                    count = 0
                r["order_count"] = str(count + 1)
                r["last_ordered"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                found = True
                break
        
        if not found:
            # Tambah baru
            rows.append({
                "user_id": str(user_id),
                "menu_id": str(menu_id),
                "order_count": "1",
                "last_ordered": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            })
        
        write_all(FAVORITE_CSV, ["user_id", "menu_id", "order_count", "last_ordered"], rows)


    def favorite_list(user_id, limit=5):
        """Ambil menu favorit user, sorted by order_count descending"""
        rows = read_all(FAVORITE_CSV)
        out = []
        
        for r in rows:
            if r.get("user_id") == str(user_id):
                try:
                    mid = int(r.get("menu_id") or 0)
                except:
                    mid = r.get("menu_id")
                try:
                    count = int(r.get("order_count") or 0)
                except:
                    count = 0
                
                last_ordered = r.get("last_ordered")
                out.append((mid, count, last_ordered))
        
        # Sort by count descending
        out.sort(key=lambda x: x[1], reverse=True)
        
        # Limit results
        if limit:
            out = out[:limit]
        
        return out


    def favorite_all(limit=10):
        """Ambil menu paling populer dari semua user"""
        rows = read_all(FAVORITE_CSV)
        menu_counts = {}
        
        for r in rows:
            menu_id = r.get("menu_id")
            try:
                count = int(r.get("order_count") or 0)
            except:
                count = 0
            
            if menu_id in menu_counts:
                menu_counts[menu_id] += count
            else:
                menu_counts[menu_id] = count
        
        # Convert to list dan sort
        out = []
        for menu_id, total_count in menu_counts.items():
            try:
                mid = int(menu_id)
            except:
                mid = menu_id
            out.append((mid, total_count))
        
        out.sort(key=lambda x: x[1], reverse=True)
        
        if limit:
            out = out[:limit]
        
        return out



    # WILAYAH DIKUASAI PEMBAYARAN & MEJA

    # === FUNGSI PEMBAYARAN ===


    def pembayaran_add(transaksi_id, metode_pembayaran, jumlah_bayar, status_pembayaran='sukses', struk=''):
        """Simpan data pembayaran baru"""
        from datetime import datetime
        
        rows = read_all(PEMBAYARAN_CSV)
        new_id = next_int_id(rows, "id")
        tanggal_bayar = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        
        rows.append({
            "id": new_id,
            "transaksi_id": str(transaksi_id),
            "metode_pembayaran": metode_pembayaran,
            "jumlah_bayar": str(float(jumlah_bayar)),
            "status_pembayaran": status_pembayaran,
            "tanggal_bayar": tanggal_bayar,
            "struk": struk
        })
        
        write_all(PEMBAYARAN_CSV, ["id", "transaksi_id", "metode_pembayaran", "jumlah_bayar", "status_pembayaran", "tanggal_bayar", "struk"], rows)
        return new_id


    def pembayaran_get_by_transaksi(transaksi_id):
        """Ambil data pembayaran berdasarkan transaksi_id"""
        rows = read_all(PEMBAYARAN_CSV)
        for r in rows:
            if r.get("transaksi_id") == str(transaksi_id):
                try:
                    pid = int(r.get("id") or 0)
                except:
                    pid = r.get("id")
                try:
                    jumlah = float(r.get("jumlah_bayar") or 0.0)
                except:
                    jumlah = 0.0
                
                return (pid, r.get("metode_pembayaran"), jumlah, r.get("status_pembayaran"), r.get("tanggal_bayar"), r.get("struk"))
        return None


    def pembayaran_list(status=None, metode=None):
        """Ambil semua pembayaran dengan filter opsional"""
        rows = read_all(PEMBAYARAN_CSV)
        out = []
        
        for r in rows:
            if status and r.get("status_pembayaran") != status:
                continue
            if metode and r.get("metode_pembayaran") != metode:
                continue
            
            try:
                pid = int(r.get("id") or 0)
            except:
                pid = r.get("id")
            try:
                tid = int(r.get("transaksi_id") or 0)
            except:
                tid = r.get("transaksi_id")
            try:
                jumlah = float(r.get("jumlah_bayar") or 0.0)
            except:
                jumlah = 0.0
            
            out.append((pid, tid, r.get("metode_pembayaran"), jumlah, r.get("status_pembayaran"), r.get("tanggal_bayar")))
        
        out.sort(key=lambda x: int(x[0]), reverse=True)
        return out



    # === FUNGSI MEJA ===


    def meja_list(status=None):
        """Ambil daftar meja dengan filter status opsional"""
        rows = read_all(MEJA_CSV)
        out = []
        
        for r in rows:
            if status and r.get("status") != status:
                continue
            
            try:
                nomor = int(r.get("nomor_meja") or 0)
            except:
                nomor = r.get("nomor_meja")
            
            transaksi_id = r.get("transaksi_id") or ""
            
            out.append((nomor, r.get("status"), transaksi_id))
        
        out.sort(key=lambda x: int(x[0]) if isinstance(x[0], int) else 0)
        return out


    def meja_get(nomor_meja):
        """Ambil data meja berdasarkan nomor"""
        rows = read_all(MEJA_CSV)
        for r in rows:
            if r.get("nomor_meja") == str(nomor_meja):
                try:
                    nomor = int(r.get("nomor_meja") or 0)
                except:
                    nomor = r.get("nomor_meja")
                
                transaksi_id = r.get("transaksi_id") or ""
                return (nomor, r.get("status"), transaksi_id)
        return None


    def meja_update_status(nomor_meja, new_status, transaksi_id=""):
        """Update status meja (kosong/terisi) dan link ke transaksi"""
        rows = read_all(MEJA_CSV)
        found = False
        
        for r in rows:
            if r.get("nomor_meja") == str(nomor_meja):
                r["status"] = new_status
                r["transaksi_id"] = str(transaksi_id) if transaksi_id else ""
                found = True
                break
        
        if found:
            write_all(MEJA_CSV, ["nomor_meja", "status", "transaksi_id"], rows)
            return True
        return False


    def meja_tutup(nomor_meja):
        """Tutup meja (set ke kosong)"""
        return meja_update_status(nomor_meja, "kosong", "")


    def meja_buka(nomor_meja, transaksi_id):
        """Buka meja (set ke terisi dengan transaksi_id)"""
        return meja_update_status(nomor_meja, "terisi", transaksi_id)


    def build_payment_tab(self, parent, readonly=False):
        """Tab pembayaran untuk kasir & admin
        
        Args:
            parent: Parent frame
            readonly: Jika True, form disabled (untuk admin monitoring)
        """
        for w in parent.winfo_children():
            w.destroy()
        
        # Header
        header = ttk.Frame(parent)
        header.pack(fill='x', padx=10, pady=8)
        
        # ✅ UBAH JUDUL SESUAI MODE
        if readonly:
            ttk.Label(
                header,
                text="👁️ Monitor Transaksi (Read-Only)",
                font=("Arial", 14, "bold"),
                foreground='orange'
            ).pack(side='left')
        else:
            ttk.Label(
                header,
                text="💰 Pembayaran Transaksi",
                font=("Arial", 14, "bold")
            ).pack(side='left')
        
        ttk.Button(
            header,
            text="🔄 Refresh",
            command=self.reload_payment_orders
        ).pack(side='right', padx=6)
        
        # Search by nomor meja (TETAP AKTIF untuk admin)
        search_frame = ttk.LabelFrame(parent, text="🔍 Cari Transaksi by Nomor Meja", padding=10)
        search_frame.pack(fill='x', padx=10, pady=6)
        
        search_inner = ttk.Frame(search_frame)
        search_inner.pack()
        
        ttk.Label(search_inner, text="Nomor Meja:", font=("Arial", 9)).grid(row=0, column=0, padx=5)
        self.search_meja_var = tk.StringVar()
        ttk.Entry(search_inner, textvariable=self.search_meja_var, width=15).grid(row=0, column=1, padx=5)
        ttk.Button(
            search_inner,
            text="🔍 Cari Tagihan",
            command=self.search_by_meja,
            style="Accent.TButton"
        ).grid(row=0, column=2, padx=5)
        ttk.Button(
            search_inner,
            text="🔄 Tampilkan Semua",
            command=self.reload_payment_orders
        ).grid(row=0, column=3, padx=5)
        
        # Summary penjualan hari ini (untuk kasir & admin)
        if self.session['role'] in ['kasir', 'admin']:
            summary_frame = ttk.LabelFrame(parent, text="📊 Penjualan Hari Ini", padding=10)
            summary_frame.pack(fill='x', padx=10, pady=6)
            
            summary_inner = ttk.Frame(summary_frame)
            summary_inner.pack()
            
            from datetime import datetime
            today_income = 0
            today_count = 0
            
            today_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
            all_transaksi = transaksi_list(status='dibayar')
            
            for trx in all_transaksi:
                tid, uid, meja, total, status, promo_code, tanggal = trx
                try:
                    trx_date = datetime.strptime(tanggal, "%Y-%m-%d %H:%M:%S")
                    if trx_date >= today_start:
                        today_income += total
                        today_count += 1
                except:
                    pass
            
            ttk.Label(summary_inner, text="Total Transaksi Hari Ini:", font=("Arial", 10)).grid(row=0, column=0, sticky='w', padx=10, pady=3)
            ttk.Label(summary_inner, text=str(today_count), font=("Arial", 10, "bold"), foreground='blue').grid(row=0, column=1, sticky='w', padx=10, pady=3)
            
            ttk.Label(summary_inner, text="Total Pendapatan Hari Ini:", font=("Arial", 10)).grid(row=1, column=0, sticky='w', padx=10, pady=3)
            ttk.Label(summary_inner, text=f"Rp {today_income:,.0f}", font=("Arial", 10, "bold"), foreground='green').grid(row=1, column=1, sticky='w', padx=10, pady=3)
        
        # Container utama
        main_container = ttk.Frame(parent)
        main_container.pack(fill='both', expand=True, padx=10, pady=6)
        
        # Panel Kiri: Daftar & Detail Transaksi (Menggunakan PanedWindow)
        left = ttk.LabelFrame(main_container, text="📋 Daftar & Detail Transaksi", padding=5)
        left.pack(side='left', fill='both', expand=True, padx=(0, 5))
        
        # 1. Buat PanedWindow (Splitter Vertical)
        paned = ttk.PanedWindow(left, orient='vertical')
        paned.pack(fill='both', expand=True)
        
        # --- BAGIAN ATAS: TREEVIEW (DAFTAR TRANSAKSI) ---
        tree_frame = ttk.Frame(paned)
        
        tree_scroll = ttk.Scrollbar(tree_frame, orient='vertical')
        tree_scroll.pack(side='right', fill='y')
        
        cols = ("ID", "Meja", "Total", "Status", "Tanggal")
        self.payment_tree = ttk.Treeview(
            tree_frame,
            columns=cols,
            show='headings',
            height=8,
            yscrollcommand=tree_scroll.set
        )
        
        tree_scroll.config(command=self.payment_tree.yview)
        
        self.payment_tree.heading("ID", text="ID")
        self.payment_tree.heading("Meja", text="Meja")
        self.payment_tree.heading("Total", text="Total")
        self.payment_tree.heading("Status", text="Status")
        self.payment_tree.heading("Tanggal", text="Tanggal")
        
        self.payment_tree.column("ID", width=50)
        self.payment_tree.column("Meja", width=60)
        self.payment_tree.column("Total", width=100)
        self.payment_tree.column("Status", width=80)
        self.payment_tree.column("Tanggal", width=120)
        
        self.payment_tree.pack(side='left', fill='both', expand=True)
        self.payment_tree.bind("<<TreeviewSelect>>", self.on_payment_select)
        
        # Tambahkan frame tree ke panel atas (bobot besar)
        paned.add(tree_frame, weight=3)
        
        # --- BAGIAN BAWAH: DETAIL TRANSAKSI ---
        detail_frame = ttk.Frame(paned)
        
        # Label instruksi kecil
        ttk.Label(detail_frame, text="Detail Transaksi (Geser batas atas untuk memperbesar):", font=("Arial", 9, "bold")).pack(anchor='w', pady=(5,0))
        
        # Area Text & Scrollbar
        text_container = ttk.Frame(detail_frame)
        text_container.pack(fill='both', expand=True)
        
        # Scrollbar WAJIB dipack duluan sebelum Text
        detail_scroll = ttk.Scrollbar(text_container, orient='vertical')
        detail_scroll.pack(side='right', fill='y') 
        
        self.payment_detail_text = tk.Text(text_container, height=8, font=("Courier New", 9), wrap='word')
        self.payment_detail_text.configure(yscrollcommand=detail_scroll.set)
        detail_scroll.config(command=self.payment_detail_text.yview)
        
        self.payment_detail_text.pack(side='left', fill='both', expand=True)
        
        # Tambahkan frame detail ke panel bawah (bobot kecil)
        paned.add(detail_frame, weight=1)
        
        # ✅ JIKA READ-ONLY, HANYA TAMPILKAN INFO, TANPA FORM PEMBAYARAN
        if readonly:
            readonly_frame = ttk.LabelFrame(main_container, text="ℹ️ Informasi", padding=20)
            readonly_frame.pack(side='right', fill='both', expand=True, padx=(5, 0))
            
            warning_text = (
                "⚠️ MODE READ-ONLY\n\n"
                "Anda hanya bisa melihat data transaksi.\n\n"
                "Untuk memproses pembayaran, silakan login sebagai KASIR.\n\n"
                "Admin hanya bisa monitoring transaksi untuk audit dan kontrol."
            )
            
            ttk.Label(
                readonly_frame,
                text=warning_text,
                font=("Arial", 10),
                foreground='red',
                justify='center'
            ).pack(expand=True)
        else:
            # Panel Kanan: Form Pembayaran (HANYA UNTUK KASIR)
            right_outer = ttk.LabelFrame(main_container, text="💳 Form Pembayaran", padding=5)
            right_outer.pack(side='right', fill='both', expand=True, padx=(5, 0))
            
            wrapper = ttk.Frame(right_outer)
            wrapper.pack(fill='both', expand=True)
            
            scrollbar = ttk.Scrollbar(wrapper, orient='vertical')
            scrollbar.pack(side='right', fill='y')
            
            canvas = tk.Canvas(wrapper, yscrollcommand=scrollbar.set, highlightthickness=0, height=450)
            canvas.pack(side='left', fill='both', expand=True)
            
            scrollbar.config(command=canvas.yview)
            
            right = ttk.Frame(canvas)
            canvas_window = canvas.create_window((0, 0), window=right, anchor='nw')
            
            def configure_scroll(event):
                canvas.configure(scrollregion=canvas.bbox('all'))
            
            right.bind('<Configure>', configure_scroll)
            
            def configure_canvas(event):
                canvas.itemconfig(canvas_window, width=event.width)
            
            canvas.bind('<Configure>', configure_canvas)
            
            def on_mousewheel(event):
                canvas.yview_scroll(-1 * int(event.delta / 120), "units")
            
            canvas.bind_all('<MouseWheel>', on_mousewheel)
            
            # Info transaksi
            info_frame = ttk.Frame(right)
            info_frame.pack(fill='x', pady=15, padx=15)
            
            self.selected_transaksi_label = ttk.Label(
                info_frame,
                text="Belum ada transaksi dipilih",
                font=("Arial", 10, "bold"),
                foreground='red'
            )
            self.selected_transaksi_label.pack()
            
            self.selected_total_label = ttk.Label(
                info_frame,
                text="Total: Rp 0",
                font=("Arial", 12, "bold"),
                foreground='green'
            )
            self.selected_total_label.pack(pady=4)
            
            ttk.Separator(right, orient='horizontal').pack(fill='x', pady=15, padx=15)
            
            # Metode pembayaran
            method_frame = ttk.Frame(right)
            method_frame.pack(fill='x', pady=10, padx=15)
            
            ttk.Label(method_frame, text="💳 Pilih Metode Pembayaran:", font=("Arial", 10, "bold")).pack(anchor='w', pady=6)
            
            self.payment_method_var = tk.StringVar(value='cash')
            
            ttk.Radiobutton(method_frame, text="💵 Cash", variable=self.payment_method_var, value='cash', command=self.on_payment_method_change).pack(anchor='w', pady=3)
            ttk.Radiobutton(method_frame, text="📱 QRIS", variable=self.payment_method_var, value='qris', command=self.on_payment_method_change).pack(anchor='w', pady=3)
            ttk.Radiobutton(method_frame, text="💳 E-Wallet (GoPay/OVO/Dana)", variable=self.payment_method_var, value='ewallet', command=self.on_payment_method_change).pack(anchor='w', pady=3)
            
            ttk.Separator(right, orient='horizontal').pack(fill='x', pady=15, padx=15)
            
            # Frame input dinamis
            self.payment_input_frame = ttk.Frame(right)
            self.payment_input_frame.pack(fill='x', pady=10, padx=15)
            
            self.build_cash_input()
            
            ttk.Separator(right, orient='horizontal').pack(fill='x', pady=15, padx=15)
            
            # Tombol
            btn_frame = ttk.Frame(right)
            btn_frame.pack(fill='x', pady=20, padx=15)
            
            ttk.Button(
                btn_frame,
                text="✅ PROSES PEMBAYARAN",
                command=self.process_payment,
                style="Accent.TButton"
            ).pack(ipadx=30, ipady=10)
            
            for i in range(20):
                ttk.Label(right, text="").pack()
        
        # Load data
        self.reload_payment_orders()


    def search_by_meja(self):
        """Cari transaksi berdasarkan nomor meja - FITUR BARU!"""
        nomor_meja = self.search_meja_var.get().strip()
        
        if not nomor_meja:
            messagebox.showwarning("Input Error", "Masukkan nomor meja")
            return
        
        try:
            nomor_meja = int(nomor_meja)
        except:
            messagebox.showerror("Input Error", "Nomor meja harus angka")
            return
        
        # Validasi range meja
        if nomor_meja < 1 or nomor_meja > 10:
            messagebox.showwarning("Invalid", "Nomor meja harus 1-10")
            return
        
        # Clear tree
        for r in self.payment_tree.get_children():
            self.payment_tree.delete(r)
        
        # Get transaksi selesai untuk meja ini
        orders = transaksi_list(status='selesai')
        
        found = False
        for order in orders:
            tid, uid, meja, total, status, promo_code, tanggal = order
            
            # Filter by nomor meja
            if meja != nomor_meja:
                continue
            
            # Cek belum dibayar
            payment_data = pembayaran_get_by_transaksi(tid)
            if payment_data:
                continue  # Skip yang sudah dibayar
            
            self.payment_tree.insert(
                "",
                tk.END,
                values=(tid, meja, f"Rp {total:,.0f}", status, tanggal)
            )
            found = True
        
        if not found:
            messagebox.showinfo(
                "Tidak Ditemukan", 
                f"❌ Tidak ada tagihan aktif untuk Meja {nomor_meja}\n\n"
                f"Kemungkinan:\n"
                f"• Meja belum pesan atau pesanan belum selesai\n"
                f"• Tagihan sudah dibayar\n"
                f"• Meja tidak ada pesanan"
            )
            
            # Tampilkan semua transaksi lagi
            self.reload_payment_orders()
        else:
            messagebox.showinfo(
                "✅ Ditemukan", 
                f"Tagihan untuk Meja {nomor_meja} berhasil ditemukan!\n\n"
                f"Silakan pilih transaksi dan proses pembayaran."
            )

    def build_report_tab(self, parent): 
        """Tab laporan penjualan untuk admin/pemilik"""
        for w in parent.winfo_children():
            w.destroy()
        
        # Header
        header = ttk.Frame(parent)
        header.pack(fill='x', padx=10, pady=8)
        ttk.Label(header, text="📊 Laporan Penjualan", font=("Arial", 14, "bold")).pack(side='left')
        ttk.Button(header, text="🔄 Refresh", command=self.reload_report).pack(side='right', padx=6)
        
        # Filter frame
        filter_frame = ttk.LabelFrame(parent, text="🔍 Filter Laporan", padding=10)
        filter_frame.pack(fill='x', padx=10, pady=6)
        
        # Row 0: Periode
        ttk.Label(filter_frame, text="Periode:").grid(row=0, column=0, sticky='w', padx=5, pady=4)
        self.report_period_var = tk.StringVar(value='harian')
        
        period_frame = ttk.Frame(filter_frame)
        period_frame.grid(row=0, column=1, sticky='w', padx=5, pady=4)
        
        ttk.Radiobutton(period_frame, text="Hari Ini", variable=self.report_period_var, value='harian').pack(side='left', padx=5)
        ttk.Radiobutton(period_frame, text="Minggu Ini", variable=self.report_period_var, value='mingguan').pack(side='left', padx=5)
        ttk.Radiobutton(period_frame, text="Bulan Ini", variable=self.report_period_var, value='bulanan').pack(side='left', padx=5)
        
        # Row 1: Metode Pembayaran
        ttk.Label(filter_frame, text="Metode:").grid(row=1, column=0, sticky='w', padx=5, pady=4)
        self.report_method_var = tk.StringVar(value='semua')
        
        
        method_combo = ttk.Combobox(filter_frame, textvariable=self.report_method_var, width=20, state='readonly')
        method_combo['values'] = (
            'Semua',              
            'Cash',               
            'Qris',
            'Ewallet-gopay',
            'Ewallet-ovo',
            'Ewallet-dana',
            'Ewallet-shopeepay'
)
        method_combo.grid(row=1, column=1, sticky='w', padx=5, pady=4)
        
        # Tombol generate
        ttk.Button(
            filter_frame,
            text="📊 Generate Laporan",
            command=self.generate_report,
            style="Accent.TButton"
        ).grid(row=2, column=0, columnspan=2, pady=10)
        ttk.Button(
            filter_frame,
            text="💾 Export Laporan",
            command=self.export_report_to_file,
            style="Accent.TButton"
            ).grid(row=3, column=0, pady=(10, 5))
        
        ttk.Button(
            filter_frame,
            text="🖼️ Export Grafik (PNG)",
            command=self.export_report_chart,
            style="Accent.TButton"
        ).grid(row=4, column=0, pady=5)
        
        # Summary frame
        summary_frame = ttk.LabelFrame(parent, text="📈 Ringkasan", padding=10)
        summary_frame.pack(fill='x', padx=10, pady=6)
        
        summary_inner = ttk.Frame(summary_frame)
        summary_inner.pack()
        
        # Labels untuk ringkasan
        ttk.Label(summary_inner, text="Total Transaksi:").grid(row=0, column=0, sticky='w', padx=10, pady=3)
        self.report_total_trx_label = ttk.Label(summary_inner, text="0", font=("Arial", 10, "bold"))
        self.report_total_trx_label.grid(row=0, column=1, sticky='w', padx=10, pady=3)
        
        ttk.Label(summary_inner, text="Total Pendapatan:").grid(row=1, column=0, sticky='w', padx=10, pady=3)
        self.report_total_income_label = ttk.Label(
            summary_inner,
            text="Rp 0",
            font=("Arial", 10, "bold"),
            foreground='green'
        )
        self.report_total_income_label.grid(row=1, column=1, sticky='w', padx=10, pady=3)
        
        ttk.Label(summary_inner, text="Rata-rata/Transaksi:").grid(row=2, column=0, sticky='w', padx=10, pady=3)
        self.report_avg_label = ttk.Label(summary_inner, text="Rp 0", font=("Arial", 10, "bold"))
        self.report_avg_label.grid(row=2, column=1, sticky='w', padx=10, pady=3)
        
        # Detail tabel
        detail_frame = ttk.LabelFrame(parent, text="📋 Detail Transaksi", padding=10)
        detail_frame.pack(fill='both', expand=True, padx=10, pady=6)
        
        # Treeview
        tree_frame = ttk.Frame(detail_frame)
        tree_frame.pack(fill='both', expand=True)
        
        tree_scroll = ttk.Scrollbar(tree_frame, orient='vertical')
        tree_scroll.pack(side='right', fill='y')
        
        cols = ("ID", "Tanggal", "Meja", "Total", "Metode", "Status")
        self.report_tree = ttk.Treeview(
            tree_frame,
            columns=cols,
            show='headings',
            height=10,
            yscrollcommand=tree_scroll.set
        )
        
        tree_scroll.config(command=self.report_tree.yview)
        
        self.report_tree.heading("ID", text="ID")
        self.report_tree.heading("Tanggal", text="Tanggal")
        self.report_tree.heading("Meja", text="Meja")
        self.report_tree.heading("Total", text="Total")
        self.report_tree.heading("Metode", text="Metode")
        self.report_tree.heading("Status", text="Status")
        
        self.report_tree.column("ID", width=50)
        self.report_tree.column("Tanggal", width=140)
        self.report_tree.column("Meja", width=60)
        self.report_tree.column("Total", width=100)
        self.report_tree.column("Metode", width=120)
        self.report_tree.column("Status", width=80)
        
        self.report_tree.pack(side='left', fill='both', expand=True)
        
        # Tombol grafik
        btn_frame = ttk.Frame(parent)
        btn_frame.pack(pady=10)
        ttk.Button(btn_frame, text="📊 Tampilkan Grafik", command=self.show_report_chart).pack()

    def build_user_manage_tab(self, parent):
        """Tab kelola user (admin bisa tambah/edit kasir)"""
        for w in parent.winfo_children():
            w.destroy()
        
        # Header
        header = ttk.Frame(parent)
        header.pack(fill='x', padx=10, pady=8)
        ttk.Label(header, text="👥 Kelola User & Kasir", font=("Arial", 14, "bold")).pack(side='left')
        ttk.Button(header, text="➕ Tambah User", command=self.open_add_user_window).pack(side='right', padx=6)
        
        # Treeview user
        tree_frame = ttk.Frame(parent)
        tree_frame.pack(fill='both', expand=True, padx=10, pady=6)
        
        tree_scroll = ttk.Scrollbar(tree_frame, orient='vertical')
        tree_scroll.pack(side='right', fill='y')
        
        cols = ("ID", "Username", "Role")
        self.user_tree = ttk.Treeview(tree_frame, columns=cols, show='headings', height=15, yscrollcommand=tree_scroll.set)
        
        tree_scroll.config(command=self.user_tree.yview)
        
        self.user_tree.heading("ID", text="ID")
        self.user_tree.heading("Username", text="Username")
        self.user_tree.heading("Role", text="Role")
        
        self.user_tree.column("ID", width=80)
        self.user_tree.column("Username", width=200)
        self.user_tree.column("Role", width=150)
        
        self.user_tree.pack(side='left', fill='both', expand=True)
        
        # Tombol aksi
        btn_frame = ttk.Frame(parent)
        btn_frame.pack(pady=10)
        
        ttk.Button(btn_frame, text="✏️ Edit User", command=self.open_edit_user_window).pack(side='left', padx=5)
        ttk.Button(btn_frame, text="🗑️ Hapus User", command=self.delete_selected_user).pack(side='left', padx=5)
        ttk.Button(btn_frame, text="🔄 Refresh", command=self.reload_user_table).pack(side='left', padx=5)
        
        # Load data
        self.reload_user_table()
    
    def reload_user_table(self):
        """Reload tabel user"""
        # Clear tree
        for r in self.user_tree.get_children():
            self.user_tree.delete(r)
        
        # Get users
        users = read_all(USERS_CSV)
        
        for user in users:
            uid = user.get('id')
            username = user.get('username')
            role = user.get('role')
            
            self.user_tree.insert("", tk.END, values=(uid, username, role))
    
    def open_add_user_window(self):
        """Popup untuk tambah user baru"""
        w = tk.Toplevel(self.root)
        w.title("➕ Tambah User Baru")
        w.geometry("400x280")
        w.transient(self.root)
        w.grab_set()
        
        frm = ttk.Frame(w, padding=15)
        frm.pack(fill='both', expand=True)
        
        ttk.Label(frm, text="Tambah User Baru", font=("Arial", 12, "bold")).pack(pady=10)
        
        # Username
        ttk.Label(frm, text="Username:").pack(anchor='w', pady=(10, 2))
        username_var = tk.StringVar()
        ttk.Entry(frm, textvariable=username_var, width=30).pack(fill='x', pady=(0, 10))
        
        # Password
        ttk.Label(frm, text="Password:").pack(anchor='w', pady=2)
        password_var = tk.StringVar()
        ttk.Entry(frm, textvariable=password_var, show="*", width=30).pack(fill='x', pady=(0, 10))
        
        # Role
        ttk.Label(frm, text="Role:").pack(anchor='w', pady=2)
        role_var = tk.StringVar(value='kasir')
        
        role_frame = ttk.Frame(frm)
        role_frame.pack(fill='x', pady=(0, 10))
        
        ttk.Radiobutton(role_frame, text="Kasir", variable=role_var, value='kasir').pack(side='left', padx=5)
        ttk.Radiobutton(role_frame, text="Waiter", variable=role_var, value='waiter').pack(side='left', padx=5)
        ttk.Radiobutton(role_frame, text="Pembeli", variable=role_var, value='pembeli').pack(side='left', padx=5)
        
        def save_user():
            username = username_var.get().strip()
            password = password_var.get().strip()
            role = role_var.get()
            
            if not username or not password:
                messagebox.showerror("Error", "Username dan password harus diisi!")
                return
            
            # Cek username sudah ada
            users = read_all(USERS_CSV)
            for u in users:
                if u.get('username') == username:
                    messagebox.showerror("Error", f"Username '{username}' sudah digunakan!")
                    return
            
            # Tambah user
            new_id = next_int_id(users, 'id')
            users.append({
                'id': new_id,
                'username': username,
                'password': password,
                'role': role
            })
            
            write_all(USERS_CSV, ["id", "username", "password", "role"], users)
            
            messagebox.showinfo("Sukses", f"User '{username}' berhasil ditambahkan!")
            w.destroy()
            self.reload_user_table()
        
        ttk.Button(frm, text="💾 Simpan", command=save_user, style="Accent.TButton").pack(pady=15)
    
    def open_edit_user_window(self):
        """Popup untuk edit user"""
        sel = self.user_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih User", "Pilih user yang akan diedit")
            return
        
        item = self.user_tree.item(sel)['values']
        user_id = item[0]
        
        # Get user data
        users = read_all(USERS_CSV)
        user_data = None
        for u in users:
            if u.get('id') == str(user_id):
                user_data = u
                break
        
        if not user_data:
            messagebox.showerror("Error", "User tidak ditemukan")
            return
        
        w = tk.Toplevel(self.root)
        w.title("✏️ Edit User")
        w.geometry("400x280")
        w.transient(self.root)
        w.grab_set()
        
        frm = ttk.Frame(w, padding=15)
        frm.pack(fill='both', expand=True)
        
        ttk.Label(frm, text=f"Edit User: {user_data.get('username')}", font=("Arial", 12, "bold")).pack(pady=10)
        
        # Password baru
        ttk.Label(frm, text="Password Baru (kosongkan jika tidak diubah):").pack(anchor='w', pady=2)
        password_var = tk.StringVar()
        ttk.Entry(frm, textvariable=password_var, show="*", width=30).pack(fill='x', pady=(0, 10))
        
        # Role
        ttk.Label(frm, text="Role:").pack(anchor='w', pady=2)
        role_var = tk.StringVar(value=user_data.get('role'))
        
        role_frame = ttk.Frame(frm)
        role_frame.pack(fill='x', pady=(0, 10))
        
        ttk.Radiobutton(role_frame, text="Kasir", variable=role_var, value='kasir').pack(side='left', padx=5)
        ttk.Radiobutton(role_frame, text="Waiter", variable=role_var, value='waiter').pack(side='left', padx=5)
        ttk.Radiobutton(role_frame, text="Pembeli", variable=role_var, value='pembeli').pack(side='left', padx=5)
        ttk.Radiobutton(role_frame, text="Admin", variable=role_var, value='admin').pack(side='left', padx=5)
        
        def update_user():
            new_password = password_var.get().strip()
            new_role = role_var.get()
            
            # Update user
            for u in users:
                if u.get('id') == str(user_id):
                    if new_password:
                        u['password'] = new_password
                    u['role'] = new_role
                    break
            
            write_all(USERS_CSV, ["id", "username", "password", "role"], users)
            
            messagebox.showinfo("Sukses", "User berhasil diupdate!")
            w.destroy()
            self.reload_user_table()
        
        ttk.Button(frm, text="💾 Update", command=update_user, style="Accent.TButton").pack(pady=15)
    
    def delete_selected_user(self):
        """Hapus user yang dipilih"""
        sel = self.user_tree.selection()
        if not sel:
            messagebox.showwarning("Pilih User", "Pilih user yang akan dihapus")
            return
        
        item = self.user_tree.item(sel)['values']
        user_id = item[0]
        username = item[1]
        
        # Cek jangan hapus admin
        if username == 'admin':
            messagebox.showerror("Error", "Tidak bisa menghapus user admin!")
            return
        
        # Konfirmasi
        if not messagebox.askyesno("Konfirmasi", f"Hapus user '{username}'?"):
            return
        
        # Hapus
        users = read_all(USERS_CSV)
        users = [u for u in users if u.get('id') != str(user_id)]
        
        write_all(USERS_CSV, ["id", "username", "password", "role"], users)
        
        messagebox.showinfo("Sukses", f"User '{username}' berhasil dihapus!")
        self.reload_user_table()

    
    def reload_report(self):
        """Reload data laporan"""
        self.generate_report()
    
    
    def generate_report(self):
        """Generate laporan dengan data terstruktur"""
        from datetime import datetime
        
        # Clear tree lama
        for r in self.report_tree.get_children():
            self.report_tree.delete(r)
        
        period = self.report_period_var.get()
        method_filter = self.report_method_var.get().lower()
        
        try:
            # GUNAKAN HELPER FUNCTIONS 
            if period == 'harian':
                report_data = report_get_daily_summary(method_filter=method_filter)
            elif period == 'mingguan':
                report_data = report_get_weekly_summary(method_filter=method_filter)
            else:  # bulanan
                report_data = report_get_monthly_summary(method_filter=method_filter)
        except Exception as e:
            messagebox.showerror("Error", f"Gagal generate laporan: {e}")
            return
        
        # Update summary labels dari report_data (BUKAN manual count!)
        self.report_total_trx_label.config(
            text=str(report_data['total_transaksi'])
        )
        self.report_total_income_label.config(
            text=format_currency(report_data['total_pendapatan'])
        )
        self.report_avg_label.config(
            text=format_currency(report_data['rata_rata'])
        )
        
        # Populate tree dari payment details
        for payment in report_data.get('details', []):
            pid, tid, metode, jumlah, status, tanggal = payment
            nomor_meja = "-"
            trx_data = transaksi_get(tid) # Ambil data transaksi berdasarkan ID
            if trx_data:
                # trx_data = (tid, uid, meja, total, status, ...)
                nomor_meja = trx_data[2] # Index 2 adalah nomor meja
            # ----------------------------------

            self.report_tree.insert("", tk.END, values=(
                tid,
                format_datetime(tanggal),
                nomor_meja,  # <--- Ganti "-" dengan variabel nomor_meja
                format_currency(jumlah),
                metode.upper() if metode else "-",
                status
            ))


    def export_report_to_file(self):
        """Export laporan ke file text - FUNGSI BARU"""
        from datetime import datetime
        
        period = self.report_period_var.get()
        
        try:
            # Get report data
            if period == 'harian':
                report_data = report_get_daily_summary()
            elif period == 'mingguan':
                report_data = report_get_weekly_summary()
            else:
                report_data = report_get_monthly_summary()
        except:
            messagebox.showerror("Error", "Belum ada data laporan. Generate terlebih dahulu!")
            return
        
        # Export ke text
        text = report_export_to_text(report_data, report_type=period)
        
        # Save ke file
        filename = f"LAPORAN_{period}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(text)
            messagebox.showinfo(
                "✅ Berhasil",
                f"Laporan berhasil disimpan:\n{filename}\n\nBuka file untuk melihat detail lengkap."
            )
        except Exception as e:
            messagebox.showerror("❌ Error", f"Gagal menyimpan laporan: {e}")

    
    def show_report_chart(self):
        """Tampilkan grafik laporan dengan breakdown metode + status"""
        try:
            import matplotlib.pyplot as plt
            from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
            from datetime import datetime
            
            # Get data from tree
            if not self.report_tree.get_children():
                messagebox.showwarning("Tidak Ada Data", "Generate laporan terlebih dahulu")
                return
            
            # Collect data untuk breakdown
            metode_counts = {}
            status_counts = {}  # ← TAMBAH INI (NEW)
            total_per_day = {}
            
            for item_id in self.report_tree.get_children():
                values = self.report_tree.item(item_id)['values']
                tid, tanggal, meja, total_str, metode, status = values
                
                # Parse total
                total = float(total_str.replace('Rp ', '').replace(',', '').replace('.', ''))
                
                # Count by metode
                metode_counts[metode] = metode_counts.get(metode, 0) + 1
                
                # NEW: Count by status pembayaran
                status_key = status.upper()
                status_counts[status_key] = status_counts.get(status_key, 0) + 1
                
                # Sum by date
                try:
                    date_obj = datetime.strptime(tanggal, "%Y-%m-%d %H:%M:%S")
                    date_key = date_obj.strftime("%Y-%m-%d")
                    total_per_day[date_key] = total_per_day.get(date_key, 0) + total
                except:
                    pass
            
            # Create window
            chart_window = tk.Toplevel(self.root)
            chart_window.title("📊 Grafik Laporan Penjualan")
            chart_window.geometry("1000x650")
            
            # Create figure with 3 subplots (atau 2 baris x 2 kolom)
            fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 10))
            fig.suptitle('📊 LAPORAN PENJUALAN - DASHBOARD GRAFIK', fontsize=16, fontweight='bold')
            
            # ===== CHART 1: Pie chart metode pembayaran (KIRI ATAS) =====
            if metode_counts:
                labels = list(metode_counts.keys())
                sizes = list(metode_counts.values())
                colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8']
                explode = (0.05,) * len(labels)  # Sedikit separation
                
                ax1.pie(sizes, labels=labels, autopct='%1.1f%%', colors=colors, 
                       startangle=90, explode=explode, shadow=True)
                ax1.set_title('💳 Transaksi per Metode Pembayaran', 
                             fontsize=12, fontweight='bold', pad=20)
            
            # ===== CHART 2: Pie chart status pembayaran (KANAN ATAS) - NEW! =====
            if status_counts:
                labels_status = list(status_counts.keys())
                sizes_status = list(status_counts.values())
                # Warna per status
                color_map = {
                    'SUKSES': '#4CAF50',
                    'GAGAL': '#F44336', 
                    'PENDING': '#FFC107',
                    'DIBAYAR': '#2196F3'
                }
                colors_status = [color_map.get(label, '#757575') for label in labels_status]
                
                ax2.pie(sizes_status, labels=labels_status, autopct='%1.1f%%', 
                       colors=colors_status, startangle=90, shadow=True)
                ax2.set_title('✓ Breakdown Status Pembayaran', 
                             fontsize=12, fontweight='bold', pad=20)
            
            # ===== CHART 3: Bar chart pendapatan per hari (KIRI BAWAH) =====
            if total_per_day:
                dates = sorted(total_per_day.keys())
                totals = [total_per_day[d] for d in dates]
                
                date_labels = [
                    datetime.strptime(d, "%Y-%m-%d").strftime("%d/%m")
                    for d in dates
                ]
                
                bars = ax3.bar(date_labels, totals, color='#4ECDC4', edgecolor='#00796B', linewidth=1.5)
                
                # Add value labels on top of bars
                for bar in bars:
                    height = bar.get_height()
                    ax3.text(bar.get_x() + bar.get_width()/2., height,
                            f'Rp{height/1000:.0f}K',
                            ha='center', va='bottom', fontsize=9)
                
                ax3.set_xlabel('Tanggal', fontweight='bold')
                ax3.set_ylabel('Pendapatan (Rp)', fontweight='bold')
                ax3.set_title('💰 Pendapatan Harian', fontsize=12, fontweight='bold', pad=20)
                ax3.tick_params(axis='x', rotation=45)
                ax3.yaxis.set_major_formatter(
                    plt.FuncFormatter(lambda x, p: f'Rp {x/1000:.0f}K')
                )
                ax3.grid(axis='y', alpha=0.3, linestyle='--')
            
            # ===== CHART 4: Kombinasi metode + status (KANAN BAWAH) - NEW! =====
            # Buat tabel summary
            ax4.axis('off')
            
            summary_text = "📋 RINGKASAN TRANSAKSI\n"
            summary_text += "═" * 40 + "\n\n"
            
            summary_text += "💳 METODE PEMBAYARAN:\n"
            summary_text += "─" * 40 + "\n"
            for metode, count in sorted(metode_counts.items()):
                pct = (count / sum(metode_counts.values()) * 100) if metode_counts else 0
                summary_text += f"  {metode:.<25} {count:>3} ({pct:>5.1f}%)\n"
            
            summary_text += "\n✓ STATUS PEMBAYARAN:\n"
            summary_text += "─" * 40 + "\n"
            for status, count in sorted(status_counts.items()):
                pct = (count / sum(status_counts.values()) * 100) if status_counts else 0
                summary_text += f"  {status:.<25} {count:>3} ({pct:>5.1f}%)\n"
            
            summary_text += "\n" + "═" * 40 + "\n"
            summary_text += f"TOTAL TRANSAKSI: {sum(metode_counts.values())}\n"
            
            ax4.text(0.05, 0.95, summary_text, transform=ax4.transAxes,
                    fontsize=9, verticalalignment='top', fontfamily='monospace',
                    bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.3))
            
            plt.tight_layout()
            
            # Embed in Tkinter
            canvas = FigureCanvasTkAgg(fig, master=chart_window)
            canvas.draw()
            canvas.get_tk_widget().pack(fill='both', expand=True)
            
            ttk.Button(chart_window, text="Tutup", command=chart_window.destroy).pack(pady=10)
            
        except ImportError:
            # Fallback: Tampilkan grafik ASCII sederhana
            self.show_text_chart()

    def export_report_chart(self):
        """Export grafik laporan ke file gambar (PNG) - REVISI FREKUENSI TRANSAKSI"""
        try:
            import matplotlib.pyplot as plt
            from matplotlib.ticker import MaxNLocator # Untuk sumbu Y bilangan bulat
            from datetime import datetime
        except ImportError:
            messagebox.showerror("Error", "Modul 'matplotlib' belum terinstall.\nSilakan install dengan: pip install matplotlib")
            return

        # 1. Cek Data
        if not self.report_tree.get_children():
            messagebox.showwarning("Data Kosong", "Generate laporan terlebih dahulu sebelum export grafik.")
            return

        # Ambil periode saat ini ('harian', 'mingguan', atau 'bulanan')
        periode_pilihan = self.report_period_var.get()

        # 2. Kumpulkan Data dari Treeview
        metode_counts = {}
        trend_data = {} # Bisa per jam atau per hari tergantung periode
        
        for item_id in self.report_tree.get_children():
            values = self.report_tree.item(item_id)['values']
            # Format values: (tid, tanggal, meja, total_str, metode, status)
            tanggal_str = values[1] # Format: DD/MM/YYYY HH:MM
            metode = values[4]
            
            # Hitung Pie Chart (Metode)
            metode_counts[metode] = metode_counts.get(metode, 0) + 1
            
            # Hitung Bar Chart (Trend Transaksi)
            try:
                dt_obj = datetime.strptime(tanggal_str, "%d/%m/%Y %H:%M")
                
                if periode_pilihan == 'harian':
                    # Jika Harian: Group by JAM (misal "14:00")
                    key = dt_obj.strftime("%H:00")
                else:
                    # Jika Mingguan/Bulanan: Group by TANGGAL (misal "2025-12-14")
                    key = dt_obj.strftime("%Y-%m-%d")
                
                # Kita hitung JUMLAH TRANSAKSI (+1), bukan total uang
                trend_data[key] = trend_data.get(key, 0) + 1
                
            except ValueError:
                pass

        # 3. Buat Figure & Plotting
        try:
            # Setup layout: 1 Baris, 2 Kolom
            fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 7))
            
            judul_periode = periode_pilihan.upper()
            fig.suptitle(f'LAPORAN TRANSAKSI - {judul_periode}', fontsize=16, fontweight='bold')
            
            # --- PLOT 1: PIE CHART (Metode Pembayaran) ---
            if metode_counts:
                labels = list(metode_counts.keys())
                sizes = list(metode_counts.values())
                # Warna-warni pastel agar cantik
                colors = ['#ff9999','#66b3ff','#99ff99','#ffcc99', '#c2c2f0']
                ax1.pie(sizes, labels=labels, autopct='%1.0f%%', colors=colors[:len(labels)], startangle=90, shadow=True)
                ax1.set_title('Proporsi Metode Pembayaran')
            else:
                ax1.text(0.5, 0.5, "Tidak ada data", ha='center')

            # --- PLOT 2: BAR CHART (Trend Frekuensi Transaksi) ---
            if trend_data:
                keys = sorted(trend_data.keys())
                counts = [trend_data[k] for k in keys]
                
                # Label X-Axis (Formatting agar rapi)
                if periode_pilihan == 'harian':
                    # Label jam tetap apa adanya
                    x_labels = keys 
                    x_title = "Jam Transaksi"
                    chart_title = "Jumlah Transaksi per Jam (Busy Hours)"
                else:
                    # Format tanggal jadi lebih pendek (DD/MM)
                    x_labels = [datetime.strptime(k, "%Y-%m-%d").strftime("%d/%m") for k in keys]
                    x_title = "Tanggal"
                    chart_title = "Total Transaksi per Hari"

                bars = ax2.bar(x_labels, counts, color='#4ECDC4', zorder=3, edgecolor='black', linewidth=0.7)
                
                ax2.set_title(chart_title)
                ax2.set_xlabel(x_title)
                ax2.set_ylabel('Jumlah Transaksi (Struk)')
                
                # Grid garis putus-putus di belakang
                ax2.grid(axis='y', linestyle='--', alpha=0.5, zorder=0)
                
                # Pastikan Sumbu Y hanya menampilkan Angka Bulat (Integer)
                # Karena tidak mungkin ada 1.5 transaksi
                ax2.yaxis.set_major_locator(MaxNLocator(integer=True))
                
                # Tambah label angka di atas setiap batang
                for bar in bars:
                    height = bar.get_height()
                    ax2.text(bar.get_x() + bar.get_width()/2., height,
                            f'{int(height)}',
                            ha='center', va='bottom', fontsize=10, fontweight='bold')
                            
                # Putar label x jika data banyak agar tidak tabrakan
                if len(x_labels) > 5:
                    plt.setp(ax2.get_xticklabels(), rotation=45, ha="right")
                    
            else:
                ax2.text(0.5, 0.5, "Tidak ada data transaksi", ha='center')

            plt.tight_layout()

            # 4. Dialog Simpan File
            filename = filedialog.asksaveasfilename(
                defaultextension=".png",
                filetypes=[("PNG Image", "*.png"), ("JPEG Image", "*.jpg")],
                title="Simpan Grafik Statistik"
            )
            
            if filename:
                plt.savefig(filename, dpi=150, bbox_inches='tight')
                messagebox.showinfo("Sukses", f"Grafik berhasil disimpan:\n{filename}")
            
            plt.close(fig)
            
        except Exception as e:
            messagebox.showerror("Export Gagal", f"Terjadi kesalahan: {e}")



    def reload_payment_orders(self):
            """Load transaksi dengan status 'selesai' yang belum dibayar"""
            # Clear tree
            for r in self.payment_tree.get_children():
                self.payment_tree.delete(r)
            
            # Get transaksi selesai
            orders = transaksi_list(status='selesai')
            
            # Filter yang belum ada pembayaran
            for order in orders:
                tid, uid, meja, total, status, promo_code, tanggal = order
                
                # Cek apakah sudah ada pembayaran
                payment_data = pembayaran_get_by_transaksi(tid)
                if payment_data:
                    continue  # Skip jika sudah dibayar
                
                self.payment_tree.insert("", tk.END, values=(
                    tid, meja, f"Rp {total:,.0f}", status, tanggal
                ))
        
    def on_payment_select(self, event):
            """Tampilkan detail transaksi saat dipilih"""
            sel = self.payment_tree.selection()
            if not sel:
                return
            
            item = self.payment_tree.item(sel)['values']
            transaksi_id = item[0]
            
            # Get detail transaksi
            transaksi_data = transaksi_get(transaksi_id)
            if not transaksi_data:
                return
            
            tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data
            detail_items = detail_transaksi_list(transaksi_id)
            
           # Update label (HANYA JIKA ADA / MODE KASIR)
            if hasattr(self, 'selected_transaksi_label'):
                self.selected_transaksi_label.config(text=f"Transaksi #{tid} - Meja {meja}", foreground='blue')
                self.selected_total_label.config(text=f"Total: Rp {total:,.0f}")
            
            # Format detail
            detail_text = f"═══════════════════════════════════════════════\n"
            detail_text += f"TRANSAKSI #{tid}\n"
            detail_text += f"═══════════════════════════════════════════════\n\n"
            detail_text += f"Meja          : {meja}\n"
            detail_text += f"Tanggal       : {tanggal}\n"
            detail_text += f"Status        : {status.upper()}\n"
            detail_text += f"Promo         : {promo_code if promo_code else '-'}\n\n"
            
            detail_text += f"───────────────────────────────────────────────\n"
            detail_text += f"ITEM PESANAN:\n"
            detail_text += f"───────────────────────────────────────────────\n"
            
            for detail in detail_items:
                did, mid, qty, harga, subtotal_item = detail
                menu_data = menu_get(mid)
                if menu_data:
                    _, nama, kategori, _, _, _, _, _ = menu_data
                    detail_text += f"• {nama}\n"
                    detail_text += f"  {qty} x Rp {harga:,.0f} = Rp {subtotal_item:,.0f}\n\n"
            
            detail_text += f"───────────────────────────────────────────────\n"
            detail_text += f"Subtotal      : Rp {subtotal:,.0f}\n"
            detail_text += f"Diskon        : Rp {item_disc + promo_disc:,.0f}\n"
            detail_text += f"───────────────────────────────────────────────\n"
            detail_text += f"TOTAL BAYAR   : Rp {total:,.0f}\n"
            detail_text += f"═══════════════════════════════════════════════\n"
            
            self.payment_detail_text.delete('1.0', tk.END)
            self.payment_detail_text.insert('1.0', detail_text)
        
    def on_payment_method_change(self):
            """Ganti input form sesuai metode pembayaran"""
            method = self.payment_method_var.get()
            
            # Clear frame
            for w in self.payment_input_frame.winfo_children():
                w.destroy()
            
            if method == 'cash':
                self.build_cash_input()
            elif method == 'qris':
                self.build_qris_input()
            elif method == 'ewallet':
                self.build_ewallet_input()
        
    def build_cash_input(self):
            """Form input untuk pembayaran cash"""
            ttk.Label(self.payment_input_frame, text="💵 Pembayaran Cash", font=("Arial", 10, "bold")).pack(pady=6)
            
            input_frame = ttk.Frame(self.payment_input_frame)
            input_frame.pack(fill='x', pady=6)
            
            ttk.Label(input_frame, text="Jumlah Bayar:").grid(row=0, column=0, sticky='w', pady=4)
            self.cash_amount_var = tk.StringVar()
            ttk.Entry(input_frame, textvariable=self.cash_amount_var, width=20).grid(row=0, column=1, pady=4, sticky='ew')
            
            input_frame.columnconfigure(1, weight=1)
            
            # Label kembalian
            self.cash_change_label = ttk.Label(self.payment_input_frame, text="Kembalian: Rp 0", font=("Arial", 10), foreground='green')
            self.cash_change_label.pack(pady=6)
            
            # Bind event untuk hitung kembalian real-time
            self.cash_amount_var.trace('w', self.calculate_cash_change)
        
    def calculate_cash_change(self, *args):
            """Hitung kembalian cash secara real-time"""
            sel = self.payment_tree.selection()
            if not sel:
                return
            
            item = self.payment_tree.item(sel)['values']
            transaksi_id = item[0]
            
            transaksi_data = transaksi_get(transaksi_id)
            if not transaksi_data:
                return
            
            total = transaksi_data[3]
            
            try:
                cash_input = float(self.cash_amount_var.get() or 0)
                change = cash_input - total
                
                if change < 0:
                    self.cash_change_label.config(text=f"Kembalian: Kurang Rp {abs(change):,.0f}", foreground='red')
                else:
                    self.cash_change_label.config(text=f"Kembalian: Rp {change:,.0f}", foreground='green')
            except:
                self.cash_change_label.config(text="Kembalian: Rp 0", foreground='gray')

    
    def build_qris_input(self):
            """Form input untuk pembayaran QRIS (simulasi QR Code)"""
            ttk.Label(self.payment_input_frame, text="📱 Pembayaran QRIS", font=("Arial", 10, "bold")).pack(pady=6)
            
            ttk.Label(self.payment_input_frame, text="Scan QR Code di bawah untuk bayar:", font=("Arial", 9)).pack(pady=4)
            
            # Generate QR Code
            sel = self.payment_tree.selection()
            if sel:
                item = self.payment_tree.item(sel)['values']
                transaksi_id = item[0]
                
                transaksi_data = transaksi_get(transaksi_id)
                if transaksi_data:
                    total = transaksi_data[3]
                    
                    # QR Code data (simulasi)
                    qr_data = f"QRIS-CAFE-TOTORO-TRX{transaksi_id}-TOTAL{int(total)}"
                    
                    try:
                        import qrcode
                        qr = qrcode.QRCode(version=1, box_size=5, border=2)
                        qr.add_data(qr_data)
                        qr.make(fit=True)
                        
                        qr_img = qr.make_image(fill_color="black", back_color="white")
                        qr_img = qr_img.resize((200, 200), Image.Resampling.LANCZOS)
                        
                        qr_photo = ImageTk.PhotoImage(qr_img)
                        self.img_cache['qris'] = qr_photo
                        
                        qr_label = ttk.Label(self.payment_input_frame, image=qr_photo)
                        qr_label.pack(pady=10)
                        
                    except ImportError:
                        ttk.Label(self.payment_input_frame, text="[QR Code - Install qrcode library]", background='#e0e0e0', font=("Arial", 9)).pack(pady=10)
            
            ttk.Label(self.payment_input_frame, text="⚠️ Ini adalah SIMULASI QRIS", font=("Arial", 8), foreground='orange').pack(pady=6)
            ttk.Label(self.payment_input_frame, text="Klik 'PROSES PEMBAYARAN' untuk konfirmasi", font=("Arial", 8)).pack()
        
    def build_ewallet_input(self):
            """Form input untuk pembayaran E-Wallet (simulasi)"""
            ttk.Label(self.payment_input_frame, text="💳 Pembayaran E-Wallet", font=("Arial", 10, "bold")).pack(pady=6)
            
            ttk.Label(self.payment_input_frame, text="Pilih E-Wallet:", font=("Arial", 9)).pack(pady=4)
            
            self.ewallet_type_var = tk.StringVar(value='gopay')
            
            ewallet_frame = ttk.Frame(self.payment_input_frame)
            ewallet_frame.pack(pady=6)
            
            ttk.Radiobutton(ewallet_frame, text="GoPay", variable=self.ewallet_type_var, value='gopay').pack(anchor='w', pady=2)
            ttk.Radiobutton(ewallet_frame, text="OVO", variable=self.ewallet_type_var, value='ovo').pack(anchor='w', pady=2)
            ttk.Radiobutton(ewallet_frame, text="Dana", variable=self.ewallet_type_var, value='dana').pack(anchor='w', pady=2)
            ttk.Radiobutton(ewallet_frame, text="ShopeePay", variable=self.ewallet_type_var, value='shopeepay').pack(anchor='w', pady=2)
            
            ttk.Label(self.payment_input_frame, text="⚠️ Ini adalah SIMULASI E-Wallet", font=("Arial", 8), foreground='orange').pack(pady=6)
            ttk.Label(self.payment_input_frame, text="Klik 'PROSES PEMBAYARAN' untuk konfirmasi", font=("Arial", 8)).pack()
        
    def process_payment(self):
            """Proses pembayaran sesuai metode yang dipilih"""
            # Cek apakah ada transaksi terpilih
            sel = self.payment_tree.selection()
            if not sel:
                messagebox.showwarning("Pilih Transaksi", "Pilih transaksi yang akan dibayar")
                return
            
            item = self.payment_tree.item(sel)['values']
            transaksi_id = item[0]
            
            transaksi_data = transaksi_get(transaksi_id)
            if not transaksi_data:
                messagebox.showerror("Error", "Data transaksi tidak ditemukan")
                return
            
            tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data
            
            # VALIDASI 1: Status harus 'selesai'
            if status != 'selesai':
                messagebox.showerror("Status Error", 
                    f"❌ Status transaksi harus 'SELESAI'\n\n"
                    f"Status saat ini: {status.upper()}\n\n"
                    f"Silakan tunggu pesanan selesai disiapkan dulu.")
                return
            
            # VALIDASI 2: Cek belum dibayar sebelumnya
            existing_payment = pembayaran_get_by_transaksi(transaksi_id)
            if existing_payment:
                messagebox.showwarning("Sudah Dibayar", 
                    f"⚠️ Transaksi ini sudah dibayar sebelumnya!\n\n"
                    f"Metode: {existing_payment[1].upper()}\n"
                    f"Tanggal: {existing_payment[4]}")
                return


            # Get metode pembayaran
            method = self.payment_method_var.get()
            
            # Validasi & proses sesuai metode
            if method == 'cash':
                success, message = self.process_cash_payment(tid, total)
            elif method == 'qris':
                success, message = self.process_qris_payment(tid, total)
            elif method == 'ewallet':
                success, message = self.process_ewallet_payment(tid, total)
            else:
                messagebox.showerror("Error", "Metode pembayaran tidak valid")
                return
            
            if success:
                # Generate struk
                struk = self.generate_struk(tid)
                
                # Update status transaksi jadi 'dibayar'
                transaksi_update_status(tid, 'dibayar')
                
                # Tutup meja
                meja_tutup(meja)
                
                # Tampilkan struk
                self.show_struk(struk)
                
                # Reload data
                self.reload_payment_orders()
                
                # Reset selection
                self.selected_transaksi_label.config(text="Belum ada transaksi dipilih", foreground='red')
                self.selected_total_label.config(text="Total: Rp 0")
                self.payment_detail_text.delete('1.0', tk.END)
            else:
                messagebox.showerror("Pembayaran Gagal", message)
        
    def process_cash_payment(self, transaksi_id, total):
        """Proses pembayaran cash - VERSI BARU"""
        try:
            cash_input = float(self.cash_amount_var.get() or 0)
        except:
            return False, "Jumlah bayar tidak valid (harus angka)"
        
        # GUNAKAN HELPER FUNCTION untuk validasi
        is_valid, msg = pembayaran_validate_cash(cash_input, total)
        if not is_valid:
            return False, msg
        
        # Hitung kembalian
        change = pembayaran_calculate_change(cash_input, total)
        
        # Simpan pembayaran
        pembayaran_add(transaksi_id, 'cash', cash_input, 'sukses', '')
        
        return True, f"✅ Pembayaran cash berhasil. Kembalian: {format_currency(change)}"


    def process_qris_payment(self, transaksi_id, total):
            """Proses pembayaran QRIS (simulasi)"""
            # Konfirmasi
            confirm = messagebox.askyesno("Konfirmasi QRIS", 
                f"Pembayaran QRIS sebesar Rp {total:,.0f}\n\n"
                f"⚠️ SIMULASI: Anggap customer sudah scan QR Code\n\n"
                f"Lanjutkan pembayaran?")
            
            if not confirm:
                return False, "Pembayaran dibatalkan"
            
            # Simpan pembayaran
            pembayaran_add(transaksi_id, 'qris', total, 'sukses', '')
            
            return True, "Pembayaran QRIS berhasil"

    def process_ewallet_payment(self, transaksi_id, total):
            """Proses pembayaran E-Wallet (simulasi)"""
            ewallet_type = self.ewallet_type_var.get()
            ewallet_name = ewallet_type.upper()
            
            # Konfirmasi
            confirm = messagebox.askyesno("Konfirmasi E-Wallet", 
                f"Pembayaran {ewallet_name} sebesar Rp {total:,.0f}\n\n"
                f"⚠️ SIMULASI: Anggap customer sudah konfirmasi di app\n\n"
                f"Lanjutkan pembayaran?")
            
            if not confirm:
                return False, "Pembayaran dibatalkan"
            
            # Simpan pembayaran
            pembayaran_add(transaksi_id, f'ewallet-{ewallet_type}', total, 'sukses', '')
            
            return True, f"Pembayaran {ewallet_name} berhasil"
        
    def generate_struk(self, transaksi_id):
        """Generate struk dengan format professional - VERSI BARU"""
        from datetime import datetime
        
        transaksi_data = transaksi_get(transaksi_id)
        if not transaksi_data:
            return ""
        
        tid, uid, meja, total, status, promo_code, subtotal, item_disc, promo_disc, tanggal = transaksi_data
        detail_items = detail_transaksi_list(transaksi_id)
        payment_data = pembayaran_get_by_transaksi(transaksi_id)
        
        # Build struk string
        struk = "═════════════════════════════════════════\n"
        struk += "             TOTORO CAFE\n"
        struk += "    Jl. Raya Totoro No. 123, Surabaya\n"
        struk += "           Telp: 031-123456\n"
        struk += "═════════════════════════════════════════\n\n"
        struk += f"No. Struk     : STR-{tid}-{datetime.now().strftime('%Y%m%d')}\n"
        struk += f"Tanggal       : {format_datetime(tanggal)}\n"
        struk += f"Meja          : {meja}\n"
        struk += f"Kasir         : {self.session['username']}\n"
        
        if payment_data:
            metode = payment_data[1]
            struk += f"Pembayaran    : {metode.upper()}\n"
        
        struk += "─────────────────────────────────────────\n"
        struk += "ITEM PESANAN:\n"
        struk += "─────────────────────────────────────────\n"
        
        for detail in detail_items:
            did, mid, qty, harga, subtotal_item = detail
            menu_data = menu_get(mid)
            if menu_data:
                _, nama, kategori, _, _, _, _, _ = menu_data
                struk += f"{nama}\n"
                struk += f"  {qty} x {format_currency(harga)}".ljust(30)
                struk += f"{format_currency(subtotal_item)}\n"
        
        struk += "─────────────────────────────────────────\n"
        struk += f"Subtotal      : {format_currency(subtotal)}\n"
        
        if item_disc > 0:
            struk += f"Diskon Item   : {format_currency(item_disc)}\n"
        
        if promo_disc > 0:
            struk += f"Diskon Promo  : {format_currency(promo_disc)}\n"
        
        struk += "─────────────────────────────────────────\n"
        struk += f"TOTAL BAYAR   : {format_currency(total)}\n"
        
        # Info pembayaran jika cash
        if payment_data and payment_data[1] == 'cash':
            jumlah_bayar = payment_data[2]
            kembalian = jumlah_bayar - total
            struk += f"Bayar         : {format_currency(jumlah_bayar)}\n"
            struk += f"Kembalian     : {format_currency(kembalian)}\n"
        
        struk += "═════════════════════════════════════════\n"
        struk += "      TERIMA KASIH ATAS KUNJUNGAN ANDA\n"
        struk += "           SAMPAI JUMPA LAGI!\n"
        struk += "═════════════════════════════════════════\n"
        
        # SIMPAN KE FILE untuk audit trail
        self.save_struk_to_file(transaksi_id, struk)
        
        return struk


    def save_struk_to_file(self, transaksi_id, struk_content):
        """Simpan struk ke file - FUNGSI BARU"""
        from datetime import datetime
        import os
        
        # Buat folder 'struk' jika belum ada
        if not os.path.exists('struk'):
            os.makedirs('struk')
        
        # Format nama file dengan timestamp
        filename = f"struk/STR-{transaksi_id}-{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
        
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(struk_content)
            return True
        except Exception as e:
            print(f"⚠️ Warning: Gagal save struk ke file: {e}")
            return False

        
    def show_struk(self, struk):
            """Tampilkan struk dalam popup window"""
            w = tk.Toplevel(self.root)
            w.title("✅ Pembayaran Berhasil - Struk Transaksi")
            w.geometry("500x600")
            w.transient(self.root)
            w.grab_set()
            
            # Frame untuk struk
            frm = ttk.Frame(w, padding=15)
            frm.pack(fill='both', expand=True)
            
            ttk.Label(frm, text="✅ PEMBAYARAN BERHASIL!", font=("Arial", 14, "bold"), foreground='green').pack(pady=10)
            
            # Text widget untuk struk
            struk_text = tk.Text(frm, width=50, height=28, font=("Courier New", 9), wrap='word')
            struk_scroll = ttk.Scrollbar(frm, orient='vertical', command=struk_text.yview)
            struk_text.configure(yscrollcommand=struk_scroll.set)
            
            struk_text.insert('1.0', struk)
            struk_text.config(state='disabled')  # Read-only
            
            struk_text.pack(side='left', fill='both', expand=True)
            struk_scroll.pack(side='right', fill='y')
            
            # Tombol
            btn_frame = ttk.Frame(w)
            btn_frame.pack(pady=10)
            
            ttk.Button(btn_frame, text="🖨️ Print (Simulasi)", command=lambda: self.print_struk_simulation(struk)).pack(side='left', padx=6)
            ttk.Button(btn_frame, text="✅ Tutup", command=w.destroy).pack(side='left', padx=6)
    
    def print_struk_simulation(self, struk):
            """Simulasi print struk (save to file)"""
            from datetime import datetime
            
            filename = f"struk_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
            
            try:
                with open(filename, 'w', encoding='utf-8') as f:
                    f.write(struk)
                
                messagebox.showinfo("Print Simulasi", f"Struk berhasil disimpan ke file:\n{filename}")
            except Exception as e:
                messagebox.showerror("Error", f"Gagal menyimpan struk: {e}")

    def check_new_orders(self):
        """Cek pesanan baru yang perlu perhatian waiter/kasir"""
        if self.session['role'] not in ['waiter', 'kasir', 'admin']:
            return 0
        
        # Hitung pesanan pending untuk waiter
        if self.session['role'] in ['waiter', 'admin']:
            pending_orders = transaksi_list(status='pending')
            return len(pending_orders)
        
        # Hitung pesanan selesai untuk kasir
        if self.session['role'] in ['kasir', 'admin']:
            selesai_orders = transaksi_list(status='selesai')
            # Filter yang belum dibayar
            unpaid_count = 0
            for order in selesai_orders:
                tid = order[0]
                payment = pembayaran_get_by_transaksi(tid)
                if not payment:
                    unpaid_count += 1
            return unpaid_count
        
        return 0


    def start_notification_check(self):
        """Start auto-refresh untuk notifikasi (setiap 10 detik)"""
        if not hasattr(self, 'notification_running'):
            self.notification_running = True
            self.update_notification_badge()


    def update_notification_badge(self):
        """Update badge notifikasi"""
        if not self.notification_running:
            return
        
        try:
            count = self.check_new_orders()
            
            # Update badge di tab yang sesuai
            if self.session['role'] in ['waiter', 'admin']:
                if hasattr(self, 'tab_waiter'):
                    for tab_id in range(self.root.nametowidget('.!notebook').index('end')):
                        tab_text = self.root.nametowidget('.!notebook').tab(tab_id, 'text')
                        if 'Kelola Pesanan' in tab_text:
                            if count > 0:
                                self.root.nametowidget('.!notebook').tab(tab_id, text=f"🍽️ Kelola Pesanan ({count})")
                            else:
                                self.root.nametowidget('.!notebook').tab(tab_id, text="🍽️ Kelola Pesanan")
                            break
            
            if self.session['role'] in ['kasir', 'admin']:
                if hasattr(self, 'tab_payment'):
                    for tab_id in range(self.root.nametowidget('.!notebook').index('end')):
                        tab_text = self.root.nametowidget('.!notebook').tab(tab_id, 'text')
                        if 'Transaksi' in tab_text:
                            if count > 0:
                                self.root.nametowidget('.!notebook').tab(tab_id, text=f"💰 Transaksi ({count})")
                            else:
                                self.root.nametowidget('.!notebook').tab(tab_id, text="💰 Transaksi")
                            break
        except:
            pass
        
        # Schedule next check (10 seconds)
        if self.notification_running:
            self.root.after(10000, self.update_notification_badge)


    def show_text_chart(self):
        """Tampilkan grafik ASCII sebagai fallback jika matplotlib tidak ada"""
        # Get data from tree
        if not self.report_tree.get_children():
            messagebox.showwarning("Tidak Ada Data", "Generate laporan terlebih dahulu")
            return
        
        # Collect data
        metode_counts = {}
        status_counts = {}  # ← TAMBAH INI (NEW)
        
        for item_id in self.report_tree.get_children():
            values = self.report_tree.item(item_id)['values']
            tid, tanggal, meja, total_str, metode, status = values
            
            # Count by metode
            metode_counts[metode] = metode_counts.get(metode, 0) + 1
            
            # NEW: Count by status
            status_key = status.upper()
            status_counts[status_key] = status_counts.get(status_key, 0) + 1
        
        # ===== BUAT ASCII CHART METODE =====
        chart_text = "=" * 70 + "\n"
        chart_text += "📊 GRAFIK PENJUALAN - TEXT MODE (Matplotlib tidak terinstall)\n"
        chart_text += "=" * 70 + "\n\n"
        
        chart_text += "💳 GRAFIK TRANSAKSI PER METODE PEMBAYARAN\n"
        chart_text += "─" * 70 + "\n"
        
        if metode_counts:
            max_count = max(metode_counts.values())
            
            for metode, count in sorted(metode_counts.items()):
                # Calculate bar length (max 50 chars)
                bar_length = int((count / max_count) * 50) if max_count > 0 else 0
                bar = "█" * bar_length
                
                percentage = (count / sum(metode_counts.values())) * 100 if sum(metode_counts.values()) > 0 else 0
                
                # Format: METODE | ███████ COUNT (PCT%)
                chart_text += f"{metode.ljust(15)} | {bar:<50} {count:>3} ({percentage:>5.1f}%)\n"
        
        chart_text += "\n" + "=" * 70 + "\n\n"
        
        # ===== BUAT ASCII CHART STATUS (NEW!) =====
        chart_text += "✓ GRAFIK STATUS PEMBAYARAN\n"
        chart_text += "─" * 70 + "\n"
        
        if status_counts:
            max_count_status = max(status_counts.values())
            
            for status, count in sorted(status_counts.items()):
                # Calculate bar length (max 50 chars)
                bar_length = int((count / max_count_status) * 50) if max_count_status > 0 else 0
                
                # Icon per status
                icon_map = {
                    'SUKSES': '✓',
                    'GAGAL': '✗',
                    'PENDING': '⏳',
                    'DIBAYAR': '✔'
                }
                icon = icon_map.get(status, '•')
                
                # Warna ASCII (menggunakan symbols)
                if status == 'SUKSES':
                    bar = "✓" * bar_length
                elif status == 'GAGAL':
                    bar = "✗" * bar_length
                elif status == 'PENDING':
                    bar = "⏳" * bar_length
                else:
                    bar = "█" * bar_length
                
                percentage = (count / sum(status_counts.values())) * 100 if sum(status_counts.values()) > 0 else 0
                
                # Format
                chart_text += f"{icon} {status.ljust(12)} | {bar:<50} {count:>3} ({percentage:>5.1f}%)\n"
        
        chart_text += "\n" + "=" * 70 + "\n"
        chart_text += f"📌 TOTAL TRANSAKSI: {sum(metode_counts.values())}\n"
        chart_text += f"✓ SUKSES: {status_counts.get('SUKSES', 0)}\n"
        chart_text += f"✗ GAGAL: {status_counts.get('GAGAL', 0)}\n"
        chart_text += f"⏳ PENDING: {status_counts.get('PENDING', 0)}\n"
        chart_text += f"✔ DIBAYAR: {status_counts.get('DIBAYAR', 0)}\n"
        chart_text += "=" * 70
        
        # Show in window
        w = tk.Toplevel(self.root)
        w.title("📊 Grafik Laporan (Text Mode)")
        w.geometry("800x650")
        
        frm = ttk.Frame(w, padding=15)
        frm.pack(fill='both', expand=True)
        
        ttk.Label(frm, text="ℹ️ Matplotlib tidak terinstall - Mode Text Chart", 
                 font=("Arial", 10), foreground='orange').pack(pady=10)
        
        text = tk.Text(frm, width=100, height=32, font=("Courier New", 9))
        text_scroll = ttk.Scrollbar(frm, orient='vertical', command=text.yview)
        text.configure(yscrollcommand=text_scroll.set)
        
        text.insert('1.0', chart_text)
        text.config(state='disabled')
        
        text.pack(side='left', fill='both', expand=True)
        text_scroll.pack(side='right', fill='y')
        
        # Tombol
        btn_frame = ttk.Frame(w)
        btn_frame.pack(pady=10)
        ttk.Button(btn_frame, text="💾 Save ke File", command=lambda: self.save_text_chart(chart_text)).pack(side='left', padx=5)
        ttk.Button(btn_frame, text="Tutup", command=w.destroy).pack(side='left', padx=5)
    
    
    def save_text_chart(self, chart_text):
        """Helper untuk save text chart ke file"""
        from datetime import datetime
        
        filename = f"GRAFIK_TEXT_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(chart_text)
            messagebox.showinfo("✅ Berhasil", f"Grafik tersimpan:\n{filename}")
        except Exception as e:
            messagebox.showerror("Error", f"Gagal save: {e}")




# MEJA

    def build_meja_tab(self, parent):
        """Tab untuk kelola status meja - VERSI BARU"""
        for w in parent.winfo_children():
            w.destroy()
        
        # Header
        header = ttk.Frame(parent)
        header.pack(fill='x', padx=10, pady=8)
        ttk.Label(header, text="🪑 Manajemen Meja Cafe", font=("Arial", 14, "bold")).pack(side='left')
        ttk.Button(header, text="🔄 Refresh", command=self.reload_meja_status).pack(side='right', padx=6)
        
        # Info panel - DENGAN OCCUPANCY
        info_frame = ttk.LabelFrame(parent, text="📊 Info Meja", padding=10)
        info_frame.pack(fill='x', padx=10, pady=6)
        
        info_inner = ttk.Frame(info_frame)
        info_inner.pack()
        
        ttk.Label(info_inner, text="🟢 Kosong:", font=("Arial", 10)).grid(row=0, column=0, padx=15, pady=3)
        self.meja_kosong_label = ttk.Label(info_inner, text="0", font=("Arial", 10, "bold"), foreground='green')
        self.meja_kosong_label.grid(row=0, column=1, padx=5, pady=3)
        
        ttk.Label(info_inner, text="🔴 Terisi:", font=("Arial", 10)).grid(row=0, column=2, padx=15, pady=3)
        self.meja_terisi_label = ttk.Label(info_inner, text="0", font=("Arial", 10, "bold"), foreground='red')
        self.meja_terisi_label.grid(row=0, column=3, padx=5, pady=3)
        
        ttk.Label(info_inner, text="📈 Okupansi:", font=("Arial", 10)).grid(row=0, column=4, padx=15, pady=3)
        self.meja_occupancy_label = ttk.Label(info_inner, text="0%", font=("Arial", 10, "bold"))
        self.meja_occupancy_label.grid(row=0, column=5, padx=5, pady=3)
        
        # Canvas untuk meja cards
        canvas_frame = ttk.Frame(parent)
        canvas_frame.pack(fill='both', expand=True, padx=10, pady=6)
        
        canvas = tk.Canvas(canvas_frame, bg='#f5f5f5', highlightthickness=0)
        scrollbar = ttk.Scrollbar(canvas_frame, orient="vertical", command=canvas.yview)
        
        self.meja_cards_frame = ttk.Frame(canvas)
        
        self.meja_cards_frame.bind(
            "<Configure>",
            lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
        )
        
        canvas.create_window((0, 0), window=self.meja_cards_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)
        
        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")
        
        def _on_mousewheel(event):
            canvas.yview_scroll(int(-1*(event.delta/120)), "units")
        canvas.bind_all("<MouseWheel>", _on_mousewheel)
        
        self.reload_meja_status()
    
    
    def reload_meja_status(self):
        """Load semua meja dalam bentuk cards - VERSI BARU"""
        # Clear cards lama
        for widget in self.meja_cards_frame.winfo_children():
            widget.destroy()
        
        # GUNAKAN HELPER FUNCTION - Ini yang baru!
        summary = meja_get_status_summary()
        
        # Update info labels dari summary
        self.meja_kosong_label.config(text=str(summary['kosong']))
        self.meja_terisi_label.config(text=str(summary['terisi']))
        
        # Hitung & display occupancy percentage
        occupancy = (summary['terisi'] / summary['total'] * 100) if summary['total'] > 0 else 0
        self.meja_occupancy_label.config(text=f"{occupancy:.0f}%")
        
        # Render meja cards (5 per row)
        row = 0
        col = 0
        
        for meja in summary['meja_list']:
            nomor = meja['nomor']
            status = meja['status']
            transaksi_info = meja['transaksi_info']
            
            # Tentukan warna berdasarkan status
            if status == 'kosong':
                bg_color = '#C8E6C9'
                status_color = '#4CAF50'
                status_text = '🟢 KOSONG'
            elif transaksi_info:
                trx_status = transaksi_info['status']
                if trx_status == 'selesai':
                    bg_color = '#FFCDD2'
                    status_color = '#F44336'
                    status_text = '🔴 SELESAI'
                elif trx_status == 'dibayar':
                    bg_color = '#BBDEFB'
                    status_color = '#1976D2'
                    status_text = '✔️ DIBAYAR'
                else:
                    bg_color = '#FFF9C4'
                    status_color = '#FBC02D'
                    status_text = '🟡 TERISI'
            else:
                bg_color = '#E0E0E0'
                status_color = '#757575'
                status_text = '⚪ UNKNOWN'
            
            # BUAT CARD
            card = tk.Frame(
                self.meja_cards_frame,
                relief='solid',
                borderwidth=2,
                bg=bg_color,
                padx=15,
                pady=15
            )
            card.grid(row=row, column=col, padx=8, pady=8, sticky='nsew')
            
            # Nomor meja (besar)
            tk.Label(
                card,
                text=f"MEJA {nomor}",
                font=("Arial", 16, "bold"),
                bg=bg_color
            ).pack(pady=(0, 5))
            
            # Status
            tk.Label(
                card,
                text=status_text,
                font=("Arial", 10, "bold"),
                fg=status_color,
                bg=bg_color
            ).pack(pady=5)
            
            # Detail transaksi jika ada
            if transaksi_info:
                tk.Label(
                    card,
                    text=f"Transaksi: #{transaksi_info['id']}",
                    font=("Arial", 8),
                    bg=bg_color
                ).pack(pady=2)
                
                tk.Label(
                    card,
                    text=f"Total: {format_currency(transaksi_info['total'])}",
                    font=("Arial", 8, "bold"),
                    bg=bg_color,
                    fg='#1976D2'
                ).pack(pady=2)
                
                tk.Label(
                    card,
                    text=f"Status: {transaksi_info['status'].upper()}",
                    font=("Arial", 7),
                    bg=bg_color
                ).pack(pady=2)
            
            # Tombol action
            btn_frame = tk.Frame(card, bg=bg_color)
            btn_frame.pack(pady=(10, 0))

            if status_text == '⚪ UNKNOWN':
                    tk.Button(
                        btn_frame,
                        text="⚠️ Reset Paksa",
                        font=("Arial", 8, "bold"),
                        bg='red',
                        fg='white',
                        width=12,
                        command=lambda n=nomor: self.tutup_meja(n)
                    ).pack()
            
            if status == 'terisi' and transaksi_info:
                if transaksi_info['status'] == 'dibayar':
                    tk.Button(
                        btn_frame,
                        text="✅ Tutup Meja",
                        font=("Arial", 9, "bold"),
                        bg='#4CAF50',
                        fg='white',
                        width=12,
                        borderwidth=0,
                        cursor='hand2',
                        command=lambda n=nomor: self.tutup_meja(n)
                    ).pack()
                else:
                    tk.Label(
                        btn_frame,
                        text="⏳ Menunggu Pembayaran",
                        font=("Arial", 8),
                        bg=bg_color,
                        fg='orange'
                    ).pack()
            else:
                tk.Label(
                    btn_frame,
                    text="Siap digunakan",
                    font=("Arial", 8),
                    bg=bg_color,
                    fg='gray'
                ).pack()
            
            # Next column
            col += 1
            if col >= 5:  # 5 meja per row
                col = 0
                row += 1
    
    
    def tutup_meja(self, nomor_meja):
        """Tutup meja dengan validasi - VERSI BARU"""
        # Validasi nomor meja
        is_valid, msg = validate_meja_number(nomor_meja)
        if not is_valid:
            messagebox.showerror("Invalid Input", msg)
            return
        
        # Cek status transaksi
        meja_data = meja_get(nomor_meja)
        if meja_data and meja_data[2]:  # ada transaksi_id
            try:
                transaksi_data = transaksi_get(int(meja_data[2]))
                if transaksi_data and transaksi_data[4] != 'dibayar':
                    messagebox.showwarning(
                        "Belum Dibayar", 
                        f"Transaksi belum dibayar!\n"
                        f"Status saat ini: {transaksi_data[4].upper()}\n\n"
                        f"Silakan proses pembayaran dulu."
                    )
                    return
            except:
                pass
        
        # Konfirmasi
        if messagebox.askyesno(
            "Konfirmasi",
            f"Tutup meja {nomor_meja}?\n\nMeja akan direset dan siap digunakan lagi."
        ):
            success = meja_tutup(nomor_meja)
            if success:
                messagebox.showinfo(
                    "✅ Berhasil",
                    f"Meja {nomor_meja} berhasil direset dan siap digunakan"
                )
                self.reload_meja_status()
            else:
                messagebox.showerror("❌ Gagal", f"Gagal menutup meja {nomor_meja}")




    # ========================================
    # FUNGSI TAMBAHAN UNTUK BACKEND
    # (sudah ada tapi ditambahkan untuk kelengkapan)
    # ========================================

    def meja_list_all():
        """Ambil semua data meja"""
        rows = read_all(MEJA_CSV)
        out = []
        
        for r in rows:
            try:
                nomor = int(r.get("nomor_meja") or 0)
            except:
                nomor = r.get("nomor_meja")
            
            transaksi_id = r.get("transaksi_id") or ""
            out.append((nomor, r.get("status"), transaksi_id))
        
        out.sort(key=lambda x: int(x[0]) if isinstance(x[0], int) else 0)
        return out







# Done
if __name__ == "__main__":
    init_db_csv()
    root = tk.Tk()
    app = App(root)
    root.mainloop()