Upload pisangora 3.ipynb

#20
by zahrawidya3020 - opened
Files changed (1) hide show
  1. pisangora 3.ipynb +1 -0
pisangora 3.ipynb ADDED
@@ -0,0 +1 @@
 
 
1
+ {"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"provenance":[],"authorship_tag":"ABX9TyPZY10tOCe3naLClSjKTGyW"},"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"}},"cells":[{"cell_type":"code","source":["!pip install matplotlib-venn"],"metadata":{"id":"nuCwTfrreYR8","executionInfo":{"status":"ok","timestamp":1758697297403,"user_tz":-420,"elapsed":9582,"user":{"displayName":"Zahra Widya Putri","userId":"05276937067783338784"}},"colab":{"base_uri":"https://localhost:8080/"},"outputId":"ae558cae-c21d-412d-f220-1807413aa1a0"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stdout","text":["Requirement already satisfied: matplotlib-venn in /usr/local/lib/python3.12/dist-packages (1.1.2)\n","Requirement already satisfied: matplotlib in /usr/local/lib/python3.12/dist-packages (from matplotlib-venn) (3.10.0)\n","Requirement already satisfied: numpy in /usr/local/lib/python3.12/dist-packages (from matplotlib-venn) (2.0.2)\n","Requirement already satisfied: scipy in /usr/local/lib/python3.12/dist-packages (from matplotlib-venn) (1.16.2)\n","Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib->matplotlib-venn) (1.3.3)\n","Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.12/dist-packages (from matplotlib->matplotlib-venn) (0.12.1)\n","Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib->matplotlib-venn) (4.60.0)\n","Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib->matplotlib-venn) (1.4.9)\n","Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib->matplotlib-venn) (25.0)\n","Requirement already satisfied: pillow>=8 in /usr/local/lib/python3.12/dist-packages (from matplotlib->matplotlib-venn) (11.3.0)\n","Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib->matplotlib-venn) (3.2.4)\n","Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.12/dist-packages (from matplotlib->matplotlib-venn) (2.9.0.post0)\n","Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.7->matplotlib->matplotlib-venn) (1.17.0)\n"]}]},{"cell_type":"code","source":["import gradio as gr\n","import google.generativeai as genai\n","import json\n","import base64\n","from PIL import Image\n","import io\n","import os\n","from datetime import datetime\n","\n","# Konfigurasi Gemini AI\n","GEMINI_API_KEY = \"AIzaSyBT4qnwSUYxdgquAQjAqvQQQaeOaRzDiL0\"\n","genai.configure(api_key=GEMINI_API_KEY)\n","\n","# Data bisnis\n","BUSINESS_INFO = {\n"," \"name\": \"Pisangora\",\n"," \"whatsapp\": \"+62 821-5399-4497\",\n"," \"instagram\": \"@pisangora_pky\",\n"," \"instagram_link\": \"https://www.instagram.com/pisangora_pky?igsh=MXM0enZ0dWU4bnoyaA==\",\n"," \"location\": \"Palangka Raya (Free Delivery Area)\",\n"," \"operating_hours\": \"Setiap hari jam 09:00 - 21:00\",\n"," \"price\": \"15.000\",\n"," \"menu\": {\n"," \"Original\": \"Pisang geprek original dengan rasa pisang murni 🍌\",\n"," \"Sambal\": \"Pisang geprek dengan sambal pedas yang nikmat 🌢️\",\n"," \"Keju Parut\": \"Pisang geprek dengan taburan keju parut yang gurih πŸ§€\",\n"," \"Coklat\": \"Pisang geprek dengan topping coklat manis 🍫\",\n"," \"Matcha\": \"Pisang geprek dengan rasa matcha yang unik πŸƒ\"\n"," },\n"," \"best_seller\": \"Original dan Keju Parut\"\n","}\n","\n","# Admin credentials\n","ADMIN_USERNAME = \"@zahradmin\"\n","ADMIN_PASSWORD = \"@admin123\"\n","\n","# State management\n","class AppState:\n"," def __init__(self):\n"," self.is_admin_logged_in = False\n"," self.menu_items = BUSINESS_INFO[\"menu\"].copy()\n"," self.uploaded_images = []\n","\n","app_state = AppState()\n","\n","def create_chatbot_response(message, history):\n"," \"\"\"Generate chatbot response using Gemini AI\"\"\"\n"," try:\n"," # Context untuk chatbot\n"," context = f\"\"\"\n","Kamu adalah chatbot untuk bisnis pisang geprek bernama \"Pisangora\" di Palangka Raya.\n","Berikut informasi bisnis:\n","- Nama: {BUSINESS_INFO['name']}\n","- WhatsApp: {BUSINESS_INFO['whatsapp']}\n","- Instagram: {BUSINESS_INFO['instagram']} ({BUSINESS_INFO['instagram_link']})\n","- Lokasi: {BUSINESS_INFO['location']}\n","- Jam Operasional: {BUSINESS_INFO['operating_hours']}\n","- Harga: Rp {BUSINESS_INFO['price']}\n","- Menu: {json.dumps(BUSINESS_INFO['menu'], ensure_ascii=False)}\n","- Best Seller: {BUSINESS_INFO['best_seller']}\n","\n","Cara memesan:\n","1. Hubungi WhatsApp: {BUSINESS_INFO['whatsapp']}\n","2. Follow Instagram: {BUSINESS_INFO['instagram']}\n","3. Gratis ongkir area Palangka Raya\n","\n","Jawab pertanyaan dengan ramah, gunakan emoji iPhone, dan selalu berikan informasi yang akurat.\n","Jika ditanya tentang pisang geprek, jelaskan bahwa ini adalah pisang yang dipanggang lalu \"digeprek\" dan diberi topping.\n","\"\"\"\n","\n"," model = genai.GenerativeModel('gemini-pro')\n","\n"," # Format history untuk context\n"," conversation_history = \"\"\n"," if history:\n"," for human_msg, bot_msg in history:\n"," conversation_history += f\"Human: {human_msg}\\nBot: {bot_msg}\\n\"\n","\n"," full_prompt = f\"{context}\\n\\nPercakapan sebelumnya:\\n{conversation_history}\\n\\nPertanyaan terbaru: {message}\"\n","\n"," response = model.generate_content(full_prompt)\n"," return response.text\n","\n"," except Exception as e:\n"," return f\"Maaf, terjadi kesalahan. Silakan hubungi langsung ke WhatsApp {BUSINESS_INFO['whatsapp']} πŸ“±\"\n","\n","def admin_login(username, password):\n"," \"\"\"Handle admin login\"\"\"\n"," if username == ADMIN_USERNAME and password == ADMIN_PASSWORD:\n"," app_state.is_admin_logged_in = True\n"," return True, \"βœ… Login berhasil! Selamat datang Admin.\", gr.update(visible=True)\n"," else:\n"," return False, \"❌ Username atau password salah!\", gr.update(visible=False)\n","\n","def admin_logout():\n"," \"\"\"Handle admin logout\"\"\"\n"," app_state.is_admin_logged_in = False\n"," return False, \"Logout berhasil\", gr.update(visible=False)\n","\n","def update_menu_item(menu_name, description):\n"," \"\"\"Update menu item\"\"\"\n"," if app_state.is_admin_logged_in and menu_name and description:\n"," app_state.menu_items[menu_name] = description\n"," return f\"βœ… Menu '{menu_name}' berhasil diupdate!\"\n"," return \"❌ Gagal update menu\"\n","\n","def delete_menu_item(menu_name):\n"," \"\"\"Delete menu item\"\"\"\n"," if app_state.is_admin_logged_in and menu_name in app_state.menu_items:\n"," del app_state.menu_items[menu_name]\n"," return f\"βœ… Menu '{menu_name}' berhasil dihapus!\"\n"," return \"❌ Gagal hapus menu\"\n","\n","def get_current_menu():\n"," \"\"\"Get current menu as string\"\"\"\n"," menu_text = \"πŸ“‹ **MENU PISANGORA TERKINI:**\\n\\n\"\n"," for name, desc in app_state.menu_items.items():\n"," menu_text += f\"β€’ **{name}**: {desc}\\n\"\n"," menu_text += f\"\\nπŸ’° **Harga**: Rp {BUSINESS_INFO['price']}\"\n"," return menu_text\n","\n","def upload_image(image):\n"," \"\"\"Handle image upload\"\"\"\n"," if app_state.is_admin_logged_in and image is not None:\n"," app_state.uploaded_images.append(image)\n"," return f\"βœ… Gambar berhasil diupload! Total gambar: {len(app_state.uploaded_images)}\"\n"," return \"❌ Gagal upload gambar\"\n","\n","# CSS untuk tema kuning-putih\n","css = \"\"\"\n","/* Force light theme dengan warna kuning-putih */\n",".gradio-container, .gradio-container * {\n"," background-color: #FFFEF7 !important;\n"," color: #B8860B !important;\n","}\n","\n",".gradio-container .dark {\n"," background-color: #FFFEF7 !important;\n","}\n","\n","/* Header styling */\n",".header {\n"," background: linear-gradient(135deg, #FFD700, #FFF8DC) !important;\n"," padding: 20px !important;\n"," text-align: center !important;\n"," border-radius: 15px !important;\n"," margin-bottom: 20px !important;\n","}\n","\n","/* Button styling */\n",".gradio-container button {\n"," background: linear-gradient(135deg, #FFD700, #FFA500) !important;\n"," color: white !important;\n"," border: none !important;\n"," border-radius: 10px !important;\n"," font-weight: bold !important;\n","}\n","\n",".gradio-container button:hover {\n"," background: linear-gradient(135deg, #FFA500, #FFD700) !important;\n"," transform: translateY(-2px) !important;\n"," box-shadow: 0 4px 8px rgba(255,215,0,0.3) !important;\n","}\n","\n","/* Input styling */\n",".gradio-container input, .gradio-container textarea {\n"," border: 2px solid #FFD700 !important;\n"," border-radius: 10px !important;\n"," background-color: #FFFEF7 !important;\n"," color: #B8860B !important;\n","}\n","\n","/* Chat styling */\n",".gradio-container .message {\n"," background-color: #FFF8DC !important;\n"," border: 1px solid #FFD700 !important;\n"," border-radius: 10px !important;\n","}\n","\n","/* Admin panel */\n",".admin-panel {\n"," background: linear-gradient(135deg, #FFE4B5, #FFFACD) !important;\n"," padding: 15px !important;\n"," border-radius: 10px !important;\n"," border: 2px solid #FFD700 !important;\n","}\n","\"\"\"\n","\n","# Membuat interface\n","with gr.Blocks(css=css, title=\"Pisangora - Pisang Geprek Palangka Raya\", theme=gr.themes.Base()) as app:\n","\n"," # State variables\n"," admin_logged_in = gr.State(False)\n","\n"," # Header\n"," gr.HTML(\"\"\"\n"," <div class=\"header\">\n"," <h1>🍌 PISANGORA 🍌</h1>\n"," <h3>Pisang Geprek Terenak di Palangka Raya!</h3>\n"," <p>πŸ“ Free Delivery Area Palangka Raya | πŸ“ž +62 821-5399-4497</p>\n"," <p>πŸ“Έ Instagram: @pisangora_pky | ⏰ Buka Setiap Hari 09:00-21:00</p>\n"," </div>\n"," \"\"\")\n","\n"," # Main content\n"," with gr.Tab(\"🏠 Beranda & Chat\"):\n"," gr.HTML(\"\"\"\n"," <div style=\"text-align: center; padding: 20px; background: linear-gradient(135deg, #FFF8DC, #FFFACD); border-radius: 15px; margin: 10px;\">\n"," <h2>🍌 Apa itu Pisang Geprek? 🍌</h2>\n"," <p style=\"font-size: 16px;\">Pisang geprek adalah pisang yang dipanggang sempurna, lalu di-'geprek' dan diberi berbagai topping lezat! Tekstur lembut di dalam, crispy di luar! πŸ˜‹</p>\n"," <h3>πŸ’° Harga Special: Rp 15.000 saja!</h3>\n"," </div>\n"," \"\"\")\n","\n"," # Menu display\n"," menu_display = gr.Markdown(get_current_menu())\n","\n"," # Refresh menu button\n"," refresh_btn = gr.Button(\"πŸ”„ Refresh Menu\", variant=\"secondary\")\n"," refresh_btn.click(fn=lambda: get_current_menu(), outputs=menu_display)\n","\n"," # Chatbot\n"," gr.HTML(\"<h3 style='text-align: center;'>πŸ’¬ Ada Pertanyaan? Chat dengan Bot Pisangora!</h3>\")\n"," chatbot = gr.Chatbot(\n"," height=400,\n"," placeholder=\"Ketik pesan Anda di bawah untuk bertanya tentang Pisangora! 🍌\",\n"," avatar_images=(\"πŸ§‘\", \"πŸ€–\")\n"," )\n"," msg = gr.Textbox(\n"," placeholder=\"Tanya apa saja tentang pisang geprek, menu, harga, cara pesan...\",\n"," show_label=False\n"," )\n","\n"," # Chat functionality\n"," msg.submit(\n"," fn=create_chatbot_response,\n"," inputs=[msg, chatbot],\n"," outputs=chatbot\n"," ).then(lambda: \"\", outputs=msg)\n","\n"," # Contact info\n"," gr.HTML(\"\"\"\n"," <div style=\"background: linear-gradient(135deg, #FFE4B5, #FFFACD); padding: 20px; border-radius: 15px; text-align: center; margin-top: 20px;\">\n"," <h3>πŸ“ž Pesan Sekarang!</h3>\n"," <p><strong>WhatsApp:</strong> +62 821-5399-4497</p>\n"," <p><strong>Instagram:</strong> <a href=\"https://www.instagram.com/pisangora_pky?igsh=MXM0enZ0dWU4bnoyaA==\" target=\"_blank\">@pisangora_pky</a></p>\n"," <p><strong>Area:</strong> Gratis ongkir se-Palangka Raya! 🚚✨</p>\n"," </div>\n"," \"\"\")\n","\n"," # Admin panel\n"," with gr.Tab(\"πŸ” Admin Login\"):\n"," gr.HTML(\"<h2 style='text-align: center;'>πŸ” Admin Panel Pisangora</h2>\")\n","\n"," # Login form\n"," with gr.Group():\n"," username_input = gr.Textbox(label=\"Username\", placeholder=\"Masukkan username admin\")\n"," password_input = gr.Textbox(label=\"Password\", type=\"password\", placeholder=\"Masukkan password admin\")\n"," login_btn = gr.Button(\"πŸ”‘ Login\", variant=\"primary\")\n"," login_status = gr.Textbox(label=\"Status\", interactive=False)\n","\n"," # Admin panel (initially hidden)\n"," with gr.Group(visible=False) as admin_panel:\n"," gr.HTML(\"\"\"\n"," <div class=\"admin-panel\">\n"," <h3>πŸŽ›οΈ Panel Admin Pisangora</h3>\n"," <p>Selamat datang Admin! Kelola menu dan konten di sini.</p>\n"," </div>\n"," \"\"\")\n","\n"," with gr.Tab(\"πŸ“‹ Kelola Menu\"):\n"," gr.HTML(\"<h4>βž• Tambah/Edit Menu</h4>\")\n"," new_menu_name = gr.Textbox(label=\"Nama Menu\", placeholder=\"Contoh: Strawberry\")\n"," new_menu_desc = gr.Textbox(label=\"Deskripsi Menu\", placeholder=\"Contoh: Pisang geprek dengan topping strawberry segar πŸ“\")\n"," add_menu_btn = gr.Button(\"βž• Tambah/Update Menu\")\n"," menu_update_status = gr.Textbox(label=\"Status Update\", interactive=False)\n","\n"," gr.HTML(\"<h4>πŸ—‘οΈ Hapus Menu</h4>\")\n"," delete_menu_name = gr.Textbox(label=\"Nama Menu yang akan dihapus\")\n"," delete_menu_btn = gr.Button(\"πŸ—‘οΈ Hapus Menu\", variant=\"stop\")\n"," menu_delete_status = gr.Textbox(label=\"Status Hapus\", interactive=False)\n","\n"," with gr.Tab(\"πŸ–ΌοΈ Kelola Gambar\"):\n"," gr.HTML(\"<h4>πŸ“Έ Upload Gambar Produk</h4>\")\n"," image_upload = gr.File(\n"," label=\"Upload Gambar\",\n"," file_types=[\"image\"],\n"," file_count=\"multiple\"\n"," )\n"," upload_btn = gr.Button(\"πŸ“€ Upload Gambar\")\n"," upload_status = gr.Textbox(label=\"Status Upload\", interactive=False)\n","\n"," gr.HTML(\"<h4>πŸ–ΌοΈ Gallery Gambar</h4>\")\n"," image_gallery = gr.Gallery(\n"," label=\"Gambar Produk\",\n"," show_label=True,\n"," elem_id=\"gallery\",\n"," columns=3,\n"," rows=2,\n"," height=\"auto\"\n"," )\n","\n"," # Logout button\n"," logout_btn = gr.Button(\"πŸšͺ Logout\", variant=\"stop\")\n","\n"," # Event handlers\n"," login_btn.click(\n"," fn=admin_login,\n"," inputs=[username_input, password_input],\n"," outputs=[admin_logged_in, login_status, admin_panel]\n"," )\n","\n"," logout_btn.click(\n"," fn=admin_logout,\n"," outputs=[admin_logged_in, login_status, admin_panel]\n"," )\n","\n"," add_menu_btn.click(\n"," fn=update_menu_item,\n"," inputs=[new_menu_name, new_menu_desc],\n"," outputs=menu_update_status\n"," )\n","\n"," delete_menu_btn.click(\n"," fn=delete_menu_item,\n"," inputs=[delete_menu_name],\n"," outputs=menu_delete_status\n"," )\n","\n"," upload_btn.click(\n"," fn=upload_image,\n"," inputs=[image_upload],\n"," outputs=upload_status\n"," )\n","\n","# Launch aplikasi\n","if __name__ == \"__main__\":\n"," print(\"🍌 Memulai Pisangora Web App...\")\n"," print(\"πŸ“± Pastikan sudah install: pip install gradio google-generativeai pillow\")\n"," app.launch(\n"," server_name=\"0.0.0.0\",\n"," server_port=7860,\n"," share=True,\n"," debug=True,\n"," show_error=True\n"," )"],"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":1000},"id":"eiuuYq0Ni_AD","executionInfo":{"status":"ok","timestamp":1758698214209,"user_tz":-420,"elapsed":591859,"user":{"displayName":"Zahra Widya Putri","userId":"05276937067783338784"}},"outputId":"a29f2d22-c437-4743-fccc-2fd2e3e0f874"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stderr","text":["/tmp/ipython-input-4261143271.py:224: UserWarning: You have not specified a value for the `type` parameter. Defaulting to the 'tuples' format for chatbot messages, but this is deprecated and will be removed in a future version of Gradio. Please set type='messages' instead, which uses openai-style dictionaries with 'role' and 'content' keys.\n"," chatbot = gr.Chatbot(\n"]},{"output_type":"stream","name":"stdout","text":["🍌 Memulai Pisangora Web App...\n","πŸ“± Pastikan sudah install: pip install gradio google-generativeai pillow\n","Colab notebook detected. This cell will run indefinitely so that you can see errors and logs. To turn off, set debug=False in launch().\n","* Running on public URL: https://1440fb2d915637c6c5.gradio.live\n","\n","This share link expires in 1 week. For free permanent hosting and GPU upgrades, run `gradio deploy` from the terminal in the working directory to deploy to Hugging Face Spaces (https://huggingface.co/spaces)\n"]},{"output_type":"display_data","data":{"text/plain":["<IPython.core.display.HTML object>"],"text/html":["<div><iframe src=\"https://1440fb2d915637c6c5.gradio.live\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"]},"metadata":{}},{"output_type":"stream","name":"stderr","text":["WARNING:tornado.access:404 POST /v1beta/models/gemini-pro:generateContent?%24alt=json%3Benum-encoding%3Dint (127.0.0.1) 1929.47ms\n","Traceback (most recent call last):\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/queueing.py\", line 745, in process_events\n"," response = await route_utils.call_process_api(\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/route_utils.py\", line 353, in call_process_api\n"," output = await app.get_blocks().process_api(\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/blocks.py\", line 2127, in process_api\n"," data = await self.postprocess_data(block_fn, result[\"prediction\"], state)\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/blocks.py\", line 1904, in postprocess_data\n"," prediction_value = block.postprocess(prediction_value)\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/components/chatbot.py\", line 632, in postprocess\n"," self._check_format(value, \"tuples\")\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/components/chatbot.py\", line 429, in _check_format\n"," raise Error(\n","gradio.exceptions.Error: 'Data incompatible with tuples format. Each message should be a list of length 2.'\n","WARNING:tornado.access:404 POST /v1beta/models/gemini-pro:generateContent?%24alt=json%3Benum-encoding%3Dint (127.0.0.1) 588.73ms\n","Traceback (most recent call last):\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/queueing.py\", line 745, in process_events\n"," response = await route_utils.call_process_api(\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/route_utils.py\", line 353, in call_process_api\n"," output = await app.get_blocks().process_api(\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/blocks.py\", line 2127, in process_api\n"," data = await self.postprocess_data(block_fn, result[\"prediction\"], state)\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/blocks.py\", line 1904, in postprocess_data\n"," prediction_value = block.postprocess(prediction_value)\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/components/chatbot.py\", line 632, in postprocess\n"," self._check_format(value, \"tuples\")\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/components/chatbot.py\", line 429, in _check_format\n"," raise Error(\n","gradio.exceptions.Error: 'Data incompatible with tuples format. Each message should be a list of length 2.'\n","WARNING:tornado.access:404 POST /v1beta/models/gemini-pro:generateContent?%24alt=json%3Benum-encoding%3Dint (127.0.0.1) 736.50ms\n","Traceback (most recent call last):\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/queueing.py\", line 745, in process_events\n"," response = await route_utils.call_process_api(\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/route_utils.py\", line 353, in call_process_api\n"," output = await app.get_blocks().process_api(\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/blocks.py\", line 2127, in process_api\n"," data = await self.postprocess_data(block_fn, result[\"prediction\"], state)\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/blocks.py\", line 1904, in postprocess_data\n"," prediction_value = block.postprocess(prediction_value)\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/components/chatbot.py\", line 632, in postprocess\n"," self._check_format(value, \"tuples\")\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/components/chatbot.py\", line 429, in _check_format\n"," raise Error(\n","gradio.exceptions.Error: 'Data incompatible with tuples format. Each message should be a list of length 2.'\n","WARNING:tornado.access:404 POST /v1beta/models/gemini-pro:generateContent?%24alt=json%3Benum-encoding%3Dint (127.0.0.1) 560.62ms\n","Traceback (most recent call last):\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/queueing.py\", line 745, in process_events\n"," response = await route_utils.call_process_api(\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/route_utils.py\", line 353, in call_process_api\n"," output = await app.get_blocks().process_api(\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/blocks.py\", line 2127, in process_api\n"," data = await self.postprocess_data(block_fn, result[\"prediction\"], state)\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/blocks.py\", line 1904, in postprocess_data\n"," prediction_value = block.postprocess(prediction_value)\n"," ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/components/chatbot.py\", line 632, in postprocess\n"," self._check_format(value, \"tuples\")\n"," File \"/usr/local/lib/python3.12/dist-packages/gradio/components/chatbot.py\", line 429, in _check_format\n"," raise Error(\n","gradio.exceptions.Error: 'Data incompatible with tuples format. Each message should be a list of length 2.'\n"]},{"output_type":"stream","name":"stdout","text":["Keyboard interruption in main thread... closing server.\n","Killing tunnel 0.0.0.0:7860 <> https://1440fb2d915637c6c5.gradio.live\n"]}]},{"cell_type":"code","source":["import gradio as gr\n","import google.generativeai as genai\n","import json\n","import base64\n","from PIL import Image\n","import io\n","import os\n","from datetime import datetime\n","\n","# Konfigurasi Gemini AI\n","GEMINI_API_KEY = \"AIzaSyBT4qnwSUYxdgquAQjAqvQQQaeOaRzDiL0\"\n","genai.configure(api_key=GEMINI_API_KEY)\n","\n","# Data bisnis\n","BUSINESS_INFO = {\n"," \"name\": \"Pisangora\",\n"," \"whatsapp\": \"+62 821-5399-4497\",\n"," \"instagram\": \"@pisangora_pky\",\n"," \"instagram_link\": \"https://www.instagram.com/pisangora_pky?igsh=MXM0enZ0dWU4bnoyaA==\",\n"," \"location\": \"Palangka Raya (Free Delivery Area)\",\n"," \"operating_hours\": \"Setiap hari jam 09:00 - 21:00\",\n"," \"price\": \"15.000\",\n"," \"menu\": {\n"," \"Original\": \"Pisang geprek original dengan rasa pisang murni yang autentik 🍌\",\n"," \"Sambal\": \"Pisang geprek dengan sambal pedas level mantap yang bikin nagih 🌢️\",\n"," \"Keju Parut\": \"Pisang geprek dengan taburan keju parut yang melimpah dan gurih πŸ§€\",\n"," \"Coklat\": \"Pisang geprek dengan topping coklat premium yang manis legit 🍫\",\n"," \"Matcha\": \"Pisang geprek dengan rasa matcha authentic dari Jepang yang unik πŸƒ\"\n"," },\n"," \"best_seller\": \"Original dan Keju Parut\"\n","}\n","\n","# Admin credentials\n","ADMIN_USERNAME = \"@zahradmin\"\n","ADMIN_PASSWORD = \"@admin123\"\n","\n","# State management\n","class AppState:\n"," def __init__(self):\n"," self.is_admin_logged_in = False\n"," self.menu_items = BUSINESS_INFO[\"menu\"].copy()\n"," self.product_images = []\n"," self.logo_image = None\n"," self.hero_images = []\n","\n","app_state = AppState()\n","\n","# Quick questions untuk chatbot\n","QUICK_QUESTIONS = [\n"," \"Apa itu pisang geprek? πŸ€”\",\n"," \"Menu apa saja yang tersedia? πŸ“‹\",\n"," \"Berapa harga pisang geprek? πŸ’°\",\n"," \"Mana yang best seller? ⭐\",\n"," \"Bagaimana cara pesan? πŸ“±\",\n"," \"Jam buka kapan? ⏰\",\n"," \"Area delivery mana saja? 🚚\",\n"," \"Ada promo gak? πŸŽ‰\"\n","]\n","\n","def create_chatbot_response(message, history):\n"," \"\"\"Generate chatbot response using Gemini AI\"\"\"\n"," try:\n"," context = f\"\"\"\n","Kamu adalah chatbot untuk bisnis pisang geprek bernama \"Pisangora\" di Palangka Raya.\n","Berikut informasi bisnis:\n","- Nama: {BUSINESS_INFO['name']}\n","- WhatsApp: {BUSINESS_INFO['whatsapp']}\n","- Instagram: {BUSINESS_INFO['instagram']} ({BUSINESS_INFO['instagram_link']})\n","- Lokasi: {BUSINESS_INFO['location']}\n","- Jam Operasional: {BUSINESS_INFO['operating_hours']}\n","- Harga: Rp {BUSINESS_INFO['price']}\n","- Menu: {json.dumps(app_state.menu_items, ensure_ascii=False)}\n","- Best Seller: {BUSINESS_INFO['best_seller']}\n","\n","Cara memesan:\n","1. Hubungi WhatsApp: {BUSINESS_INFO['whatsapp']}\n","2. Follow Instagram: {BUSINESS_INFO['instagram']}\n","3. Gratis ongkir area Palangka Raya\n","\n","Jawab pertanyaan dengan ramah, gunakan emoji iPhone yang relevan, dan selalu berikan informasi yang akurat.\n","Jika ditanya tentang pisang geprek, jelaskan bahwa ini adalah pisang yang dipanggang lalu \"digeprek\" dan diberi topping.\n","Gunakan gaya bahasa yang casual dan friendly seperti anak muda zaman now.\n","\"\"\"\n","\n"," model = genai.GenerativeModel('gemini-pro')\n","\n"," conversation_history = \"\"\n"," if history:\n"," for human_msg, bot_msg in history[-3:]: # Ambil 3 percakapan terakhir\n"," conversation_history += f\"Human: {human_msg}\\nBot: {bot_msg}\\n\"\n","\n"," full_prompt = f\"{context}\\n\\nPercakapan sebelumnya:\\n{conversation_history}\\n\\nPertanyaan terbaru: {message}\"\n","\n"," response = model.generate_content(full_prompt)\n"," return response.text\n","\n"," except Exception as e:\n"," return f\"Maaf kak, lagi ada gangguan nih πŸ˜… Langsung aja hubungi WhatsApp {BUSINESS_INFO['whatsapp']} ya! πŸ“±βœ¨\"\n","\n","def send_quick_question(question, history):\n"," \"\"\"Send quick question to chatbot\"\"\"\n"," response = create_chatbot_response(question, history)\n"," history.append([question, response])\n"," return history, \"\"\n","\n","def admin_login(username, password):\n"," \"\"\"Handle admin login\"\"\"\n"," if username == ADMIN_USERNAME and password == ADMIN_PASSWORD:\n"," app_state.is_admin_logged_in = True\n"," return True, \"βœ… Login berhasil! Selamat datang Admin Pisangora! πŸŽ‰\", gr.update(visible=True)\n"," else:\n"," return False, \"❌ Username atau password salah! Coba lagi ya πŸ”\", gr.update(visible=False)\n","\n","def admin_logout():\n"," \"\"\"Handle admin logout\"\"\"\n"," app_state.is_admin_logged_in = False\n"," return False, \"πŸ‘‹ Logout berhasil! Sampai jumpa Admin!\", gr.update(visible=False)\n","\n","def update_menu_item(menu_name, description):\n"," \"\"\"Update menu item\"\"\"\n"," if app_state.is_admin_logged_in and menu_name and description:\n"," app_state.menu_items[menu_name] = description\n"," return f\"βœ… Menu '{menu_name}' berhasil diupdate! πŸŽ‰\"\n"," return \"❌ Gagal update menu. Pastikan semua field terisi!\"\n","\n","def delete_menu_item(menu_name):\n"," \"\"\"Delete menu item\"\"\"\n"," if app_state.is_admin_logged_in and menu_name in app_state.menu_items:\n"," del app_state.menu_items[menu_name]\n"," return f\"βœ… Menu '{menu_name}' berhasil dihapus! πŸ—‘οΈ\"\n"," return \"❌ Menu tidak ditemukan atau gagal dihapus!\"\n","\n","def get_current_menu():\n"," \"\"\"Get current menu as formatted HTML\"\"\"\n"," menu_html = \"\"\"\n"," <div style=\"background: linear-gradient(135deg, #FFE4B5, #FFFACD); padding: 25px; border-radius: 20px; margin: 15px 0; box-shadow: 0 8px 25px rgba(255,215,0,0.3);\">\n"," <h2 style=\"text-align: center; color: #B8860B; margin-bottom: 20px; font-size: 28px;\">🍌 MENU PISANGORA 🍌</h2>\n"," <div style=\"display: grid; gap: 15px;\">\n"," \"\"\"\n","\n"," for name, desc in app_state.menu_items.items():\n"," menu_html += f\"\"\"\n"," <div style=\"background: rgba(255,255,255,0.8); padding: 15px; border-radius: 15px; border-left: 5px solid #FFD700; backdrop-filter: blur(10px);\">\n"," <h3 style=\"color: #B8860B; margin: 0 0 8px 0; font-size: 20px;\">πŸ”₯ {name}</h3>\n"," <p style=\"margin: 0; color: #8B4513; font-size: 16px;\">{desc}</p>\n"," </div>\n"," \"\"\"\n","\n"," menu_html += f\"\"\"\n"," </div>\n"," <div style=\"text-align: center; margin-top: 25px; padding: 15px; background: rgba(255,215,0,0.2); border-radius: 15px;\">\n"," <h3 style=\"color: #B8860B; font-size: 24px; margin: 0;\">πŸ’° Harga Special: Rp {BUSINESS_INFO['price']} aja!</h3>\n"," <p style=\"color: #8B4513; margin: 5px 0 0 0;\">⭐ Best Seller: {BUSINESS_INFO['best_seller']}</p>\n"," </div>\n"," </div>\n"," \"\"\"\n"," return menu_html\n","\n","def upload_logo(image):\n"," \"\"\"Handle logo upload\"\"\"\n"," if app_state.is_admin_logged_in and image is not None:\n"," app_state.logo_image = image\n"," return \"βœ… Logo berhasil diupload! 🎨\", image\n"," return \"❌ Gagal upload logo!\", None\n","\n","def upload_product_images(images):\n"," \"\"\"Handle product images upload\"\"\"\n"," if app_state.is_admin_logged_in and images:\n"," for img in images:\n"," app_state.product_images.append(img)\n"," return f\"βœ… {len(images)} gambar produk berhasil diupload! Total: {len(app_state.product_images)} πŸ“Έ\"\n"," return \"❌ Gagal upload gambar produk!\"\n","\n","def upload_hero_images(images):\n"," \"\"\"Handle hero banner images upload\"\"\"\n"," if app_state.is_admin_logged_in and images:\n"," app_state.hero_images = images # Replace hero images\n"," return f\"βœ… {len(images)} gambar banner berhasil diupload! 🎯\"\n"," return \"❌ Gagal upload gambar banner!\"\n","\n","def get_gallery_images():\n"," \"\"\"Get all uploaded images for gallery\"\"\"\n"," return app_state.product_images if app_state.product_images else []\n","\n","def get_hero_gallery():\n"," \"\"\"Get hero images for display\"\"\"\n"," return app_state.hero_images if app_state.hero_images else []\n","\n","# Modern CSS dengan glassmorphism dan animasi\n","css = \"\"\"\n","/* Modern styling dengan glassmorphism */\n",":root {\n"," --primary-gold: #FFD700;\n"," --secondary-gold: #FFA500;\n"," --cream: #FFFEF7;\n"," --light-cream: #FFF8DC;\n"," --text-dark: #B8860B;\n"," --text-brown: #8B4513;\n","}\n","\n",".gradio-container, .gradio-container * {\n"," background-color: var(--cream) !important;\n"," color: var(--text-dark) !important;\n"," font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;\n","}\n","\n",".gradio-container .dark {\n"," background-color: var(--cream) !important;\n","}\n","\n","/* Animated gradient background */\n","body {\n"," background: linear-gradient(-45deg, #FFE4B5, #FFFACD, #FFF8DC, #FFEBCD) !important;\n"," background-size: 400% 400% !important;\n"," animation: gradientBG 15s ease infinite !important;\n","}\n","\n","@keyframes gradientBG {\n"," 0% { background-position: 0% 50%; }\n"," 50% { background-position: 100% 50%; }\n"," 100% { background-position: 0% 50%; }\n","}\n","\n","/* Modern header with glassmorphism */\n",".modern-header {\n"," background: rgba(255, 255, 255, 0.25) !important;\n"," backdrop-filter: blur(20px) !important;\n"," border: 1px solid rgba(255, 215, 0, 0.2) !important;\n"," padding: 30px !important;\n"," text-align: center !important;\n"," border-radius: 25px !important;\n"," margin-bottom: 30px !important;\n"," box-shadow: 0 15px 35px rgba(255,215,0,0.1) !important;\n"," transition: all 0.3s ease !important;\n","}\n","\n",".modern-header:hover {\n"," transform: translateY(-5px) !important;\n"," box-shadow: 0 25px 50px rgba(255,215,0,0.2) !important;\n","}\n","\n","/* Modern buttons dengan hover effects */\n",".gradio-container button {\n"," background: linear-gradient(135deg, var(--primary-gold), var(--secondary-gold)) !important;\n"," color: white !important;\n"," border: none !important;\n"," border-radius: 15px !important;\n"," font-weight: 600 !important;\n"," padding: 12px 24px !important;\n"," font-size: 14px !important;\n"," transition: all 0.3s ease !important;\n"," box-shadow: 0 5px 15px rgba(255,215,0,0.3) !important;\n","}\n","\n",".gradio-container button:hover {\n"," background: linear-gradient(135deg, var(--secondary-gold), var(--primary-gold)) !important;\n"," transform: translateY(-3px) !important;\n"," box-shadow: 0 10px 25px rgba(255,215,0,0.4) !important;\n","}\n","\n","/* Quick question buttons */\n",".quick-btn {\n"," background: rgba(255, 255, 255, 0.8) !important;\n"," color: var(--text-dark) !important;\n"," border: 2px solid var(--primary-gold) !important;\n"," border-radius: 20px !important;\n"," padding: 8px 16px !important;\n"," margin: 4px !important;\n"," font-size: 13px !important;\n"," transition: all 0.3s ease !important;\n"," backdrop-filter: blur(10px) !important;\n","}\n","\n",".quick-btn:hover {\n"," background: var(--primary-gold) !important;\n"," color: white !important;\n"," transform: scale(1.05) !important;\n","}\n","\n","/* Modern input fields */\n",".gradio-container input, .gradio-container textarea {\n"," border: 2px solid rgba(255, 215, 0, 0.3) !important;\n"," border-radius: 15px !important;\n"," background: rgba(255, 255, 255, 0.8) !important;\n"," color: var(--text-dark) !important;\n"," padding: 12px !important;\n"," transition: all 0.3s ease !important;\n"," backdrop-filter: blur(10px) !important;\n","}\n","\n",".gradio-container input:focus, .gradio-container textarea:focus {\n"," border: 2px solid var(--primary-gold) !important;\n"," box-shadow: 0 0 20px rgba(255,215,0,0.3) !important;\n"," transform: translateY(-2px) !important;\n","}\n","\n","/* Modern chat styling */\n",".gradio-container .message {\n"," background: rgba(255, 255, 255, 0.9) !important;\n"," border: 1px solid rgba(255, 215, 0, 0.2) !important;\n"," border-radius: 20px !important;\n"," margin: 8px 0 !important;\n"," backdrop-filter: blur(15px) !important;\n"," box-shadow: 0 5px 15px rgba(255,215,0,0.1) !important;\n","}\n","\n","/* Modern admin panel */\n",".admin-panel {\n"," background: rgba(255, 228, 181, 0.3) !important;\n"," backdrop-filter: blur(20px) !important;\n"," padding: 25px !important;\n"," border-radius: 20px !important;\n"," border: 2px solid rgba(255, 215, 0, 0.3) !important;\n"," box-shadow: 0 15px 35px rgba(255,215,0,0.1) !important;\n","}\n","\n","/* Modern tabs */\n",".gradio-container .tab-nav button {\n"," border-radius: 15px 15px 0 0 !important;\n"," background: rgba(255, 255, 255, 0.6) !important;\n"," backdrop-filter: blur(10px) !important;\n","}\n","\n",".gradio-container .tab-nav button.selected {\n"," background: var(--primary-gold) !important;\n"," color: white !important;\n","}\n","\n","/* Modern gallery */\n",".gradio-container .gallery {\n"," border-radius: 20px !important;\n"," overflow: hidden !important;\n"," box-shadow: 0 10px 30px rgba(255,215,0,0.2) !important;\n","}\n","\n","/* Floating elements */\n",".floating-card {\n"," background: rgba(255, 255, 255, 0.25) !important;\n"," backdrop-filter: blur(20px) !important;\n"," border-radius: 20px !important;\n"," padding: 20px !important;\n"," margin: 15px 0 !important;\n"," border: 1px solid rgba(255, 215, 0, 0.2) !important;\n"," box-shadow: 0 10px 30px rgba(255,215,0,0.1) !important;\n"," transition: all 0.3s ease !important;\n","}\n","\n",".floating-card:hover {\n"," transform: translateY(-5px) !important;\n"," box-shadow: 0 20px 40px rgba(255,215,0,0.2) !important;\n","}\n","\n","/* Responsive design */\n","@media (max-width: 768px) {\n"," .modern-header {\n"," padding: 20px !important;\n"," }\n","\n"," .floating-card {\n"," margin: 10px 0 !important;\n"," padding: 15px !important;\n"," }\n","}\n","\n","/* Loading animations */\n","@keyframes pulse {\n"," 0% { opacity: 1; }\n"," 50% { opacity: 0.5; }\n"," 100% { opacity: 1; }\n","}\n","\n",".loading {\n"," animation: pulse 2s infinite;\n","}\n","\"\"\"\n","\n","# Membuat interface\n","with gr.Blocks(css=css, title=\"Pisangora - Modern Pisang Geprek Experience\", theme=gr.themes.Soft()) as app:\n","\n"," # State variables\n"," admin_logged_in = gr.State(False)\n","\n"," # Modern Header\n"," gr.HTML(\"\"\"\n"," <div class=\"modern-header\">\n"," <h1 style=\"font-size: 48px; margin: 0; background: linear-gradient(135deg, #FFD700, #FFA500); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-shadow: 0 5px 15px rgba(255,215,0,0.3);\">🍌 PISANGORA 🍌</h1>\n"," <h2 style=\"font-size: 24px; margin: 10px 0; color: #B8860B; font-weight: 300;\">Pisang Geprek Experience Terdepan di Palangka Raya</h2>\n"," <div style=\"display: flex; justify-content: center; flex-wrap: wrap; gap: 20px; margin-top: 20px;\">\n"," <div style=\"display: flex; align-items: center; background: rgba(255,255,255,0.8); padding: 10px 20px; border-radius: 25px; backdrop-filter: blur(10px);\">\n"," <span style=\"font-size: 18px; margin-right: 8px;\">πŸ“</span>\n"," <span style=\"color: #8B4513; font-weight: 500;\">Free Delivery Palangka Raya</span>\n"," </div>\n"," <div style=\"display: flex; align-items: center; background: rgba(255,255,255,0.8); padding: 10px 20px; border-radius: 25px; backdrop-filter: blur(10px);\">\n"," <span style=\"font-size: 18px; margin-right: 8px;\">πŸ“ž</span>\n"," <span style=\"color: #8B4513; font-weight: 500;\">+62 821-5399-4497</span>\n"," </div>\n"," <div style=\"display: flex; align-items: center; background: rgba(255,255,255,0.8); padding: 10px 20px; border-radius: 25px; backdrop-filter: blur(10px);\">\n"," <span style=\"font-size: 18px; margin-right: 8px;\">⏰</span>\n"," <span style=\"color: #8B4513; font-weight: 500;\">09:00 - 21:00 Daily</span>\n"," </div>\n"," </div>\n"," <div style=\"margin-top: 15px;\">\n"," <a href=\"https://www.instagram.com/pisangora_pky?igsh=MXM0enZ0dWU4bnoyaA==\" target=\"_blank\" style=\"display: inline-flex; align-items: center; background: linear-gradient(135deg, #E4405F, #FCCC63); color: white; padding: 10px 20px; border-radius: 25px; text-decoration: none; font-weight: 600; transition: all 0.3s ease; box-shadow: 0 5px 15px rgba(228,64,95,0.3);\">\n"," <span style=\"font-size: 18px; margin-right: 8px;\">πŸ“Έ</span>\n"," Follow @pisangora_pky\n"," </a>\n"," </div>\n"," </div>\n"," \"\"\")\n","\n"," # Main content\n"," with gr.Tab(\"🏠 Beranda\"):\n"," # Hero section dengan gambar\n"," hero_gallery = gr.Gallery(\n"," label=\"✨ Hero Banner\",\n"," show_label=False,\n"," columns=1,\n"," rows=1,\n"," height=300,\n"," elem_classes=[\"hero-gallery\"],\n"," value=[]\n"," )\n","\n"," # What is Pisang Geprek\n"," gr.HTML(\"\"\"\n"," <div class=\"floating-card\">\n"," <h2 style=\"text-align: center; color: #B8860B; margin-bottom: 20px; font-size: 32px;\">🍌 Apa itu Pisang Geprek? 🍌</h2>\n"," <p style=\"font-size: 18px; line-height: 1.8; text-align: center; color: #8B4513; margin: 0;\">\n"," Pisang geprek adalah inovasi kuliner modern yang menggabungkan pisang premium yang dipanggang sempurna,\n"," lalu di-<strong>'geprek'</strong> untuk tekstur yang unik, dan diberi berbagai topping pilihan yang menggugah selera!\n"," <br><br>\n"," <span style=\"font-size: 20px; color: #B8860B;\">πŸ”₯ Tekstur lembut di dalam, crispy di luar! Dijamin nagih! πŸ˜‹</span>\n"," </p>\n"," </div>\n"," \"\"\")\n","\n"," # Menu display dengan refresh otomatis\n"," menu_display = gr.HTML(get_current_menu())\n"," refresh_menu_btn = gr.Button(\"πŸ”„ Refresh Menu Terbaru\", variant=\"secondary\", size=\"sm\")\n"," refresh_menu_btn.click(fn=lambda: get_current_menu(), outputs=menu_display)\n","\n"," # Product Gallery\n"," gr.HTML(\"<h2 style='text-align: center; color: #B8860B; margin: 30px 0 20px 0; font-size: 28px;'>πŸ“Έ Gallery Produk</h2>\")\n"," product_gallery = gr.Gallery(\n"," label=\"Produk Pisangora\",\n"," show_label=False,\n"," columns=3,\n"," rows=2,\n"," height=400,\n"," value=[]\n"," )\n","\n"," refresh_gallery_btn = gr.Button(\"πŸ”„ Refresh Gallery\", variant=\"secondary\", size=\"sm\")\n"," refresh_gallery_btn.click(fn=get_gallery_images, outputs=product_gallery)\n","\n"," with gr.Tab(\"πŸ’¬ Chat Assistant\"):\n"," gr.HTML(\"\"\"\n"," <div class=\"floating-card\">\n"," <h2 style=\"text-align: center; color: #B8860B; font-size: 28px; margin-bottom: 20px;\">πŸ€– Pisangora Smart Assistant</h2>\n"," <p style=\"text-align: center; color: #8B4513; font-size: 16px; margin: 0;\">\n"," Ada pertanyaan tentang pisang geprek? Chat langsung dengan assistant pintar kami atau pilih pertanyaan cepat di bawah! ✨\n"," </p>\n"," </div>\n"," \"\"\")\n","\n"," # Quick Questions\n"," gr.HTML(\"<h3 style='text-align: center; color: #B8860B; margin: 20px 0 15px 0;'>⚑ Pertanyaan Cepat</h3>\")\n","\n"," with gr.Row():\n"," quick_question_buttons = []\n"," for i in range(0, len(QUICK_QUESTIONS), 2):\n"," with gr.Column(scale=1):\n"," for j in range(2):\n"," if i + j < len(QUICK_QUESTIONS):\n"," btn = gr.Button(\n"," QUICK_QUESTIONS[i + j],\n"," variant=\"secondary\",\n"," size=\"sm\",\n"," elem_classes=[\"quick-btn\"]\n"," )\n"," quick_question_buttons.append(btn)\n","\n"," # Chatbot\n"," chatbot = gr.Chatbot(\n"," height=500,\n"," placeholder=\"Ketik pesan atau pilih pertanyaan cepat di atas untuk mulai chat! 🍌\",\n"," avatar_images=(\"πŸ‘€\", \"πŸ€–\"),\n"," bubble_full_width=False,\n"," show_copy_button=True\n"," )\n","\n"," with gr.Row():\n"," msg = gr.Textbox(\n"," placeholder=\"Ketik pertanyaan Anda disini... (misal: 'Menu apa yang paling enak?')\",\n"," show_label=False,\n"," container=False,\n"," scale=4\n"," )\n"," send_btn = gr.Button(\"πŸ“€ Kirim\", variant=\"primary\", scale=1)\n","\n"," # Chat functionality\n"," def handle_message(message, history):\n"," if message.strip():\n"," response = create_chatbot_response(message, history)\n"," history.append([message, response])\n"," return history, \"\"\n"," return history, message\n","\n"," send_btn.click(\n"," fn=handle_message,\n"," inputs=[msg, chatbot],\n"," outputs=[chatbot, msg]\n"," )\n","\n"," msg.submit(\n"," fn=handle_message,\n"," inputs=[msg, chatbot],\n"," outputs=[chatbot, msg]\n"," )\n","\n"," # Quick question functionality\n"," for i, btn in enumerate(quick_question_buttons):\n"," if i < len(QUICK_QUESTIONS):\n"," btn.click(\n"," fn=send_quick_question,\n"," inputs=[gr.State(QUICK_QUESTIONS[i]), chatbot],\n"," outputs=[chatbot, msg]\n"," )\n","\n"," # Contact CTA\n"," gr.HTML(\"\"\"\n"," <div class=\"floating-card\" style=\"text-align: center; margin-top: 30px;\">\n"," <h3 style=\"color: #B8860B; font-size: 24px; margin-bottom: 20px;\">πŸš€ Siap Pesan Pisang Geprek?</h3>\n"," <div style=\"display: flex; justify-content: center; gap: 20px; flex-wrap: wrap;\">\n"," <a href=\"https://wa.me/6282153994497\" target=\"_blank\" style=\"display: inline-flex; align-items: center; background: linear-gradient(135deg, #25D366, #128C7E); color: white; padding: 15px 25px; border-radius: 25px; text-decoration: none; font-weight: 600; transition: all 0.3s ease; box-shadow: 0 5px 15px rgba(37,211,102,0.3);\">\n"," <span style=\"font-size: 20px; margin-right: 10px;\">πŸ“±</span>\n"," Order via WhatsApp\n"," </a>\n"," <a href=\"https://www.instagram.com/pisangora_pky?igsh=MXM0enZ0dWU4bnoyaA==\" target=\"_blank\" style=\"display: inline-flex; align-items: center; background: linear-gradient(135deg, #E4405F, #FCCC63); color: white; padding: 15px 25px; border-radius: 25px; text-decoration: none; font-weight: 600; transition: all 0.3s ease; box-shadow: 0 5px 15px rgba(228,64,95,0.3);\">\n"," <span style=\"font-size: 20px; margin-right: 10px;\">πŸ“Έ</span>\n"," Follow Instagram\n"," </a>\n"," </div>\n"," <p style=\"color: #8B4513; margin: 15px 0 0 0; font-size: 16px;\">🚚✨ <strong>FREE DELIVERY</strong> se-Palangka Raya!</p>\n"," </div>\n"," \"\"\")\n","\n"," # Enhanced Admin Panel\n"," with gr.Tab(\"πŸ” Admin Dashboard\"):\n"," gr.HTML(\"\"\"\n"," <div class=\"floating-card\">\n"," <h2 style='text-align: center; color: #B8860B; font-size: 28px;'>πŸ” Admin Dashboard Pisangora</h2>\n"," <p style='text-align: center; color: #8B4513; font-size: 16px; margin: 10px 0 0 0;'>Kelola seluruh konten dan fitur website dengan mudah</p>\n"," </div>\n"," \"\"\")\n","\n"," # Login form\n"," with gr.Group():\n"," with gr.Row():\n"," with gr.Column(scale=1):\n"," pass\n"," with gr.Column(scale=2):\n"," username_input = gr.Textbox(\n"," label=\"πŸ‘€ Username Admin\",\n"," placeholder=\"Masukkan username admin\",\n"," container=True\n"," )\n"," password_input = gr.Textbox(\n"," label=\"πŸ”’ Password Admin\",\n"," type=\"password\",\n"," placeholder=\"Masukkan password admin\",\n"," container=True\n"," )\n"," login_btn = gr.Button(\"πŸ”‘ Login Dashboard\", variant=\"primary\", size=\"lg\")\n"," login_status = gr.Textbox(label=\"πŸ“Š Status Login\", interactive=False, container=True)\n"," with gr.Column(scale=1):\n"," pass\n","\n"," # Enhanced Admin Panel\n"," with gr.Group(visible=False) as admin_panel:\n"," gr.HTML(\"\"\"\n"," <div class=\"admin-panel\">\n"," <h3 style=\"text-align: center; color: #B8860B; font-size: 24px; margin-bottom: 20px;\">πŸŽ›οΈ Dashboard Admin Pisangora</h3>\n"," <p style=\"text-align: center; color: #8B4513; font-size: 16px;\">Selamat datang Admin! Kelola semua aspek bisnis Anda dari sini ✨</p>\n"," </div>\n"," \"\"\")\n","\n"," with gr.Tab(\"🍽️ Kelola Menu\"):\n"," gr.HTML(\"<h4 style='color: #B8860B; margin-bottom: 15px;'>βž• Tambah/Edit Menu Baru</h4>\")\n"," with gr.Row():\n"," new_menu_name = gr.Textbox(label=\"πŸ“ Nama Menu\", placeholder=\"Contoh: Strawberry Premium\")\n"," new_menu_desc = gr.Textbox(label=\"πŸ“‹ Deskripsi Menu\", placeholder=\"Contoh: Pisang geprek dengan topping strawberry segar dan cream πŸ“\")\n"," add_menu_btn = gr.Button(\"βž• Tambah/Update Menu\", variant=\"primary\")\n"," menu_update_status = gr.Textbox(label=\"πŸ“Š Status Update\", interactive=False)\n","\n"," gr.HTML(\"<br><h4 style='color: #B8860B; margin-bottom: 15px;'>πŸ—‘οΈ Hapus Menu</h4>\")\n"," with gr.Row():\n"," delete_menu_dropdown = gr.Dropdown(\n"," choices=list(app_state.menu_items.keys()),\n"," label=\"🎯 Pilih Menu yang akan Dihapus\",\n"," interactive=True\n"," )\n"," refresh_dropdown_btn = gr.Button(\"πŸ”„ Refresh List\", variant=\"secondary\")\n"," delete_menu_btn = gr.Button(\"πŸ—‘οΈ Hapus Menu Terpilih\", variant=\"stop\")\n"," menu_delete_status = gr.Textbox(label=\"πŸ“Š Status Hapus\", interactive=False)\n","\n"," # Update dropdown choices\n"," def refresh_menu_dropdown():\n"," return gr.update(choices=list(app_state.menu_items.keys()))\n","\n"," refresh_dropdown_btn.click(fn=refresh_menu_dropdown, outputs=delete_menu_dropdown)\n","\n"," with gr.Tab(\"🎨 Kelola Visual\"):\n"," gr.HTML(\"<h4 style='color: #B8860B; margin-bottom: 15px;'>🏷️ Upload Logo Bisnis</h4>\")\n"," logo_upload = gr.File(\n"," label=\"πŸ“€ Upload Logo Baru\",\n"," file_types=[\"image\"],\n"," file_count=\"single\"\n"," )\n"," logo_upload_btn = gr.Button(\"🎨 Set Logo\", variant=\"primary\")\n"," logo_status = gr.Textbox(label=\"πŸ“Š Status Logo\", interactive=False)\n","\n"," current_logo = gr.Image(\n"," label=\"πŸ–ΌοΈ Logo Saat Ini\",\n"," show_label=True,\n"," height=200,\n"," interactive=False\n"," )\n","\n"," gr.HTML(\"<br><h4 style='color: #B8860B; margin-bottom: 15px;'>🎯 Upload Hero Banner</h4>\")\n"," hero_upload = gr.File(\n"," label=\"πŸ“€ Upload Gambar Banner (Bisa Multiple)\",\n"," file_types=[\"image\"],\n"," file_count=\"multiple\"\n"," )\n"," hero_upload_btn = gr.Button(\"🎯 Set Hero Banner\", variant=\"primary\")\n"," hero_status = gr.Textbox(label=\"πŸ“Š Status Hero\", interactive=False)\n","\n"," gr.HTML(\"<br><h4 style='color: #B8860B; margin-bottom: 15px;'>πŸ“Έ Upload Foto Produk</h4>\")\n"," product_upload = gr.File(\n"," label=\"πŸ“€ Upload Foto Produk (Bisa Multiple)\",\n"," file_types=[\"image\"],\n"," file_count=\"multiple\"\n"," )\n"," product_upload_btn = gr.Button(\"πŸ“Έ Tambah Foto Produk\", variant=\"primary\")\n"," product_status = gr.Textbox(label=\"πŸ“Š Status Produk\", interactive=False)\n","\n"," # Current galleries\n"," gr.HTML(\"<br><h4 style='color: #B8860B; margin-bottom: 15px;'>πŸ–ΌοΈ Preview Gallery</h4>\")\n"," admin_hero_gallery = gr.Gallery(\n"," label=\"Hero Banner Saat Ini\",\n"," show_label=True,\n"," columns=2,\n"," rows=1,\n"," height=200\n"," )\n","\n"," admin_product_gallery = gr.Gallery(\n"," label=\"Foto Produk Saat Ini\",\n"," show_label=True,\n"," columns=3,\n"," rows=2,\n"," height=300\n"," )\n","\n"," refresh_galleries_btn = gr.Button(\"πŸ”„ Refresh Preview\", variant=\"secondary\")\n","\n"," with gr.Tab(\"πŸ“Š Dashboard Info\"):\n"," gr.HTML(\"\"\"\n"," <div style=\"background: rgba(255,255,255,0.8); padding: 20px; border-radius: 15px; backdrop-filter: blur(10px);\">\n"," <h4 style=\"color: #B8860B; margin-bottom: 15px;\">πŸ“ˆ Statistik Konten</h4>\n"," <div style=\"display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;\">\n"," <div style=\"background: linear-gradient(135deg, #FFE4B5, #FFFACD); padding: 15px; border-radius: 10px; text-align: center;\">\n"," <h5 style=\"color: #B8860B; margin: 0;\">🍽️ Total Menu</h5>\n"," <p style=\"font-size: 24px; font-weight: bold; color: #8B4513; margin: 5px 0 0 0;\" id=\"menu-count\">-</p>\n"," </div>\n"," <div style=\"background: linear-gradient(135deg, #FFE4B5, #FFFACD); padding: 15px; border-radius: 10px; text-align: center;\">\n"," <h5 style=\"color: #B8860B; margin: 0;\">πŸ“Έ Foto Produk</h5>\n"," <p style=\"font-size: 24px; font-weight: bold; color: #8B4513; margin: 5px 0 0 0;\" id=\"product-count\">-</p>\n"," </div>\n"," <div style=\"background: linear-gradient(135deg, #FFE4B5, #FFFACD); padding: 15px; border-radius: 10px; text-align: center;\">\n"," <h5 style=\"color: #B8860B; margin: 0;\">🎯 Hero Banner</h5>\n"," <p style=\"font-size: 24px; font-weight: bold; color: #8B4513; margin: 5px 0 0 0;\" id=\"hero-count\">-</p>\n"," </div>\n"," </div>\n"," </div>\n"," \"\"\")\n","\n"," gr.HTML(\"\"\"\n"," <br>\n"," <div style=\"background: rgba(255,255,255,0.8); padding: 20px; border-radius: 15px; backdrop-filter: blur(10px);\">\n"," <h4 style=\"color: #B8860B; margin-bottom: 15px;\">ℹ️ Informasi Bisnis</h4>\n"," <div style=\"color: #8B4513; line-height: 1.8;\">\n"," <p><strong>πŸ“± WhatsApp:</strong> +62 821-5399-4497</p>\n"," <p><strong>πŸ“Έ Instagram:</strong> @pisangora_pky</p>\n"," <p><strong>πŸ“ Area Delivery:</strong> Palangka Raya (Gratis Ongkir)</p>\n"," <p><strong>⏰ Jam Operasional:</strong> 09:00 - 21:00 (Setiap Hari)</p>\n"," <p><strong>πŸ’° Harga:</strong> Rp 15.000</p>\n"," </div>\n"," </div>\n"," \"\"\")\n","\n"," # Logout button\n"," gr.HTML(\"<br>\")\n"," logout_btn = gr.Button(\"πŸšͺ Logout Dashboard\", variant=\"stop\", size=\"lg\")\n","\n"," # Event handlers untuk admin functions\n"," def delete_selected_menu(menu_name):\n"," if menu_name and menu_name in app_state.menu_items:\n"," del app_state.menu_items[menu_name]\n"," return f\"βœ… Menu '{menu_name}' berhasil dihapus! πŸ—‘οΈ\", gr.update(choices=list(app_state.menu_items.keys()))\n"," return \"❌ Menu tidak dipilih atau tidak ditemukan!\", gr.update()\n","\n"," def get_stats():\n"," return (\n"," f\"{len(app_state.menu_items)}\",\n"," f\"{len(app_state.product_images)}\",\n"," f\"{len(app_state.hero_images)}\"\n"," )\n","\n"," # Event handlers\n"," login_btn.click(\n"," fn=admin_login,\n"," inputs=[username_input, password_input],\n"," outputs=[admin_logged_in, login_status, admin_panel]\n"," )\n","\n"," logout_btn.click(\n"," fn=admin_logout,\n"," outputs=[admin_logged_in, login_status, admin_panel]\n"," )\n","\n"," add_menu_btn.click(\n"," fn=update_menu_item,\n"," inputs=[new_menu_name, new_menu_desc],\n"," outputs=menu_update_status\n"," )\n","\n"," delete_menu_btn.click(\n"," fn=delete_selected_menu,\n"," inputs=[delete_menu_dropdown],\n"," outputs=[menu_delete_status, delete_menu_dropdown]\n"," )\n","\n"," logo_upload_btn.click(\n"," fn=upload_logo,\n"," inputs=[logo_upload],\n"," outputs=[logo_status, current_logo]\n"," )\n","\n"," hero_upload_btn.click(\n"," fn=upload_hero_images,\n"," inputs=[hero_upload],\n"," outputs=hero_status\n"," )\n","\n"," product_upload_btn.click(\n"," fn=upload_product_images,\n"," inputs=[product_upload],\n"," outputs=product_status\n"," )\n","\n"," refresh_galleries_btn.click(\n"," fn=lambda: (get_hero_gallery(), get_gallery_images()),\n"," outputs=[admin_hero_gallery, admin_product_gallery]\n"," )\n","\n"," # Auto refresh hero gallery di halaman utama\n"," hero_upload_btn.click(\n"," fn=get_hero_gallery,\n"," outputs=hero_gallery\n"," )\n","\n","# Launch aplikasi\n","if __name__ == \"__main__\":\n"," print(\"🍌 Memulai Pisangora Modern Web App...\")\n"," print(\"πŸ“± Dependencies yang dibutuhkan:\")\n"," print(\" pip install gradio google-generativeai pillow\")\n"," print(\"πŸš€ Launching aplikasi dengan fitur:\")\n"," print(\" βœ… Modern UI dengan Glassmorphism\")\n"," print(\" βœ… Smart Chatbot dengan Quick Questions\")\n"," print(\" βœ… Enhanced Admin Panel dengan Upload\")\n"," print(\" βœ… Dynamic Menu & Gallery Management\")\n"," print(\" βœ… Responsive Design untuk semua device\")\n","\n"," app.launch(\n"," server_name=\"0.0.0.0\",\n"," server_port=7860,\n"," share=True,\n"," debug=True,\n"," show_error=True,\n"," favicon_path=None\n"," )"],"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":889},"id":"2Bt7QoJxloDU","executionInfo":{"status":"ok","timestamp":1758699119830,"user_tz":-420,"elapsed":807032,"user":{"displayName":"Zahra Widya Putri","userId":"05276937067783338784"}},"outputId":"1d1f50d8-e592-472b-fe2c-cbd316854383"},"execution_count":null,"outputs":[{"output_type":"stream","name":"stderr","text":["/tmp/ipython-input-3009572360.py:484: UserWarning: You have not specified a value for the `type` parameter. Defaulting to the 'tuples' format for chatbot messages, but this is deprecated and will be removed in a future version of Gradio. Please set type='messages' instead, which uses openai-style dictionaries with 'role' and 'content' keys.\n"," chatbot = gr.Chatbot(\n","/tmp/ipython-input-3009572360.py:484: DeprecationWarning: The 'bubble_full_width' parameter is deprecated and will be removed in a future version. This parameter no longer has any effect.\n"," chatbot = gr.Chatbot(\n"]},{"output_type":"stream","name":"stdout","text":["🍌 Memulai Pisangora Modern Web App...\n","πŸ“± Dependencies yang dibutuhkan:\n"," pip install gradio google-generativeai pillow\n","πŸš€ Launching aplikasi dengan fitur:\n"," βœ… Modern UI dengan Glassmorphism\n"," βœ… Smart Chatbot dengan Quick Questions\n"," βœ… Enhanced Admin Panel dengan Upload\n"," βœ… Dynamic Menu & Gallery Management\n"," βœ… Responsive Design untuk semua device\n","Colab notebook detected. This cell will run indefinitely so that you can see errors and logs. To turn off, set debug=False in launch().\n","* Running on public URL: https://6670bd5460b50ebcdb.gradio.live\n","\n","This share link expires in 1 week. For free permanent hosting and GPU upgrades, run `gradio deploy` from the terminal in the working directory to deploy to Hugging Face Spaces (https://huggingface.co/spaces)\n"]},{"output_type":"display_data","data":{"text/plain":["<IPython.core.display.HTML object>"],"text/html":["<div><iframe src=\"https://6670bd5460b50ebcdb.gradio.live\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"]},"metadata":{}},{"output_type":"stream","name":"stderr","text":["WARNING:tornado.access:404 POST /v1beta/models/gemini-pro:generateContent?%24alt=json%3Benum-encoding%3Dint (127.0.0.1) 837.64ms\n","WARNING:tornado.access:404 POST /v1beta/models/gemini-pro:generateContent?%24alt=json%3Benum-encoding%3Dint (127.0.0.1) 710.23ms\n","WARNING:tornado.access:404 POST /v1beta/models/gemini-pro:generateContent?%24alt=json%3Benum-encoding%3Dint (127.0.0.1) 910.77ms\n"]},{"output_type":"stream","name":"stdout","text":["Keyboard interruption in main thread... closing server.\n","Killing tunnel 0.0.0.0:7860 <> https://6670bd5460b50ebcdb.gradio.live\n"]}]}]}