// main.js (Versi Final, Lengkap, dan Bersih)

const { createApp } = Vue;

createApp({
    data() {
        return {
            selectedTable: null,
            lastScrollPosition: 0,
            transactionSuccessDetails: null, // Menyimpan detail untuk layar sukses
            paymentQrUrl: '', // Untuk kode QR pembayaran di halaman sukses
            transactionSuccessDetails: null,
            // State Aplikasi & UI
            appState: {
                isReady: false,
                loadingMessage: 'Mempersiapkan Aplikasi...',
                subView: 'main',
            },
            uiState: {
                currentView: '',
            },
            isLoading: false,

            // Konfigurasi & Sesi
            appConfig: {
                loginMode: 0,
                mejaMode: 0,
            },
            sessionState: {
                currentUser: null,
                selectedMeja: null,
            },

            // Data Utama
            products: [],
            categories: [],
            vouchers: [],
            paymentMethods: [],
            mejaList: [],
            cart: [],
            appliedVoucher: null,
            selectedPaymentMethod: null,

            // Filter & Pencarian
            filters: { q: '' },
            debounceTimeout: null,

            // State untuk Modal Detail Produk
            isDetailVisible: false,
            selectedProduct: null,
            modalSelections: {},
            modalSelectedAddons: [],
            modalCondimentChoices: {},
            modalQuantity: 1,
            editingCartId: null,

            // State untuk Modal Panel (Voucher)
            activeModal: null,

            // State untuk Login & Register
            loginForm: { username: '', password: '' },
            registerForm: { username: '', password: '', fullName: '' },
            authError: '',

            // State untuk Scroll Kategori
            activeCategoryId: null,
            isScrolling: false,
            scrollTimeout: null,

            // State untuk Direct Print (WebSocket)
            printSocket: null,
            isPrintingConnected: false,

            // State untuk Notifikasi Kustom
            alert: {
                visible: false,
                title: '',
                message: '',
                type: 'info', // 'info' atau 'confirm'
                onConfirm: null,
                onCancel: null,
            },

            // Properti Lainnya
            variantLabels: { color: 'Warna', size: 'Ukuran', type: 'Tipe', ram: 'RAM', ssd: 'SSD', storage: 'Penyimpanan', daging: 'Jenis Daging', ukuran: 'Ukuran', patty: 'Patty', suhu: 'Suhu' },
            generatedQrUrl: '',
            generatedLink: '',
            qrMejaName: '',
        };
    },
    watch: {
        cart: {
            handler(newCart) {
                localStorage.setItem('kioskCart', JSON.stringify(newCart));
            },
            deep: true,
        },
        activeCategoryId(newId) {
            if (!newId || this.isScrolling) {
                return;
            }
            this.$nextTick(() => {
                // const button = this.$refs[`categoryButton-${newId}`]?.[0];
                const button = this.$refs[`categoryButton-${newId}`] && this.$refs[`categoryButton-${newId}`][0];
                if (button) {
                    button.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
                }
            });
        },
    },
    computed: {
        groupedProducts() {
            let productsToDisplay = this.products;
            if (this.filters.q) {
                const query = this.filters.q.toLowerCase();
                productsToDisplay = this.products.filter(p => p.name.toLowerCase().includes(query));
            }
            const groups = productsToDisplay.reduce((acc, product) => {
                const categoryId = product.category_id || 'other';
                const categoryName = product.category_name || 'Lainnya';
                if (!acc[categoryId]) {
                    acc[categoryId] = { id: categoryId, name: categoryName, products: [] };
                }
                acc[categoryId].products.push(product);
                return acc;
            }, {});
            return Object.values(groups).sort((a, b) => {
                const catA = this.categories.find(c => c.id == a.id) || { name: a.name };
                const catB = this.categories.find(c => c.id == b.id) || { name: b.name };
                return catA.name.localeCompare(catB.name);
            });
        },
        bestSellerProducts() {
            return this.products.filter(p => p.is_bestseller == 1);
        },
        promoProducts() {
            return this.products.filter(p => p.promo_type !== 'none');
        },
        isModalOpen() { return this.isDetailVisible || !!this.activeModal || this.alert.visible; },
        cartItemCount() { return this.cart.reduce((sum, item) => sum + item.quantity, 0); },
        cartTotal() { return this.cart.reduce((sum, item) => sum + (item.priceBeforePromo * item.quantity), 0); },
        discountAmount() { if (!this.appliedVoucher || this.cartTotal < this.appliedVoucher.min_purchase) { if (this.appliedVoucher) { this.appliedVoucher = null; } return 0; } if (this.appliedVoucher.type === 'fixed') { return parseFloat(this.appliedVoucher.value); } if (this.appliedVoucher.type === 'percentage') { const discount = this.cartTotal * (parseFloat(this.appliedVoucher.value) / 100); return (this.appliedVoucher.max_discount && discount > parseFloat(this.appliedVoucher.max_discount)) ? parseFloat(this.appliedVoucher.max_discount) : discount; } return 0; },
        finalTotal() { const totalAfterItemPromo = this.cart.reduce((sum, item) => sum + (item.finalPrice * item.quantity), 0); const total = totalAfterItemPromo - this.discountAmount; return total < 0 ? 0 : total; },
        variantAttributes() { if (!this.selectedProduct || !this.selectedProduct.variants) return []; const keys = new Set(); this.selectedProduct.variants.forEach(variant => { if (variant.attributes) Object.keys(variant.attributes).forEach(key => keys.add(key)); }); return Array.from(keys); },
        modalSelectedVariant() { if (!this.selectedProduct) return null; if (!this.selectedProduct.variants || this.selectedProduct.variants.length === 0) { return { id: null, product_id: this.selectedProduct.id, stock: this.selectedProduct.stock, attributes: {}, price: this.selectedProduct.base_price }; } const found = this.selectedProduct.variants.find(v => this.variantAttributes.every(key => v.attributes[key] === this.modalSelections[key])); return found || null; },
        // modalFinalPrice() { if (!this.selectedProduct) return 0; let total = parseFloat(this.selectedProduct.base_price); if (this.modalSelectedVariant && this.modalSelectedVariant.price !== null && this.modalSelectedVariant.price >= 0) { total = parseFloat(this.modalSelectedVariant.price); } total += (this.modalSelectedAddons || []).reduce((sum, addon) => sum + parseFloat(addon.price), 0); const condimentChoices = Object.values(this.modalCondimentChoices).flat().filter(Boolean); total += (condimentChoices || []).reduce((sum, choice) => sum + parseFloat(choice.price || 0), 0); return total; },


modalFinalPrice() {
    if (!this.selectedProduct) {
        return 0;
    }

    let total = parseFloat(this.selectedProduct.base_price);

    if (this.modalSelectedVariant && this.modalSelectedVariant.price !== null && this.modalSelectedVariant.price >= 0) {
        total = parseFloat(this.modalSelectedVariant.price);
    }

    // Penambahan harga dari addons (tidak ada perubahan, sudah kompatibel)
    total += (this.modalSelectedAddons || []).reduce((sum, addon) => sum + parseFloat(addon.price), 0);

    // --- BAGIAN YANG DIUBAH ---
    // Mengganti .flat() dengan metode yang lebih kompatibel
    const condimentValues = Object.values(this.modalCondimentChoices);
    const flattenedCondiments = condimentValues.reduce((acc, val) => acc.concat(val), []);
    const condimentChoices = flattenedCondiments.filter(Boolean); // filter(Boolean) untuk menghapus nilai null/undefined

    total += (condimentChoices || []).reduce((sum, choice) => sum + parseFloat(choice.price || 0), 0);

    return total;
},        
        isModalFormValid() { if (!this.selectedProduct || !this.modalSelectedVariant || this.modalSelectedVariant.stock <= 0) return false; return (this.selectedProduct.condiment_groups || []).every(group => { if (!group.is_required) return true; const choice = this.modalCondimentChoices[group.id]; return choice && (Array.isArray(choice) ? choice.length > 0 : Object.keys(choice).length > 0); }); },
        productIdsInCart() { return new Set(this.cart.map(item => item.id)); },
        modalTitle() { if (this.activeModal === 'voucher') return 'Pilih Voucher'; return ''; },
        groupedMeja() { return this.mejaList.reduce((acc, meja) => { (acc[meja.area] = acc[meja.area] || []).push(meja); return acc; }, {}); }
    },
    methods: {
        
// main.js

async submitOrder() {
    this.sessionState.selectedMeja='00';
    if (this.cart.length === 0) {
        this.showCustomAlert({ title: 'Keranjang Kosong', message: 'Silakan pilih produk terlebih dahulu.' });
        return;
    }
    if (!this.sessionState.selectedMeja) {
        this.showCustomAlert({ title: 'Meja Belum Dipilih', message: 'Terjadi kesalahan, data meja tidak ditemukan.' });
        return;
    }

    this.isLoading = true;

    // --- BAGIAN YANG DIPERBAIKI ---
    // Menyiapkan data dengan format yang persis sama seperti Kiosk
    const transactionData = {
        customerName: `Meja ${this.sessionState.selectedMeja.nama_meja}`,
        customerPhone: '',
        total: this.finalTotal, // Menggunakan finalTotal agar diskon ikut terhitung
        brand: '001',
        cab: '002',
        items: this.cart.map(item => {
            // Logika untuk memastikan semua data yang dibutuhkan server ada
            const productMaster = this.products.find(p => p.id === item.id);

            return {
                // 'productId' sekarang diisi dengan ID INT dari master, bukan KODE
                productId: productMaster ? productMaster.id_int : null, // Asumsi ada 'id_int' di data produk
                name: item.name,
                quantity: item.quantity,
                finalPrice: item.finalPrice, // Ini akan menjadi 'price_per_item' di server
                
                // Server mengambil 'variant_ref_id' dari sini
                variant: item.details.variant ? { id: item.details.variant.id, ...item.details.variant.attributes } : {},
                
                // Data lain yang dibutuhkan oleh 'item_details' di server
                addons: item.details.addons || [],
                condiments: item.details.condiments || [],
                note: item.note || ''
            };
        })
    };
    // ----------------------------

    try {
        const response = await fetch("http://localhost/bener/pos2/kiosk/api_server.php?action=process_transaction", {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: `cartData=${encodeURIComponent(JSON.stringify(transactionData))}`
        });
        
        const result = await response.json();

        if (result.success) {
            this.showCustomAlert({
                title: 'Pesanan Berhasil',
                message: `Nomor transaksi Anda: ${result.transaction_code}`,
                onConfirm: () => {
                    this.cart = [];
                    this.uiState.currentView = 'tableSelect';
                }
            });
        } else {
            throw new Error(result.error || 'Terjadi kesalahan saat memproses pesanan.');
        }

    } catch (error) {
        console.error('Submit Order Error:', error);
        this.showCustomAlert({ title: 'Error', message: error.message });
    } finally {
        this.isLoading = false;
    }
},
        async showProductDetail(productFromList, event) {
            this.filters.q = '';
            if (this.isModalOpen) return;
            
            // --- [BARU] Simpan posisi scroll saat ini sebelum modal terbuka ---
            this.lastScrollPosition = window.scrollY;

            this.isLoading = true;
            try {
                // ... (sisa kode di dalam fungsi ini tetap sama) ...
                this.isDetailVisible = true;
                history.pushState({ modal: 'detail' }, 'Detail Produk');
            } catch (error) {
                console.error("Gagal memuat detail produk:", error);
                this.showCustomAlert({ title: 'Error', message: 'Gagal memuat detail produk.' });
            } finally {
                this.isLoading = false;
            }
        },
                closeDetailModal() {
            this.isDetailVisible = false;
            // --- [BARU] Kembalikan ke posisi scroll sebelumnya setelah modal tertutup ---
            this.$nextTick(() => {
                // Gunakan scrollTo dengan behavior 'auto' agar tidak ada animasi aneh
                window.scrollTo({ top: this.lastScrollPosition, behavior: 'auto' });
            });
        },
handleBackButton(event) {
            if (this.alert.visible) {
                this.alert.visible = false;
                return;
            }
            if (this.isDetailVisible) {
                // --- [MODIFIKASI] Panggil fungsi closeDetailModal yang sudah dimodifikasi ---
                this.closeDetailModal();
                return;
            }
            if (this.activeModal) {
                this.activeModal = null;
                return;
            }
            if (this.appState.subView === 'cart') {
                this.goToMain();
                return; // Tambahkan return agar tidak melanjutkan ke logika di bawah
            }

            // --- [BARU] Logika untuk konfirmasi keluar aplikasi ---
            // Cek jika kita berada di halaman utama
            if (this.uiState.currentView === 'mainApp' && this.appState.subView === 'main') {
                 this.showCustomAlert({
                    title: 'Konfirmasi Keluar',
                    message: 'Apakah Anda yakin ingin keluar dari aplikasi?',
                    type: 'confirm',
                    onConfirm: () => {
                        // Biarkan browser melakukan aksi 'kembali' default, yang akan menutup aplikasi/tab
                        window.history.back();
                    },
                    onCancel: () => {
                        // Jika dibatalkan, "kembalikan" state history yang baru saja di-pop
                        // Ini secara efektif membatalkan aksi "kembali"
                        history.pushState({ view: 'main' }, '', window.location.pathname + window.location.search);
                    }
                });
            }
        },

        backToMainFromSuccess() {
            this.uiState.currentView = 'main';
            this.transactionSuccessDetails = null;
            this.paymentQrUrl = '';
            history.replaceState({ view: 'main' }, '', '/'); // Bersihkan riwayat
        },


        processPayment(savedOrderData) {
    try {
        // 1. Kosongkan keranjang dan data terkait
        this.cart = [];
        localStorage.setItem('kioskCart', JSON.stringify([])); // Perbarui penyimpanan lokal
        this.appliedVoucher = null;
        this.selectedPaymentMethod = null;

        // 2. Set tampilan ke 'transactionSuccess'
        this.uiState.currentView = 'transactionSuccess';

        // 3. Isi detail transaksi untuk ditampilkan di layar sukses
        const basePath = window.location.href.substring(0, window.location.href.lastIndexOf('/') + 1);
        
        // this.transactionSuccessDetails = {
        //     orderId: savedOrderData.transaction_code, 
        //     totalAmount: this.finalTotal, 
        //     paymentStatus: 'Menunggu Pembayaran',
        //     mejaName: this.sessionState.selectedMeja ? this.sessionState.selectedMeja.nama_meja : 'Take Away',
        //     // Gunakan basePath untuk membuat URL yang benar
        //     qrContent: `${basePath}cek_transaksi.php?kode=${savedOrderData.transaction_code}`
        // };

        this.transactionSuccessDetails = {
            orderId: savedOrderData.transaction_code, // Ambil dari response server
            totalAmount: this.finalTotal, // Gunakan computed property yang sudah ada
            paymentStatus: 'Menunggu Pembayaran', // atau status lain dari backend
            mejaName: this.sessionState.selectedMeja ? this.sessionState.selectedMeja.nama_meja : 'Take Away',
            // Konten QR bisa berupa URL untuk cek status atau data lain yang relevan
            qrContent: `${basePath}/cek_transaksi.php?kode=${savedOrderData.transaction_code}`
        };

        // 4. Hasilkan QR Code untuk ditampilkan
        this.generateQrCodeForPayment(this.transactionSuccessDetails.qrContent);

    } catch (error) {
        console.error('Error preparing success screen:', error);
        this.showAlert('Error', 'Terjadi kesalahan saat menampilkan konfirmasi pesanan.');
    } finally {
        this.isLoading = false; // Hentikan loading setelah semua selesai
    }
},
    

    // Metode untuk menghasilkan QR Code
    generateQrCodeForPayment(text) {
        if (!text) {
            this.paymentQrUrl = '';
            return;
        }
        try {
            const qr = qrcode(0, 'M');
            qr.addData(text);
            qr.make();
            this.paymentQrUrl = qr.createDataURL(4); // Skala 4
            console.log("QR Code URL:", this.paymentQrUrl); // Debugging: pastikan URL terbuat
        } catch (e) {
            console.error("Gagal membuat QR Code pembayaran:", e);
            this.paymentQrUrl = '';
        }
    },

    // Metode untuk kembali dari halaman sukses
backToMainFromSuccess() {
    this.uiState.currentView = 'mainApp'; 
    this.appState.subView = 'main'; 
    this.transactionSuccessDetails = null;
    this.paymentQrUrl = '';
    
    // PERBAIKAN: Ganti '/' dengan '' atau hapus parameter ketiga
    // Ini akan memperbarui state history tanpa mengubah URL saat ini.
    history.replaceState({ view: 'mainApp' }, '', ''); 
},

    debounceSearch() {
        clearTimeout(this.debounceTimeout);
        this.debounceTimeout = setTimeout(() => {
            // Panggil method yang benar untuk memfilter produk
            // Berdasarkan kode Anda, sepertinya Anda tidak perlu fetch ulang,
            // karena `groupedProducts` sudah reaktif terhadap `filters.q`
            // Jika Anda ingin fetch dari server setiap kali, panggil `fetchProducts(true)`
            console.log('Mencari untuk:', this.filters.q);
        }, 500); // Tunda eksekusi selama 500ms
    },

    clearSearch() {
        this.filters.q = '';
    },
        // --- Inisialisasi & Fetch Data ---
        // async initializeApp() { this.appState.loadingMessage = "Memuat data..."; await this.fetchInitialData(); await this.fetchPaymentMethods(); if (this.appConfig.loginMode > 0 && !this.sessionState.currentUser) { this.uiState.currentView = 'login'; } else if (this.appConfig.mejaMode > 0 && !this.sessionState.selectedMeja) { await this.fetchMeja(); this.uiState.currentView = 'tableSelect'; } else if (!this.uiState.currentView) { this.uiState.currentView = 'mainApp'; } if (this.uiState.currentView === 'mainApp' && this.products.length === 0) { await this.fetchProducts(); } this.appState.isReady = true; },



        




       async initializeApp() {
            const params = new URLSearchParams(window.location.search);
            this.appConfig.loginMode = parseInt(params.get('login')) || 0;
            this.appConfig.mejaMode = parseInt(params.get('meja')) || 0;
            const encryptedIdx = params.get('idx');

            if (encryptedIdx) {
                this.appState.loadingMessage = "Memvalidasi QR Code...";
                try {
                    const decrypted = this.xorEncryptDecrypt(this.hexToString(encryptedIdx), 'fma');
                    const [kode_meja, timestamp] = decrypted.split('_');
                    this.appConfig.validatedIdxData = { kode_meja, timestamp };
                    await this.fetchMeja();
                    const meja = this.mejaList.find(m => m.kode_meja === kode_meja);
                    if (meja) this.selectMeja(meja, true);
                    else throw new Error("Meja dari QR Code tidak ditemukan.");
                } catch (e) {
                    this.appState.loadingMessage = "QR Code tidak valid atau kedaluwarsa!";
                    return;
                }
            }
            
            this.appState.loadingMessage = "Memuat data...";
            await this.fetchInitialData();
            
            if (this.appConfig.loginMode > 0 && !this.sessionState.currentUser) {
                this.uiState.currentView = 'login';
            } else if (this.appConfig.mejaMode > 0 && !this.sessionState.selectedMeja) {
                await this.fetchMeja();
                this.uiState.currentView = 'tableSelect';
            } else if (!this.uiState.currentView) {
                this.uiState.currentView = 'mainApp';
            }
            
            if(this.uiState.currentView === 'mainApp') {
                await this.fetchProducts();
            }
            
            this.appState.isReady = true;
        },
        goToMainApp() {
            this.filters.q = '';
            if (this.appConfig.mejaMode > 0 && !this.sessionState.selectedMeja) {
                this.fetchMeja().then(() => { this.uiState.currentView = 'tableSelect'; });
            } else {
                this.uiState.currentView = 'mainApp';
                if(this.products.length === 0) this.fetchProducts();
            }
        },
        
        // --- LOGIN & USER ---
        async handleLogin() {
            this.isLoading = true; this.authError = '';
            try {
                const response = await fetch('crud_mobile.php', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ action: 'login', ...this.loginForm }) });
                const result = await response.json();
                if (result.success) {
                    this.sessionState.currentUser = result.user;
                    this.goToMainApp();
                } else { this.authError = result.error; }
            } catch(e) { this.authError = "Gagal terhubung ke server."; }
            finally { this.isLoading = false; }
        },
        async handleRegister() {
            this.isLoading = true; this.authError = '';
            try {
                const response = await fetch('crud_mobile.php', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ action: 'register', ...this.registerForm }) });
                const result = await response.json();
                if (result.success) {
                    alert('Pendaftaran berhasil! Silakan login.');
                    this.uiState.currentView = 'login';
                } else { this.authError = result.error; }
            } catch(e) { this.authError = "Gagal terhubung ke server."; }
            finally { this.isLoading = false; }
        },
        loginAsGuest() {
            this.sessionState.currentUser = { fullName: 'Tamu' };
            this.goToMainApp();
        },

        // --- MEJA & QR CODE ---
        xorEncryptDecrypt(input, key) { let output = ''; for (let i = 0; i < input.length; i++) { const charCode = input.charCodeAt(i) ^ key.charCodeAt(i % key.length); output += String.fromCharCode(charCode); } return output; },
        stringToHex(str) { return Array.from(str).map(c => c.charCodeAt(0).toString(16).padStart(2, '0')).join(''); },
        hexToString(hex) { let str = ''; for (let i = 0; i < hex.length; i += 2) { str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); } return str; },
        async fetchMeja() {
            try {
                const res = await fetch('crud_mobile.php?action=get_meja');
                this.mejaList = await res.json();
            } catch(e) { console.error("Gagal mengambil data meja:", e); }
        },
        selectMeja(meja, fromQr = false) {
            this.sessionState.selectedMeja = meja;
            if (!fromQr) { this.goToMainApp(); }
        },
        generateQrCode(meja) {
            const now = new Date(); const expiry = new Date(now.getTime() + 120 * 60000); const formatDate = (d) => `${String(d.getDate()).padStart(2,'0')}${String(d.getMonth()+1).padStart(2,'0')}${d.getFullYear()}`; const formatTime = (d) => `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`; const plainText = `${meja.kode_meja}_${formatDate(now)}:${formatTime(now)}:${formatTime(expiry)}`; const encrypted = this.xorEncryptDecrypt(plainText, 'fma'); const urlParam = this.stringToHex(encrypted); this.generatedLink = `${window.location.origin}${window.location.pathname}?idx=${urlParam}`; const qr = qrcode(0, 'L'); qr.addData(this.generatedLink); qr.make(); this.generatedQrUrl = qr.createDataURL(5); this.qrMejaName = meja.nama_meja;
        },

        // --- PENGAMBILAN DATA ---
        async fetchInitialData() {
            try {
                const response = await fetch(`crud_mobile.php?action=get_initial_data`);
                const data = await response.json();
                this.categories = data.categories || [];
                this.vouchers = data.vouchers || [];
            } catch (e) { console.error("Gagal memuat data awal:", e); this.categories = []; this.vouchers = []; }
        },
        async fetchProducts(isNewFilter = false) {
            if (this.isLoading) return;
            this.isLoading = true;
            if (isNewFilter) { window.scrollTo(0, 0); this.currentPage = 1; this.products = []; this.hasMoreProducts = true; }
            const params = new URLSearchParams({ page: this.currentPage, sort_by: this.filters.sortBy, ...(this.filters.q && { q: this.filters.q }), ...(this.filters.minPrice && { min_price: this.filters.minPrice }), ...(this.filters.maxPrice && { max_price: this.filters.maxPrice }), ...(this.filters.minRating && { min_rating: this.filters.minRating }), ...(this.filters.categoryId && { category_id: this.filters.categoryId }), });
            try {
                const response = await fetch(`crud_mobile.php?action=get_products_list&${params.toString()}`);
                const newProducts = await response.json();
                if (newProducts && newProducts.length > 0) { this.products.push(...newProducts); this.currentPage++; } else { this.hasMoreProducts = false; }
            } catch (e) { console.error("Gagal memuat produk:", e); this.hasMoreProducts = false; } finally { this.isLoading = false; }
        },
        setupInfiniteScroll() {
            const observer = new IntersectionObserver((entries) => {
                if (entries[0].isIntersecting && this.hasMoreProducts && !this.isLoading) { this.fetchProducts(); }
            }, { threshold: 0.1 });
            if (this.$refs.infiniteLoader) { observer.observe(this.$refs.infiniteLoader); }
        },







        async fetchInitialData() { try { const response = await fetch(`crud_mobile.php?action=get_initial_data`); const data = await response.json(); this.categories = data.categories || []; this.vouchers = data.vouchers || []; if (this.categories.length > 0 && !this.activeCategoryId) { this.activeCategoryId = this.categories[0].id; } } catch (e) { console.error("Gagal memuat data awal:", e); } },
        async fetchProducts() { if (this.isLoading) return; this.isLoading = true; try { const response = await fetch(`crud_mobile.php?action=get_products_list`); this.products = await response.json(); } catch (e) { console.error("Gagal memuat produk:", e); } finally { this.isLoading = false; } },
        async fetchPaymentMethods() { try { const response = await fetch('crud_mobile.php?action=get_payment_methods'); this.paymentMethods = await response.json(); if (this.paymentMethods.length > 0) { this.selectedPaymentMethod = this.paymentMethods[0].id; } } catch (e) { console.error("Gagal memuat metode pembayaran:", e); } },
        async fetchMeja() { try { const res = await fetch('crud_mobile.php?action=get_meja'); this.mejaList = await res.json(); } catch (e) { console.error("Gagal mengambil data meja:", e); } },

        // --- Navigasi & Scroll ---
        goToCart() { this.filters.q = '';this.appState.subView = 'cart'; history.pushState({ view: 'cart' }, 'Keranjang'); },
        // goToMain() { this.appState.subView = 'main'; try { if (window.history.state?.view !== 'main') history.back(); } catch (e) { this.appState.subView = 'main'; } },
        goToMain() {
    this.appState.subView = 'main';
    try {
        // Cek dulu apakah window.history.state ada, baru akses properti 'view'
        if ((window.history.state && window.history.state.view) !== 'main') {
            history.back();
        }
    } catch (e) {
        // Fallback jika terjadi error lain
        this.appState.subView = 'main';
    }
},
        scrollToCategory(categoryId) { this.filters.q = '';this.isScrolling = true; this.activeCategoryId = categoryId; this.scrollToElement(`#category-group-${categoryId}`); clearTimeout(this.scrollTimeout); this.scrollTimeout = setTimeout(() => { this.isScrolling = false; }, 1000); },
        scrollToElement(selector) { this.filters.q = '';const element = document.querySelector(selector); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'start' }); } },
        handleScroll() { if (this.isScrolling) return; window.requestAnimationFrame(() => { const stickyHeaderHeight = 105; let bestCandidateId = null; const categoryHeaders = document.querySelectorAll('.category-group-header'); for (const header of categoryHeaders) { const rect = header.getBoundingClientRect(); if (rect.top <= stickyHeaderHeight) { bestCandidateId = header.dataset.categoryId; } else { break; } } if (bestCandidateId && this.activeCategoryId !== bestCandidateId) { this.activeCategoryId = bestCandidateId; } }); },

        // --- Logika Promo & Harga ---
        calculatePromoPrice(product) { let finalPrice = parseFloat(product.base_price); if (product.promo_type === 'discount') { finalPrice = finalPrice * (1 - (parseFloat(product.promo_value) / 100)); } else if (product.promo_type === 'fixed_cut') { finalPrice = finalPrice - parseFloat(product.promo_value); } return finalPrice < 0 ? 0 : finalPrice; },

        // --- Modal & Interaksi Produk ---
        async showProductDetail(productFromList, event) { this.filters.q = '';if (this.isModalOpen) return; this.isLoading = true; try { const response = await fetch(`crud_mobile.php?action=get_product_details&id=${productFromList.id}`); const fullProductData = await response.json(); if (fullProductData.error) throw new Error(fullProductData.error); this.selectedProduct = { ...productFromList, ...fullProductData }; this.editingCartId = null; this.initializeModalState(); this.isDetailVisible = true; history.pushState({ modal: 'detail' }, 'Detail Produk'); } catch (error) { console.error("Gagal memuat detail produk:", error); this.showCustomAlert({ title: 'Error', message: 'Gagal memuat detail produk.' }); } finally { this.isLoading = false; } },
        initializeModalState() { if (!this.selectedProduct) return; this.modalSelectedAddons = []; this.modalSelections = {}; this.modalQuantity = 1; if (this.selectedProduct.variants && this.selectedProduct.variants.length > 0) { const firstAvailableVariant = this.selectedProduct.variants.find(v => v.stock > 0) || this.selectedProduct.variants[0]; if (firstAvailableVariant && firstAvailableVariant.attributes) { this.modalSelections = { ...firstAvailableVariant.attributes }; } } this.modalCondimentChoices = {}; (this.selectedProduct.condiment_groups || []).forEach(group => { if (group.type === 'radio') { this.modalCondimentChoices[group.id] = group.is_required && group.options.length > 0 ? group.options[0] : null; } else { this.modalCondimentChoices[group.id] = []; } }); },
        closeDetailModal() { this.isDetailVisible = false; },
        selectModalOption(key, option) { this.modalSelections[key] = option; },
        getAvailableOptions(key) { if (!this.selectedProduct || !this.selectedProduct.variants) return []; const options = this.selectedProduct.variants.map(v => v.attributes[key]); return [...new Set(options)]; },

        // --- Logika Keranjang (Cart) ---
        addProductToCart(event) {
            if (!this.isModalFormValid) return;
            const variant = this.modalSelectedVariant;
            const addons = this.modalSelectedAddons;
            const condiments = Object.values(this.modalCondimentChoices)  .reduce((acc, val) => acc.concat(val), [])    .filter(Boolean);
            const priceBeforePromo = this.modalFinalPrice;
            const finalItemPrice = this.calculatePromoPrice({ base_price: priceBeforePromo, promo_type: this.selectedProduct.promo_type, promo_value: this.selectedProduct.promo_value });

            if (this.editingCartId) { this.cart = this.cart.filter(i => i.cartId !== this.editingCartId); }
            const cartId = `${this.selectedProduct.id}-${variant.id}-${addons.map(a => a.id).sort().join('')}-${condiments.map(c => c.id).sort().join('')}`;
            const existingItem = this.cart.find(item => item.cartId === cartId);
            if (existingItem) {
                existingItem.quantity += this.modalQuantity;
            } else {
                this.cart.push({
                    cartId: this.editingCartId || cartId, id: this.selectedProduct.id, name: this.selectedProduct.name, image_url: variant.image_url || this.selectedProduct.image_url,
                    quantity: this.modalQuantity, priceBeforePromo: priceBeforePromo, finalPrice: finalItemPrice,
                    details: { variant, addons, condiments }
                });
            }
            if (event) this.flyToCart(event);
            try { history.back(); } catch (e) { this.isDetailVisible = false; }
        },
        async editCartItem(itemToEdit) { this.isLoading = true; try { const response = await fetch(`crud_mobile.php?action=get_product_details&id=${itemToEdit.id}`); const fullProductData = await response.json(); this.selectedProduct = { ...itemToEdit, ...fullProductData }; this.editingCartId = itemToEdit.cartId; this.modalQuantity = itemToEdit.quantity; const details = itemToEdit.details; if (details) { this.modalSelections = details.variant ? { ...details.variant.attributes } : {}; this.modalSelectedAddons = details.addons ? [...details.addons] : []; this.modalCondimentChoices = {}; (this.selectedProduct.condiment_groups || []).forEach(group => { const choicesInCart = (details.condiments || []).filter(c => c.group_id === group.id); if (group.type === 'radio') { this.modalCondimentChoices[group.id] = choicesInCart.length > 0 ? choicesInCart[0] : null; } else { this.modalCondimentChoices[group.id] = choicesInCart; } }); } else { this.initializeModalState(); } this.isDetailVisible = true; history.pushState({ modal: 'detail' }, 'Detail Produk'); } catch (e) { console.error("Gagal memuat item untuk diedit:", e); } finally { this.isLoading = false; } },
        updateQuantity(item, amount) { if (item.quantity === 1 && amount === -1) { this.confirmRemoveItem(item); } else if (item.quantity > 0) { item.quantity += amount; } },
        confirmRemoveItem(item) { this.showCustomAlert({ title: 'Konfirmasi Hapus', message: `Yakin ingin menghapus <strong>${item.name}</strong> dari keranjang?`, type: 'confirm', onConfirm: () => { this.removeItemFromCart(item); } }); },
        removeItemFromCart(itemToRemove) { const itemInCart = this.cart.find(item => item.cartId === itemToRemove.cartId); if (itemInCart) { itemInCart.isDeleting = true; setTimeout(() => { this.cart = this.cart.filter(item => item.cartId !== itemToRemove.cartId); }, 400); } },

        // --- Notifikasi Kustom ---
        showCustomAlert(config) { this.alert = { ...this.alert, visible: true, title: config.title, message: config.message, type: config.type || 'info', onConfirm: config.onConfirm, onCancel: config.onCancel }; },
        handleAlertConfirm() { if (typeof this.alert.onConfirm === 'function') { this.alert.onConfirm(); } this.alert.visible = false; },
        handleAlertCancel() { if (typeof this.alert.onCancel === 'function') { this.alert.onCancel(); } this.alert.visible = false; },

        // --- Direct Print (WebSocket) ---
        initializePrintServerConnection() { const socketUrl = "ws://localhost"; this.printSocket = new WebSocket(socketUrl); this.printSocket.onopen = () => { console.log("Koneksi ke server print berhasil."); this.isPrintingConnected = true; }; this.printSocket.onmessage = (event) => { const response = JSON.parse(event.data); if (response.success) { console.log("Perintah cetak berhasil dieksekusi oleh server."); } else { console.error("Server print melaporkan error:", response.error); this.showCustomAlert({ title: 'Gagal Mencetak', message: response.error }); } }; this.printSocket.onerror = (error) => { console.error("Koneksi ke server print gagal:", error); this.isPrintingConnected = false; }; this.printSocket.onclose = () => { console.log("Koneksi ke server print terputus."); this.isPrintingConnected = false; }; },
        bufferToHex(buffer) { return Array.from(new Uint8Array(buffer)).map(b => b.toString(16).padStart(2, '0')).join(''); },
        printReceipt(transactionData) { if (!this.isPrintingConnected) { this.showCustomAlert({ title: 'Koneksi Gagal', message: "Printer tidak terhubung. Cetak struk dibatalkan." }); return; } const { transaction_code } = transactionData; const encoder = new EscPosEncoder(); const PRINTER_CHAR_WIDTH = 42; const horizontalLine = '-'.repeat(PRINTER_CHAR_WIDTH); encoder.initialize().align('center').bold(true).line('NAMA TOKO').bold(false).line('Alamat Toko Anda').line(horizontalLine); encoder.align('left').text('Kode: ' + transaction_code).newline().text('Tgl : ' + new Date().toLocaleString('id-ID', { hour12: false })).newline().text('Kasir: ' + (this.sessionState.currentUser ? this.sessionState.currentUser.fullName : 'Tamu')).newline().line(horizontalLine); this.cart.forEach(item => { encoder.line(item.name); const details = item.details; if (details) { if (details.variant && details.variant.attributes && Object.keys(details.variant.attributes).length > 0) { const attrText = Object.values(details.variant.attributes).join(', '); encoder.line(`  - ${attrText}`); } if (details.condiments && details.condiments.length > 0) { details.condiments.forEach(condiment => { encoder.line(`  + ${condiment.name}`); }); } if (details.addons && details.addons.length > 0) { details.addons.forEach(addon => { encoder.line(`  + ${addon.name}`); }); } } const leftPart = `${item.quantity} x ${this.formatCurrency(item.finalPrice)}`; const rightPart = this.formatCurrency(item.quantity * item.finalPrice); const spacesNeeded = PRINTER_CHAR_WIDTH - leftPart.length - rightPart.length; const spaces = ' '.repeat(Math.max(0, spacesNeeded)); encoder.line(leftPart + spaces + rightPart); }); encoder.line(horizontalLine); encoder.align('right'); const subtotalLabel = "Subtotal: "; const subtotalValue = this.formatCurrency(this.cartTotal); encoder.line(subtotalLabel + ' '.repeat(PRINTER_CHAR_WIDTH - subtotalLabel.length - subtotalValue.length) + subtotalValue); const diskonLabel = "Diskon  : "; const diskonValue = `-${this.formatCurrency(this.discountAmount)}`; encoder.line(diskonLabel + ' '.repeat(PRINTER_CHAR_WIDTH - diskonLabel.length - diskonValue.length) + diskonValue); if (this.selectedPaymentMethod) { const paymentMethod = this.paymentMethods.find(p => p.id === this.selectedPaymentMethod); if (paymentMethod) { const paymentLabel = "Bayar dg: "; const paymentValue = paymentMethod.name; encoder.line(paymentLabel + ' '.repeat(PRINTER_CHAR_WIDTH - paymentLabel.length - paymentValue.length) + paymentValue); } } const totalLabel = "TOTAL   : "; const totalValue = this.formatCurrency(this.finalTotal); encoder.bold(true).line(totalLabel + ' '.repeat(PRINTER_CHAR_WIDTH - totalLabel.length - totalValue.length) + totalValue).bold(false); encoder.align('center').newline().text('Terima Kasih!').newline(); const qrData = `${window.location.origin}/cek_transaksi.php?kode=${transaction_code}`; encoder.qrcode(qrData, 1, 6, 'l'); encoder.newline().newline().newline().cut(); const printBuffer = encoder.encode(); const hexData = this.bufferToHex(printBuffer); const payload = { action: 'cetak', printData: hexData }; this.printSocket.send(JSON.stringify(payload)); },

        // --- Proses Checkout ---
async processCheckout() {
    this.isLoading = true;

    const orderPayload = {
        action: 'save_transaction',
        userId: this.sessionState.currentUser ? this.sessionState.currentUser.id : null,
        mejaId: this.sessionState.selectedMeja ? this.sessionState.selectedMeja.id : null,
        customerName: this.sessionState.currentUser ? this.sessionState.currentUser.fullName : 'Tamu',
        totalAmount: this.cartTotal,
        discountAmount: this.discountAmount,
        finalAmount: this.finalTotal,
        cart: this.cart,
        paymentMethodId: this.selectedPaymentMethod,
        cartSnapshot: JSON.stringify(this.cart)
    };

    try {
        const response = await fetch('crud_mobile.php', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(orderPayload)
        });

        const result = await response.json();

        if (result.success) {
            // Cetak struk jika perlu
            this.printReceipt(result);
            
            // PANGGIL FUNGSI UNTUK MENAMPILKAN LAYAR SUKSES
            // Teruskan data yang relevan dari hasil simpan data
            this.processPayment(result); 

        } else {
            this.showCustomAlert({
                title: 'Gagal',
                message: result.error
            });
            this.isLoading = false; // Hentikan loading jika gagal
        }
    } catch (e) {
        console.error("Checkout Error:", e);
        this.showCustomAlert({
            title: 'Error',
            message: 'Tidak dapat terhubung ke server. Periksa koneksi Anda.'
        });
        this.isLoading = false; // Hentikan loading jika ada error koneksi
    }
    // `isLoading` di-set ke false di dalam processPayment atau blok error
},
        // --- Helper Lainnya ---
        formatCurrency(value) { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(value || 0); },
        formatVariantInfo(details) { if (!details) return ''; let info = []; if (details.variant && details.variant.attributes && Object.keys(details.variant.attributes).length > 0) { info.push(Object.values(details.variant.attributes).join(', ')); } if (details.condiments && details.condiments.length > 0) { info.push(details.condiments.map(c => c.name).join(', ')); } if (details.addons && details.addons.length > 0) { info.push(details.addons.map(a => a.name).join(', ')); } return info.join(' • '); },
        applyVoucher(voucher) { this.appliedVoucher = voucher; history.back(); },
        loadCartFromLocalStorage() { const savedCart = localStorage.getItem('kioskCart'); if (savedCart) { try { this.cart = JSON.parse(savedCart); } catch (e) { localStorage.removeItem('kioskCart'); } } },
        handleBackButton(event) { if (this.alert.visible) { this.alert.visible = false; return; } if (this.isDetailVisible) { this.isDetailVisible = false; return; } if (this.activeModal) { this.activeModal = null; return; } if (this.appState.subView === 'cart') { this.goToMain(); } },
        isVariantCombinationAvailable(key, value) { if (!this.selectedProduct || !this.selectedProduct.variants) return false; const tempSelections = { ...this.modalSelections, [key]: value }; return this.selectedProduct.variants.some(variant => { const currentSelectionKeys = Object.keys(tempSelections); const isMatch = currentSelectionKeys.every(k => tempSelections[k] === undefined || variant.attributes[k] === tempSelections[k]); return isMatch && variant.stock > 0; }); },
        getPriceForOption(key, value) { if (!this.selectedProduct || !this.selectedProduct.variants) return null; const firstMatchingVariant = this.selectedProduct.variants.find(v => v.attributes[key] === value && v.stock > 0); return firstMatchingVariant ? firstMatchingVariant.price : null; },
        flyToCart(event) { const card = event.target.closest('.product-card, .product-card-small, .product-detail-modal'); if (!card) return; const img = card.querySelector('.product-image, .detail-image'); const cartIcon = document.querySelector('.header-icon-btn'); if (!img || !cartIcon) return; const imgRect = img.getBoundingClientRect(); const cartRect = cartIcon.getBoundingClientRect(); const flyingEl = document.createElement('img'); flyingEl.src = img.src; flyingEl.style.position = 'fixed'; flyingEl.style.left = imgRect.left + 'px'; flyingEl.style.top = imgRect.top + 'px'; flyingEl.style.width = imgRect.width + 'px'; flyingEl.style.height = imgRect.height + 'px'; flyingEl.style.borderRadius = '8px'; flyingEl.style.zIndex = '9999'; flyingEl.style.transition = 'all 0.6s ease-in-out'; flyingEl.style.objectFit = 'cover'; document.body.appendChild(flyingEl); flyingEl.getBoundingClientRect(); flyingEl.style.left = (cartRect.left + cartRect.width / 2) + 'px'; flyingEl.style.top = (cartRect.top + cartRect.height / 2) + 'px'; flyingEl.style.width = '0px'; flyingEl.style.height = '0px'; flyingEl.style.transform = 'rotate(360deg)'; flyingEl.style.opacity = '0.5'; flyingEl.addEventListener('transitionend', () => { flyingEl.remove(); cartIcon.classList.add('pulse'); setTimeout(() => cartIcon.classList.remove('pulse'), 400); }); },
    },
    mounted() {
        this.initializeApp();
        this.loadCartFromLocalStorage();
        this.initializePrintServerConnection();
        // Memastikan history.replaceState mengacu ke currentView yang benar saat mounted
        history.replaceState({ view: this.uiState.currentView || 'main' }, ''); // Pastikan state awal ada
        window.addEventListener('popstate', this.handleBackButton);
        window.addEventListener('scroll', this.handleScroll, { passive: true });
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('sw.js').catch(err => console.error('Pendaftaran SW gagal:', err));
        }
    },
    beforeUnmount() {
        window.removeEventListener('popstate', this.handleBackButton);
        window.removeEventListener('scroll', this.handleScroll);
    }
}).mount('#app');





