E - BATIK

E-BATIK

<!DOCTYPE html>
<html lang="id" class="h-full">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>suryo_batikkreasi - Studio Edukasi Membatik Digital Siswa</title>
    <!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- FontAwesome Icons -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <!-- Google Fonts Inter & Playfair -->
    <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Playfair+Display:wght@700&display=swap" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    fontFamily: {
                        sans: ['"Plus Jakarta Sans"', 'sans-serif'],
                        serif: ['"Playfair Display"', 'serif'],
                    },
                    colors: {
                        batikGold: '#C59B27',
                        batikBrown: '#5C3A21',
                        batikRed: '#8B263E',
                    }
                }
            }
        }
    </script>
    <style>
        .pattern-bg {
            background-color: #fdfbf7;
            background-image: radial-gradient(#c59b27 0.75px, transparent 0.75px), radial-gradient(#c59b27 0.75px, #fdfbf7 0.75px);
            background-size: 30px 30px;
            background-position: 0 0, 15px 15px;
        }
        canvas {
            touch-action: none;
        }
    </style>
</head>
<body class="h-full pattern-bg text-slate-800 flex flex-col font-sans selection:bg-amber-200">

    <header class="bg-white/90 backdrop-blur-md border-b border-amber-100 sticky top-0 z-50 px-4 lg:px-8 py-3 shadow-sm">
        <div class="max-w-7xl mx-auto flex items-center justify-between">
            <div class="flex items-center space-x-3">
                <div class="w-10 h-10 rounded-xl bg-gradient-to-tr from-batikBrown to-batikGold flex items-center justify-center text-white shadow-md">
                    <i class="fa-solid fa-paintbrush text-lg"></i>
                </div>
                <div>
                    <h1 class="font-serif text-xl font-bold text-batikBrown tracking-wide">suryo_batikkreasi</h1>
                    <p class="text-xs text-slate-500 font-medium">Platform Edukasi & Galeri Batik Digital Siswa</p>
                </div>
            </div>
           
            <div class="flex items-center space-x-2 sm:space-x-3">
                <button onclick="app.switchView('editor')" id="nav-editor" class="px-3.5 py-2 rounded-xl text-sm font-semibold transition bg-amber-100 text-batikBrown shadow-inner">
                    <i class="fa-solid fa-palette mr-1.5"></i> Studio
                </button>
                <button onclick="app.switchView('mockup')" id="nav-mockup" class="px-3.5 py-2 rounded-xl text-sm font-semibold transition text-slate-600 hover:bg-amber-50">
                    <i class="fa-solid fa-shirt mr-1.5"></i> Model Baju
                </button>
                <button onclick="app.switchView('gallery')" id="nav-gallery" class="px-3.5 py-2 rounded-xl text-sm font-semibold transition text-slate-600 hover:bg-amber-50">
                    <i class="fa-solid fa-images mr-1.5"></i> Galeri
                </button>
            </div>
        </div>
    </header>

    <main class="flex-1 max-w-7xl w-full mx-auto p-4 sm:p-6 overflow-y-auto">
       
        <!-- VIEW 1: STUDIO MEWARNAI -->
        <div id="view-editor" class="grid grid-cols-1 lg:grid-cols-12 gap-6 h-full">
           
            <!-- Sidebar Panel Kiri -->
            <div class="lg:col-span-4 flex flex-col space-y-4">
               
                <!-- Step 1: Pilih Pola Motif -->
                <div class="bg-white rounded-2xl p-5 shadow-sm border border-amber-100">
                    <h2 class="text-xs font-bold tracking-wider text-batikGold uppercase mb-3 flex items-center">
                        <span class="w-5 h-5 rounded-full bg-amber-100 text-batikBrown inline-flex items-center justify-center mr-2 text-xs">1</span>
                        Pilih Motif Batik & Edukasi
                    </h2>
                    <div class="grid grid-cols-2 gap-2.5" id="pattern-selector"></div>
                    <div id="pattern-info-card" class="mt-3 p-3 bg-amber-50/60 rounded-xl border border-amber-200/60 text-xs text-slate-600"></div>
                </div>

                <!-- Step 2: Alat & Warna -->
                <div class="bg-white rounded-2xl p-5 shadow-sm border border-amber-100 flex-1 flex flex-col justify-between space-y-4">
                    <div>
                        <h2 class="text-xs font-bold tracking-wider text-batikGold uppercase mb-3 flex items-center">
                            <span class="w-5 h-5 rounded-full bg-amber-100 text-batikBrown inline-flex items-center justify-center mr-2 text-xs">2</span>
                            Pilih Alat & Warna Kreatif
                        </h2>
                       
                        <!-- Tool Switcher -->
                        <div class="grid grid-cols-2 gap-2 mb-4">
                            <button onclick="app.setTool('brush')" id="tool-brush" class="py-2.5 px-3 rounded-xl border border-amber-300 bg-amber-50 text-batikBrown text-sm font-semibold flex items-center justify-center space-x-2 transition shadow-sm">
                                <i class="fa-solid fa-paintbrush"></i>
                                <span>Kuas Bebas</span>
                            </button>
                            <button onclick="app.setTool('fill')" id="tool-fill" class="py-2.5 px-3 rounded-xl border border-slate-200 bg-white text-slate-600 text-sm font-semibold flex items-center justify-center space-x-2 transition hover:bg-slate-50">
                                <i class="fa-solid fa-fill-drip"></i>
                                <span>Isi Ruang</span>
                            </button>
                        </div>

                        <!-- Brush Size Slider -->
                        <div id="brush-size-container" class="mb-4 bg-slate-50 p-3 rounded-xl border border-slate-100">
                            <div class="flex justify-between text-xs font-medium text-slate-600 mb-1">
                                <span>Ukuran Kuas</span>
                                <span id="brush-size-val">15px</span>
                            </div>
                            <input type="range" min="5" max="40" value="15" oninput="app.setBrushSize(this.value)" class="w-full accent-batikBrown cursor-pointer">
                        </div>

                        <!-- Palette Warna -->
                        <label class="block text-xs font-semibold text-slate-600 mb-2">Palette Warna Nusantara</label>
                        <div class="grid grid-cols-6 gap-2 mb-4" id="color-palette"></div>

                        <!-- Input Nama dan Kelas Siswa -->
                        <div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-2">
                            <div>
                                <label class="block text-xs font-semibold text-slate-600 mb-1">Nama Siswa</label>
                                <input type="text" id="student-name" placeholder="Nama lengkap..." class="w-full px-3 py-2 text-sm rounded-xl border border-slate-200 focus:outline-none focus:border-batikBrown bg-slate-50">
                            </div>
                            <div>
                                <label class="block text-xs font-semibold text-slate-600 mb-1">Kelas</label>
                                <input type="text" id="student-class" placeholder="Contoh: VII A" class="w-full px-3 py-2 text-sm rounded-xl border border-slate-200 focus:outline-none focus:border-batikBrown bg-slate-50">
                            </div>
                        </div>
                    </div>

                    <!-- Action Buttons -->
                    <div class="pt-3 border-t border-slate-100 flex flex-col gap-2">
                        <div class="flex gap-2">
                            <button onclick="app.clearCanvas()" class="flex-1 py-2.5 px-2 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded-xl font-semibold text-xs transition flex items-center justify-center space-x-1">
                                <i class="fa-solid fa-rotate-right"></i>
                                <span>Reset</span>
                            </button>
                            <button onclick="app.previewOnShirt()" class="flex-1 py-2.5 px-2 bg-amber-600 hover:bg-amber-700 text-white rounded-xl font-semibold text-xs transition shadow-sm flex items-center justify-center space-x-1">
                                <i class="fa-solid fa-shirt"></i>
                                <span>Lihat Baju</span>
                            </button>
                        </div>
                        <button onclick="app.saveArtwork()" class="w-full py-2.5 px-3 bg-gradient-to-r from-batikBrown to-amber-800 hover:opacity-95 text-white rounded-xl font-semibold text-xs sm:text-sm transition shadow-md flex items-center justify-center space-x-1.5">
                            <i class="fa-solid fa-cloud-arrow-up"></i>
                            <span>Simpan ke Galeri</span>
                        </button>
                    </div>
                </div>

            </div>

            <!-- Canvas Area Kanan -->
            <div class="lg:col-span-8 bg-white rounded-2xl p-5 shadow-sm border border-amber-100 flex flex-col items-center justify-center relative min-h-[480px]">
                <div class="absolute top-4 left-4 bg-amber-50 border border-amber-200 px-3.5 py-1.5 rounded-lg text-xs font-semibold text-batikBrown flex items-center space-x-2 shadow-xs z-10">
                    <i class="fa-solid fa-circle-info text-batikGold"></i>
                    <span id="active-pattern-label">Motif Aktif: Megamendung</span>
                </div>

                <div class="relative w-full max-w-[500px] aspect-square rounded-xl overflow-hidden shadow-inner border border-slate-200 bg-white flex items-center justify-center mt-8 lg:mt-0">
                    <canvas id="batikCanvas" width="600" height="600" class="w-full h-full cursor-crosshair"></canvas>
                </div>

                <div class="flex items-center justify-between w-full max-w-[500px] mt-3 px-1 text-xs text-slate-500">
                    <span><i class="fa-solid fa-lightbulb text-amber-500 mr-1"></i> Kanvas diperbesar agar mudah diwarnai.</span>
                    <span id="canvas-dimension">600 x 600 px</span>
                </div>
            </div>

        </div>

        <!-- VIEW 2: MOCKUP MODEL BUSANA DENGAN NAMA, KELAS & FITUR SHARE -->
        <div id="view-mockup" class="hidden space-y-6">
            <div class="bg-gradient-to-r from-batikBrown to-amber-900 rounded-2xl p-6 md:p-8 text-white shadow-md flex flex-col md:flex-row justify-between items-center">
                <div>
                    <h2 class="font-serif text-2xl md:text-3xl font-bold mb-2">Simulasi Model Busana Batik</h2>
                    <p class="text-amber-100 text-sm max-w-xl">Hasil karya warna motif Anda beserta identitas siswa tersemat langsung pada peraga busana profesional!</p>
                </div>
                <div class="flex items-center space-x-3 mt-4 md:mt-0">
                    <button onclick="app.openShareModal()" class="bg-emerald-600 hover:bg-emerald-700 text-white font-semibold px-4 py-3 rounded-xl shadow transition text-sm flex items-center space-x-2">
                        <i class="fa-solid fa-share-nodes"></i>
                        <span>Bagikan Karya</span>
                    </button>
                    <button onclick="app.switchView('editor')" class="bg-white text-batikBrown font-semibold px-4 py-3 rounded-xl shadow hover:bg-amber-50 transition text-sm flex items-center space-x-2">
                        <i class="fa-solid fa-paintbrush"></i>
                        <span>Kembali Mewarnai</span>
                    </button>
                </div>
            </div>

            <div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
                <!-- Pilihan Model Baju -->
                <div class="lg:col-span-4 bg-white rounded-2xl p-5 shadow-sm border border-amber-100 flex flex-col space-y-4">
                    <h3 class="text-xs font-bold tracking-wider text-batikGold uppercase">Pilih Jenis Pakaian</h3>
                    <div class="grid grid-cols-2 gap-3" id="shirt-model-selector">
                        <button onclick="app.setShirtModel('shirt')" id="model-btn-shirt" class="p-3 rounded-xl border border-batikBrown bg-amber-50 text-batikBrown font-semibold text-xs flex flex-col items-center justify-center space-y-2 transition shadow-xs">
                            <i class="fa-solid fa-shirt text-2xl"></i>
                            <span>Kemeja Pria</span>
                        </button>
                        <button onclick="app.setShirtModel('blouse')" id="model-btn-blouse" class="p-3 rounded-xl border border-slate-200 bg-white text-slate-600 font-semibold text-xs flex flex-col items-center justify-center space-y-2 transition hover:bg-slate-50">
                            <i class="fa-solid fa-person-dress text-2xl"></i>
                            <span>Dress / Blus</span>
                        </button>
                        <button onclick="app.setShirtModel('bomber')" id="model-btn-bomber" class="p-3 rounded-xl border border-slate-200 bg-white text-slate-600 font-semibold text-xs flex flex-col items-center justify-center space-y-2 transition hover:bg-slate-50">
                            <i class="fa-solid fa-vest text-2xl"></i>
                            <span>Jaket Bomber</span>
                        </button>
                        <button onclick="app.setShirtModel('blazer')" id="model-btn-blazer" class="p-3 rounded-xl border border-slate-200 bg-white text-slate-600 font-semibold text-xs flex flex-col items-center justify-center space-y-2 transition hover:bg-slate-50">
                            <i class="fa-solid fa-user-tie text-2xl"></i>
                            <span>Outer / Blazer</span>
                        </button>
                    </div>

                    <div class="p-4 bg-amber-50/60 rounded-xl border border-amber-200/60 text-xs text-slate-600 space-y-2">
                        <p class="font-bold text-batikBrown"><i class="fa-solid fa-circle-info mr-1"></i> Identitas & Atribut</p>
                        <p>Nama dan kelas yang diisi di studio akan otomatis tertera pada label sertifikat kartu peraga busana di sebelah kanan.</p>
                    </div>

                    <button onclick="app.saveArtwork()" class="w-full py-3 bg-batikBrown hover:bg-amber-900 text-white rounded-xl font-semibold text-sm transition shadow-md flex items-center justify-center space-x-2">
                        <i class="fa-solid fa-cloud-arrow-up"></i>
                        <span>Simpan Hasil Busana ke Galeri</span>
                    </button>
                </div>

                <!-- Preview Area Baju dengan Nama, Kelas & Watermark SMK Kebonjeruk -->
                <div class="lg:col-span-8 bg-white rounded-2xl p-6 shadow-sm border border-amber-100 flex flex-col items-center justify-center relative min-h-[500px]">
                    <div class="absolute top-4 left-4 bg-amber-50 border border-amber-200 px-3.5 py-1.5 rounded-lg text-xs font-semibold text-batikBrown flex items-center space-x-2 shadow-xs">
                        <i class="fa-solid fa-wand-magic-sparkles text-batikGold"></i>
                        <span id="active-model-label">Mockup: Kemeja Pria Formal Batik</span>
                    </div>

                    <!-- Card Wrapper dengan Nama, Kelas & Watermark SMK Kebonjeruk di bagian akhir -->
                    <div class="w-full max-w-[420px] bg-slate-50/80 border border-amber-200/70 rounded-2xl p-4 shadow-inner flex flex-col items-center mt-8 lg:mt-0">
                        <div id="shirt-svg-wrapper" class="w-full h-[320px] flex items-center justify-center transition-all duration-300 mb-3"></div>
                       
                        <!-- Badge Identitas Siswa pada Mockup -->
                        <div class="w-full bg-white border border-amber-200 rounded-xl p-3 shadow-xs flex items-center justify-between mb-2.5">
                            <div class="flex items-center space-x-2.5">
                                <div class="w-8 h-8 rounded-lg bg-amber-100 text-batikBrown flex items-center justify-center font-bold text-xs">
                                    <i class="fa-solid fa-id-badge"></i>
                                </div>
                                <div>
                                    <p id="preview-student-name" class="text-xs font-bold text-slate-800">Siswa Kreatif</p>
                                    <p class="text-[11px] text-slate-500">Kelas: <span id="preview-student-class" class="font-semibold text-batikBrown">VII</span></p>
                                </div>
                            </div>
                            <span id="preview-pattern-badge" class="text-[10px] font-bold uppercase tracking-wider text-amber-800 bg-amber-50 px-2.5 py-1 rounded-full border border-amber-200">Megamendung</span>
                        </div>

                        <!-- Watermark SMK Kebonjeruk di Akhir Preview -->
                        <div class="w-full pt-2 border-t border-dashed border-amber-200 flex items-center justify-center space-x-2 text-slate-500 text-[11px] font-semibold tracking-wide">
                            <i class="fa-solid fa-school text-batikBrown"></i>
                            <span>Karya Resmi Digital &bull; SMK Kebonjeruk</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- VIEW 3: GALERI KARYA SISWA -->
        <div id="view-gallery" class="hidden space-y-6">
            <div class="bg-gradient-to-r from-batikBrown to-amber-900 rounded-2xl p-6 md:p-8 text-white shadow-md flex flex-col md:flex-row justify-between items-center">
                <div>
                    <h2 class="font-serif text-2xl md:text-3xl font-bold mb-2">Galeri Pameran Batik Siswa</h2>
                    <p class="text-amber-100 text-sm max-w-xl">Koleksi mahakarya digital beserta informasi lengkap nama dan kelas siswa.</p>
                </div>
                <button onclick="app.switchView('editor')" class="mt-4 md:mt-0 bg-white text-batikBrown font-semibold px-5 py-3 rounded-xl shadow hover:bg-amber-50 transition text-sm flex items-center space-x-2">
                    <i class="fa-solid fa-plus"></i>
                    <span>Buat Batik Baru</span>
                </button>
            </div>

            <div class="flex flex-wrap items-center gap-2" id="gallery-filters">
                <button onclick="app.filterGallery('all')" class="px-4 py-2 rounded-xl text-xs font-semibold bg-batikBrown text-white shadow-sm transition">Semua Karya</button>
                <button onclick="app.filterGallery('Megamendung')" class="px-4 py-2 rounded-xl text-xs font-semibold bg-white text-slate-600 hover:bg-amber-50 border border-slate-200 transition">Megamendung</button>
                <button onclick="app.filterGallery('Parang')" class="px-4 py-2 rounded-xl text-xs font-semibold bg-white text-slate-600 hover:bg-amber-50 border border-slate-200 transition">Parang Rusak</button>
                <button onclick="app.filterGallery('Kawung')" class="px-4 py-2 rounded-xl text-xs font-semibold bg-white text-slate-600 hover:bg-amber-50 border border-slate-200 transition">Kawung</button>
                <button onclick="app.filterGallery('Truntum')" class="px-4 py-2 rounded-xl text-xs font-semibold bg-white text-slate-600 hover:bg-amber-50 border border-slate-200 transition">Truntum</button>
            </div>

            <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6" id="gallery-grid"></div>
        </div>

    </main>

    <!-- MODAL SHARE KARYA -->
    <div id="share-modal" class="fixed inset-0 z-50 bg-slate-900/60 backdrop-blur-xs flex items-center justify-center p-4 hidden">
        <div class="bg-white rounded-2xl max-w-md w-full p-6 shadow-xl border border-amber-100 space-y-4 transform transition-all">
            <div class="flex items-center justify-between border-b border-slate-100 pb-3">
                <div class="flex items-center space-x-2">
                    <div class="w-8 h-8 rounded-lg bg-amber-100 text-batikBrown flex items-center justify-center font-bold">
                        <i class="fa-solid fa-share-nodes text-sm"></i>
                    </div>
                    <h3 class="font-serif text-lg font-bold text-batikBrown">Bagikan Hasil Karya</h3>
                </div>
                <button onclick="app.closeShareModal()" class="w-8 h-8 rounded-full bg-slate-100 hover:bg-slate-200 text-slate-500 flex items-center justify-center transition">
                    <i class="fa-solid fa-xmark"></i>
                </button>
            </div>

            <p class="text-xs text-slate-600">Pilih cara untuk membagikan karya busana batik Anda kepada teman atau guru:</p>

            <div class="grid grid-cols-2 gap-3">
                <button onclick="app.copyShareLink()" class="p-3 rounded-xl border border-amber-200 bg-amber-50 hover:bg-amber-100/70 text-batikBrown font-semibold text-xs flex flex-col items-center justify-center space-y-1.5 transition">
                    <i class="fa-solid fa-link text-lg"></i>
                    <span>Salin Tautan</span>
                </button>
                <button onclick="app.downloadShareCard()" class="p-3 rounded-xl border border-amber-200 bg-amber-50 hover:bg-amber-100/70 text-batikBrown font-semibold text-xs flex flex-col items-center justify-center space-y-1.5 transition">
                    <i class="fa-solid fa-download text-lg"></i>
                    <span>Unduh Gambar</span>
                </button>
            </div>

            <div class="p-3 bg-slate-50 rounded-xl border border-slate-200 text-[11px] text-slate-500 flex items-center space-x-2">
                <i class="fa-solid fa-circle-info text-amber-600"></i>
                <span id="share-link-text" class="truncate">https://suryo-batikkreasi.edu/pameran/karya-siswa</span>
            </div>

            <div class="pt-2 flex justify-end">
                <button onclick="app.closeShareModal()" class="px-4 py-2 bg-slate-200 hover:bg-slate-300 text-slate-700 rounded-xl text-xs font-semibold transition">Tutup</button>
            </div>
        </div>
    </div>

    <div id="toast" class="fixed bottom-5 right-5 z-50 transform translate-y-20 opacity-0 transition-all duration-300 bg-slate-900 text-white px-5 py-3 rounded-xl shadow-xl flex items-center space-x-3 text-sm font-medium">
        <i class="fa-solid fa-circle-check text-emerald-400 text-lg"></i>
        <span id="toast-message">Berhasil menyimpan karya!</span>
    </div>

    <script>
        class BatikApp {
            constructor() {
                this.currentView = 'editor';
                this.currentTool = 'brush';
                this.brushSize = 15;
                this.currentColor = '#8B263E';
                this.selectedPatternId = 'megamendung';
                this.activeGalleryFilter = 'all';
                this.currentShirtModel = 'shirt';
               
                this.canvas = document.getElementById('batikCanvas');
                this.ctx = this.canvas.getContext('2d', { willReadFrequently: true });
                this.isDrawing = false;
                this.lastX = 0;
                this.lastY = 0;

                this.colors = [
                    '#8B263E', '#C59B27', '#5C3A21', '#1B3B36', '#1E3A8A', '#7C2D12',
                    '#B45309', '#047857', '#4338CA', '#BE185D', '#374151', '#F59E0B'
                ];

                this.patterns = {
                    megamendung: {
                        name: 'Megamendung (Cirebon)',
                        desc: 'Motif awan bertumpuk besar asal Cirebon, melambangkan keteduhan dan kebijaksanaan.',
                        draw: (ctx, w, h) => this.drawMegaMendung(ctx, w, h)
                    },
                    parang: {
                        name: 'Parang Rusak (Yogyakarta)',
                        desc: 'Motif garis diagonal besar melambangkan semangat juang tak pernah padam.',
                        draw: (ctx, w, h) => this.drawParang(ctx, w, h)
                    },
                    kawung: {
                        name: 'Kawung (Solo)',
                        desc: 'Motif bulatan besar berbentuk kolang-kaling melambangkan kemurnian hati.',
                        draw: (ctx, w, h) => this.drawKawung(ctx, w, h)
                    },
                    truntum: {
                        name: 'Truntum (Surakarta)',
                        desc: 'Motif taburan bintang besar melambangkan cinta kasih yang tulus.',
                        draw: (ctx, w, h) => this.drawTruntum(ctx, w, h)
                    }
                };

                this.shirtModels = {
                    shirt: { name: 'Kemeja Pria Formal Batik', icon: 'fa-shirt' },
                    blouse: { name: 'Dress / Blus Wanita Batik', icon: 'fa-person-dress' },
                    bomber: { name: 'Jaket Bomber Batik Modern', icon: 'fa-vest' },
                    blazer: { name: 'Outer / Blazer Batik Elegan', icon: 'fa-user-tie' }
                };

                this.gallery = [
                    { id: 1, title: 'Megamendung Senja', author: 'Siti Aisyah', studentClass: 'VII A', pattern: 'Megamendung', date: '03 Okt 2026', image: 'https://placehold.co/400x400/8B263E/FDFBF7?text=Megamendung+Senja' },
                    { id: 2, title: 'Parang Keraton', author: 'Budi Santoso', studentClass: 'VIII B', pattern: 'Parang', date: '02 Okt 2026', image: 'https://placehold.co/400x400/C59B27/5C3A21?text=Parang+Keraton' }
                ];

                this.init();
            }

            init() {
                this.renderPatternSelector();
                this.renderPalette();
                this.loadPattern(this.selectedPatternId);
                this.renderGallery();
                this.setupCanvasEvents();
            }

            switchView(viewName) {
                this.currentView = viewName;
                const editorView = document.getElementById('view-editor');
                const mockupView = document.getElementById('view-mockup');
                const galleryView = document.getElementById('view-gallery');
               
                const navEditor = document.getElementById('nav-editor');
                const navMockup = document.getElementById('nav-mockup');
                const navGallery = document.getElementById('nav-gallery');

                editorView.classList.add('hidden');
                mockupView.classList.add('hidden');
                galleryView.classList.add('hidden');

                navEditor.className = "px-3.5 py-2 rounded-xl text-sm font-semibold transition text-slate-600 hover:bg-amber-50";
                navMockup.className = "px-3.5 py-2 rounded-xl text-sm font-semibold transition text-slate-600 hover:bg-amber-50";
                navGallery.className = "px-3.5 py-2 rounded-xl text-sm font-semibold transition text-slate-600 hover:bg-amber-50";

                if (viewName === 'editor') {
                    editorView.classList.remove('hidden');
                    navEditor.className = "px-3.5 py-2 rounded-xl text-sm font-semibold transition bg-amber-100 text-batikBrown shadow-inner";
                } else if (viewName === 'mockup') {
                    mockupView.classList.remove('hidden');
                    navMockup.className = "px-3.5 py-2 rounded-xl text-sm font-semibold transition bg-amber-100 text-batikBrown shadow-inner";
                    this.updatePreviewStudentInfo();
                    this.renderShirtPreview();
                } else {
                    galleryView.classList.remove('hidden');
                    navGallery.className = "px-3.5 py-2 rounded-xl text-sm font-semibold transition bg-amber-100 text-batikBrown shadow-inner";
                    this.renderGallery();
                }
            }

            updatePreviewStudentInfo() {
                const nameInput = document.getElementById('student-name');
                const classInput = document.getElementById('student-class');
                const studentName = nameInput.value.trim() || 'Siswa Kreatif';
                const studentClass = classInput.value.trim() || 'VII';
                const shortPattern = this.patterns[this.selectedPatternId].name.split(' ')[0];

                document.getElementById('preview-student-name').innerText = studentName;
                document.getElementById('preview-student-class').innerText = studentClass;
                document.getElementById('preview-pattern-badge').innerText = shortPattern;
            }

            renderPatternSelector() {
                const container = document.getElementById('pattern-selector');
                container.innerHTML = Object.keys(this.patterns).map(key => {
                    const pat = this.patterns[key];
                    const isSelected = this.selectedPatternId === key;
                    const shortName = pat.name.split(' ')[0];
                    return `
                        <button onclick="app.loadPattern('${key}')" class="p-2.5 rounded-xl border text-center transition flex flex-col items-center justify-center space-y-1 ${isSelected ? 'border-batikBrown bg-amber-50/80 shadow-xs ring-1 ring-batikBrown' : 'border-slate-200 bg-white hover:bg-slate-50'}">
                            <i class="fa-solid fa-shapes text-batikBrown text-base"></i>
                            <span class="text-xs font-semibold text-slate-700 truncate w-full">${shortName}</span>
                        </button>
                    `;
                }).join('');
            }

            loadPattern(key) {
                this.selectedPatternId = key;
                const pat = this.patterns[key];
                document.getElementById('active-pattern-label').innerText = `Motif Aktif: ${pat.name}`;
                document.getElementById('pattern-info-card').innerHTML = `<p class="font-semibold text-batikBrown mb-1"><i class="fa-solid fa-book-open mr-1"></i> ${pat.name}</p><p>${pat.desc}</p>`;
               
                this.renderPatternSelector();
                this.redrawCanvas();
            }

            redrawCanvas() {
                const w = this.canvas.width;
                const h = this.canvas.height;
                this.ctx.fillStyle = '#FFFFFF';
                this.ctx.fillRect(0, 0, w, h);

                this.patterns[this.selectedPatternId].draw(this.ctx, w, h);
            }

            renderPalette() {
                const paletteContainer = document.getElementById('color-palette');
                paletteContainer.innerHTML = this.colors.map(col => `
                    <button onclick="app.setColor('${col}')" class="w-full aspect-square rounded-xl transition transform hover:scale-105 shadow-xs border-2 ${this.currentColor === col ? 'border-slate-900 scale-105 ring-2 ring-amber-300' : 'border-white'}" style="background-color: ${col};"></button>
                `).join('');
            }

            setColor(col) {
                this.currentColor = col;
                this.renderPalette();
            }

            setTool(tool) {
                this.currentTool = tool;
                const btnBrush = document.getElementById('tool-brush');
                const btnFill = document.getElementById('tool-fill');
                const sizeContainer = document.getElementById('brush-size-container');

                if (tool === 'brush') {
                    btnBrush.className = "py-2.5 px-3 rounded-xl border border-amber-300 bg-amber-50 text-batikBrown text-sm font-semibold flex items-center justify-center space-x-2 transition shadow-sm";
                    btnFill.className = "py-2.5 px-3 rounded-xl border border-slate-200 bg-white text-slate-600 text-sm font-semibold flex items-center justify-center space-x-2 transition hover:bg-slate-50";
                    sizeContainer.style.display = 'block';
                } else {
                    btnFill.className = "py-2.5 px-3 rounded-xl border border-amber-300 bg-amber-50 text-batikBrown text-sm font-semibold flex items-center justify-center space-x-2 transition shadow-sm";
                    btnBrush.className = "py-2.5 px-3 rounded-xl border border-slate-200 bg-white text-slate-600 text-sm font-semibold flex items-center justify-center space-x-2 transition hover:bg-slate-50";
                    sizeContainer.style.display = 'none';
                }
            }

            setBrushSize(val) {
                this.brushSize = parseInt(val);
                document.getElementById('brush-size-val').innerText = `${val}px`;
            }

            setupCanvasEvents() {
                const getPos = (e) => {
                    const rect = this.canvas.getBoundingClientRect();
                    const clientX = e.touches ? e.touches[0].clientX : e.clientX;
                    const clientY = e.touches ? e.touches[0].clientY : e.clientY;
                    return {
                        x: (clientX - rect.left) * (this.canvas.width / rect.width),
                        y: (clientY - rect.top) * (this.canvas.height / rect.height)
                    };
                };

                const startAction = (e) => {
                    e.preventDefault();
                    const pos = getPos(e);
                    if (this.currentTool === 'fill') {
                        this.floodFill(Math.floor(pos.x), Math.floor(pos.y), this.hexToRgba(this.currentColor));
                    } else {
                        this.isDrawing = true;
                        this.lastX = pos.x;
                        this.lastY = pos.y;
                    }
                };

                const moveAction = (e) => {
                    if (!this.isDrawing || this.currentTool !== 'brush') return;
                    e.preventDefault();
                    const pos = getPos(e);
                   
                    this.ctx.beginPath();
                    this.ctx.moveTo(this.lastX, this.lastY);
                    this.ctx.lineTo(pos.x, pos.y);
                    this.ctx.strokeStyle = this.currentColor;
                    this.ctx.lineWidth = this.brushSize;
                    this.ctx.lineCap = 'round';
                    this.ctx.lineJoin = 'round';
                    this.ctx.stroke();

                    this.lastX = pos.x;
                    this.lastY = pos.y;
                };

                const endAction = () => {
                    this.isDrawing = false;
                };

                this.canvas.addEventListener('mousedown', startAction);
                this.canvas.addEventListener('mousemove', moveAction);
                window.addEventListener('mouseup', endAction);

                this.canvas.addEventListener('touchstart', startAction, { passive: false });
                this.canvas.addEventListener('touchmove', moveAction, { passive: false });
                window.addEventListener('touchend', endAction);
            }

            hexToRgba(hex) {
                let c = hex.replace('#', '');
                if (c.length === 3) c = c.split('').map(x => x + x).join('');
                const num = parseInt(c, 16);
                return [num >> 16, (num >> 8) & 255, num & 255, 255];
            }

            floodFill(startX, startY, fillColor) {
                const w = this.canvas.width;
                const h = this.canvas.height;
                const imgData = this.ctx.getImageData(0, 0, w, h);
                const data = imgData.data;

                const getIdx = (x, y) => (y * w + x) * 4;
                const startIdx = getIdx(startX, startY);
                const startR = data[startIdx];
                const startG = data[startIdx + 1];
                const startB = data[startIdx + 2];
                const startA = data[startIdx + 3];

                if (startR === fillColor[0] && startG === fillColor[1] && startB === fillColor[2] && startA === fillColor[3]) return;
                if (startR < 50 && startG < 50 && startB < 50) return;

                const matchStartColor = (idx) => {
                    return data[idx] === startR && data[idx + 1] === startG && data[idx + 2] === startB && data[idx + 3] === startA;
                };

                const colorPixel = (idx) => {
                    data[idx] = fillColor[0];
                    data[idx + 1] = fillColor[1];
                    data[idx + 2] = fillColor[2];
                    data[idx + 3] = fillColor[3];
                };

                const queue = [[startX, startY]];
                const visited = new Uint8Array(w * h);

                while (queue.length > 0) {
                    const [x, y] = queue.pop();
                    if (x < 0 || x >= w || y < 0 || y >= h) continue;
                    const idx = y * w + x;
                    if (visited[idx]) continue;
                    visited[idx] = 1;

                    const pIdx = getIdx(x, y);
                    if (data[pIdx] < 50 && data[pIdx + 1] < 50 && data[pIdx + 2] < 50) continue;
                    if (!matchStartColor(pIdx)) continue;

                    colorPixel(pIdx);

                    queue.push([x + 1, y]);
                    queue.push([x - 1, y]);
                    queue.push([x, y + 1]);
                    queue.push([x, y - 1]);
                }

                this.ctx.putImageData(imgData, 0, 0);
            }

            clearCanvas() {
                this.redrawCanvas();
                this.showToast('Kanvas telah diatur ulang.');
            }

            previewOnShirt() {
                this.switchView('mockup');
            }

            setShirtModel(modelKey) {
                this.currentShirtModel = modelKey;
                const keys = ['shirt', 'blouse', 'bomber', 'blazer'];
                keys.forEach(k => {
                    const btn = document.getElementById(`model-btn-${k}`);
                    if (k === modelKey) {
                        btn.className = "p-3 rounded-xl border border-batikBrown bg-amber-50 text-batikBrown font-semibold text-xs flex flex-col items-center justify-center space-y-2 transition shadow-xs";
                    } else {
                        btn.className = "p-3 rounded-xl border border-slate-200 bg-white text-slate-600 font-semibold text-xs flex flex-col items-center justify-center space-y-2 transition hover:bg-slate-50";
                    }
                });

                document.getElementById('active-model-label').innerText = `Mockup: ${this.shirtModels[modelKey].name}`;
                this.renderShirtPreview();
            }

            renderShirtPreview() {
                const wrapper = document.getElementById('shirt-svg-wrapper');
                const dataURL = this.canvas.toDataURL('image/png');

                let svgContent = '';
                if (this.currentShirtModel === 'shirt') {
                    svgContent = `
                        <svg viewBox="0 0 300 350" class="w-full h-full drop-shadow-md">
                            <defs>
                                <pattern id="batikTex" width="80" height="80" patternUnits="userSpaceOnUse">
                                    <image href="${dataURL}" width="80" height="80" />
                                </pattern>
                            </defs>
                            <path d="M90 60 L60 80 L30 140 L70 160 L80 320 L220 320 L230 160 L270 140 L240 80 L210 60 C180 80 120 80 90 60 Z" fill="url(#batikTex)" stroke="#5C3A21" stroke-width="3"/>
                            <polygon points="90,60 150,110 210,60 170,40 150,50 130,40" fill="#fdfbf7" stroke="#5C3A21" stroke-width="2"/>
                            <line x1="150" y1="110" x2="150" y2="320" stroke="#5C3A21" stroke-width="3" stroke-dasharray="6,6"/>
                            <circle cx="150" cy="140" r="3" fill="#C59B27"/>
                            <circle cx="150" cy="180" r="3" fill="#C59B27"/>
                            <circle cx="150" cy="220" r="3" fill="#C59B27"/>
                        </svg>
                    `;
                } else if (this.currentShirtModel === 'blouse') {
                    svgContent = `
                        <svg viewBox="0 0 300 350" class="w-full h-full drop-shadow-md">
                            <defs>
                                <pattern id="batikTex" width="80" height="80" patternUnits="userSpaceOnUse">
                                    <image href="${dataURL}" width="80" height="80" />
                                </pattern>
                            </defs>
                            <path d="M100 50 L50 90 L70 140 L90 330 L210 330 L230 140 L250 90 L200 50 C175 70 125 70 100 50 Z" fill="url(#batikTex)" stroke="#5C3A21" stroke-width="3"/>
                            <path d="M100 50 Q150 90 200 50 Q150 70 100 50 Z" fill="#fdfbf7" stroke="#5C3A21" stroke-width="2"/>
                        </svg>
                    `;
                } else if (this.currentShirtModel === 'bomber') {
                    svgContent = `
                        <svg viewBox="0 0 300 350" class="w-full h-full drop-shadow-md">
                            <defs>
                                <pattern id="batikTex" width="80" height="80" patternUnits="userSpaceOnUse">
                                    <image href="${dataURL}" width="80" height="80" />
                                </pattern>
                            </defs>
                            <path d="M85 70 L40 100 L30 180 L80 320 L220 320 L270 180 L260 100 L215 70 C185 85 115 85 85 70 Z" fill="url(#batikTex)" stroke="#1B3B36" stroke-width="3"/>
                            <rect x="80" y="300" width="140" height="20" rx="5" fill="#1B3B36"/>
                            <line x1="150" y1="70" x2="150" y2="320" stroke="#1B3B36" stroke-width="4"/>
                        </svg>
                    `;
                } else {
                    svgContent = `
                        <svg viewBox="0 0 300 350" class="w-full h-full drop-shadow-md">
                            <defs>
                                <pattern id="batikTex" width="80" height="80" patternUnits="userSpaceOnUse">
                                    <image href="${dataURL}" width="80" height="80" />
                                </pattern>
                            </defs>
                            <path d="M90 60 L50 90 L40 320 L130 320 L130 130 L170 130 L170 320 L260 320 L250 90 L210 60 C180 80 120 80 90 60 Z" fill="url(#batikTex)" stroke="#5C3A21" stroke-width="3"/>
                            <polygon points="90,60 130,130 130,60" fill="#fdfbf7" stroke="#5C3A21" stroke-width="2"/>
                            <polygon points="210,60 170,130 170,60" fill="#fdfbf7" stroke="#5C3A21" stroke-width="2"/>
                        </svg>
                    `;
                }

                wrapper.innerHTML = svgContent;
            }

            openShareModal() {
                const modal = document.getElementById('share-modal');
                modal.classList.remove('hidden');
            }

            closeShareModal() {
                const modal = document.getElementById('share-modal');
                modal.classList.add('hidden');
            }

            copyShareLink() {
                const dummyText = `https://suryo-batikkreasi.edu/pameran/karya-${Date.now()}`;
                const tempInput = document.createElement('input');
                tempInput.value = dummyText;
                document.body.appendChild(tempInput);
                tempInput.select();
                document.execCommand('copy');
                document.body.removeChild(tempInput);

                this.showToast('Tautan karya berhasil disalin ke clipboard!');
                this.closeShareModal();
            }

            downloadShareCard() {
                const studentName = document.getElementById('student-name').value.trim() || 'Siswa Kreatif';
                this.showToast(`Mengunduh kartu pameran busana untuk ${studentName}...`);
                this.closeShareModal();
            }

            saveArtwork() {
                const studentNameInput = document.getElementById('student-name');
                const studentClassInput = document.getElementById('student-class');
                const studentName = studentNameInput.value.trim() || 'Siswa Kreatif';
                const studentClass = studentClassInput.value.trim() || 'VII';
                const dataURL = this.canvas.toDataURL('image/png');
                const patternNameFull = this.patterns[this.selectedPatternId].name;
                const shortPattern = patternNameFull.split(' ')[0];

                const newEntry = {
                    id: Date.now(),
                    title: `${shortPattern} (${this.shirtModels[this.currentShirtModel].name.split(' ')[0]}) - ${studentName}`,
                    author: studentName,
                    studentClass: studentClass,
                    pattern: shortPattern,
                    date: 'Hari ini',
                    image: dataURL
                };

                this.gallery.unshift(newEntry);
                this.showToast('Karya & Mockup Busana berhasil disimpan!');
                this.switchView('gallery');
            }

            filterGallery(patternName) {
                this.activeGalleryFilter = patternName;
                const buttons = document.querySelectorAll('#gallery-filters button');
                buttons.forEach(btn => {
                    if (btn.innerText.toLowerCase().includes(patternName.toLowerCase()) || (patternName === 'all' && btn.innerText.includes('Semua'))) {
                        btn.className = "px-4 py-2 rounded-xl text-xs font-semibold bg-batikBrown text-white shadow-sm transition";
                    } else {
                        btn.className = "px-4 py-2 rounded-xl text-xs font-semibold bg-white text-slate-600 hover:bg-amber-50 border border-slate-200 transition";
                    }
                });
                this.renderGallery();
            }

            renderGallery() {
                const grid = document.getElementById('gallery-grid');
                const filtered = this.activeGalleryFilter === 'all'
                    ? this.gallery
                    : this.gallery.filter(item => item.pattern.toLowerCase().includes(this.activeGalleryFilter.toLowerCase()));

                if (filtered.length === 0) {
                    grid.innerHTML = `
                        <div class="col-span-full py-12 text-center text-slate-400">
                            <i class="fa-solid fa-folder-open text-4xl mb-2"></i>
                            <p class="text-sm">Belum ada karya untuk kategori ini.</p>
                        </div>
                    `;
                    return;
                }

                grid.innerHTML = filtered.map(item => `
                    <div class="bg-white rounded-2xl overflow-hidden shadow-sm border border-amber-100 flex flex-col group hover:shadow-md transition">
                        <div class="aspect-square bg-amber-50/50 overflow-hidden relative flex items-center justify-center p-3">
                            <img src="${item.image}" alt="${item.title}" class="w-full h-full object-contain rounded-xl group-hover:scale-105 transition duration-300">
                        </div>
                        <div class="p-4 flex-1 flex flex-col justify-between">
                            <div>
                                <span class="text-[10px] font-bold uppercase tracking-wider text-amber-700 bg-amber-50 px-2.5 py-1 rounded-full border border-amber-200">${item.pattern}</span>
                                <h3 class="font-bold text-slate-800 mt-2 text-base truncate">${item.title}</h3>
                                <p class="text-xs text-slate-500 mt-0.5">Karya: <span class="font-medium text-slate-700">${item.author}</span> <span class="text-slate-400">(${item.studentClass})</span></p>
                            </div>
                            <div class="mt-4 pt-3 border-t border-slate-100 flex items-center justify-between text-xs text-slate-400">
                                <span><i class="fa-regular fa-calendar mr-1"></i> ${item.date}</span>
                                <a href="${item.image}" download="batik-${item.author.toLowerCase().replace(/\s+/g, '-')}.png" class="text-batikBrown font-semibold hover:underline flex items-center space-x-1">
                                    <i class="fa-solid fa-download"></i>
                                    <span>Unduh</span>
                                </a>
                            </div>
                        </div>
                    </div>
                `).join('');
            }

            showToast(msg) {
                const toast = document.getElementById('toast');
                const toastMsg = document.getElementById('toast-message');
                toastMsg.innerText = msg;
                toast.classList.remove('translate-y-20', 'opacity-0');
                setTimeout(() => {
                    toast.classList.add('translate-y-20', 'opacity-0');
                }, 3000);
            }

            drawMegaMendung(ctx, w, h) {
                ctx.lineWidth = 6;
                ctx.strokeStyle = '#1a1a1a';
                ctx.fillStyle = '#FFFFFF';

                ctx.beginPath();
                ctx.arc(w * 0.35, h * 0.35, w * 0.22, Math.PI, 0, false);
                ctx.arc(w * 0.65, h * 0.35, w * 0.18, Math.PI, 0, false);
                ctx.closePath();
                ctx.fill();
                ctx.stroke();

                ctx.beginPath();
                ctx.arc(w * 0.65, h * 0.65, w * 0.22, Math.PI, 0, true);
                ctx.arc(w * 0.35, h * 0.65, w * 0.18, Math.PI, 0, true);
                ctx.closePath();
                ctx.fill();
                ctx.stroke();

                ctx.beginPath();
                ctx.arc(w * 0.5, h * 0.5, w * 0.12, 0, Math.PI * 2);
                ctx.fill();
                ctx.stroke();
            }

            drawParang(ctx, w, h) {
                ctx.lineWidth = 6;
                ctx.strokeStyle = '#1a1a1a';
                ctx.fillStyle = '#FFFFFF';

                for (let i = -1; i < 3; i++) {
                    ctx.beginPath();
                    ctx.moveTo(0, i * 200);
                    ctx.bezierCurveTo(w * 0.5, i * 200 + 100, w * 0.5, i * 200 + 200, w, i * 200 + 300);
                    ctx.lineTo(w, i * 200 + 400);
                    ctx.bezierCurveTo(w * 0.5, i * 200 + 300, w * 0.5, i * 200 + 200, 0, i * 200 + 100);
                    ctx.closePath();
                    ctx.fill();
                    ctx.stroke();
                }
            }

            drawKawung(ctx, w, h) {
                ctx.lineWidth = 6;
                ctx.strokeStyle = '#1a1a1a';
                ctx.fillStyle = '#FFFFFF';

                const cx = w / 2;
                const cy = h / 2;
                const r = w * 0.22;

                const offsets = [
                    {x: 0, y: -r}, {x: r, y: 0}, {x: 0, y: r}, {x: -r, y: 0}
                ];

                offsets.forEach(off => {
                    ctx.beginPath();
                    ctx.arc(cx + off.x, cy + off.y, r * 0.9, 0, Math.PI * 2);
                    ctx.fill();
                    ctx.stroke();
                });

                ctx.beginPath();
                ctx.arc(cx, cy, r * 0.45, 0, Math.PI * 2);
                ctx.fill();
                ctx.stroke();
            }

            drawTruntum(ctx, w, h) {
                ctx.lineWidth = 6;
                ctx.strokeStyle = '#1a1a1a';
                ctx.fillStyle = '#FFFFFF';

                const centers = [
                    {x: w * 0.25, y: h * 0.25},
                    {x: w * 0.75, y: h * 0.25},
                    {x: w * 0.5, y: h * 0.5},
                    {x: w * 0.25, y: h * 0.75},
                    {x: w * 0.75, y: h * 0.75}
                ];

                centers.forEach(c => {
                    ctx.beginPath();
                    ctx.arc(c.x, c.y, w * 0.16, 0, Math.PI * 2);
                    ctx.fill();
                    ctx.stroke();

                    for (let i = 0; i < 8; i++) {
                        const angle = (i * Math.PI) / 4;
                        const bx = c.x + Math.cos(angle) * (w * 0.12);
                        const by = c.y + Math.sin(angle) * (w * 0.12);
                        ctx.beginPath();
                        ctx.arc(bx, by, w * 0.04, 0, Math.PI * 2);
                        ctx.fill();
                        ctx.stroke();
                    }
                });
            }
        }

        let app;
        window.onload = function() {
            app = new BatikApp();
        };
    </script>
</body>
</html>

Post a Comment

0 Comments