Unstop_Retail / admin_ui.py
Dasdeman's picture
Upload 26 files
a96d4f1 verified
import os, time, shutil
import gradio as gr
from utils import get_models_data, save_config, get_contacts, save_contacts
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
def build_admin_app():
initial_models = get_models_data(BASE_DIR)
with gr.Blocks(title="Unstop Admin", analytics_enabled=False) as app_admin:
with gr.Tabs():
# ── ВКЛ 1: ТОВАРИ ──────────────────────────────────────────────
with gr.Tab("🛒 Товари"):
with gr.Row():
model_selector = gr.Dropdown(
choices=[m['name'] for m in initial_models],
label="Оберіть товар для редагування",
value=initial_models[0]['name'] if initial_models else None,
scale=4
)
add_btn = gr.Button("➕ Створити новий", variant="primary", scale=1)
del_btn = gr.Button("🗑 Видалити товар", variant="stop", scale=1)
status_msg = gr.Markdown("")
with gr.Group():
gr.Markdown("## 🗂 Структура картки товару")
with gr.Row():
with gr.Column(scale=1, variant="panel"):
gr.Markdown("### 🖼 Фотографії")
m_folder = gr.Textbox(label="Назва папки (у /models/)", info="Порожнє = автостворення")
m_images = gr.File(label="Завантажити нові фото", file_count="multiple", type="filepath")
with gr.Row():
upload_btn = gr.Button("✅ Завантажити")
clear_img_btn = gr.Button("🗑 Очистити папку")
with gr.Column(scale=1, variant="panel"):
gr.Markdown("### 📄 Основна інформація")
m_name = gr.Textbox(label="Назва (вкладка)", placeholder="Unstop Retail 4032W")
m_price = gr.Textbox(label="💵 Ціна", placeholder="49 000 грн")
m_title = gr.Textbox(label="⚡ Заголовок H1")
m_subtitle = gr.Textbox(label="🔋 Підзаголовок")
with gr.Row():
with gr.Column(variant="panel"):
gr.Markdown("### 📝 Детальний опис (Markdown)")
m_opis = gr.Textbox(label="Текст опису", lines=12, show_label=False)
with gr.Row():
save_btn = gr.Button("💾 ЗБЕРЕГТИ ЗМІНИ ТОВАРУ", variant="primary", size="lg")
# ── ВКЛ 2: НАЛАШТУВАННЯ САЙТУ ──────────────────────────────────
with gr.Tab("⚙️ Налаштування сайту"):
gr.Markdown("## 📞 Глобальні контакти")
_c = get_contacts(BASE_DIR)
c_phone = gr.Textbox(label="Телефон (для tel:)", value=_c.get("phone", ""), placeholder="+380675745662")
c_phone_display = gr.Textbox(label="Телефон (відображення)", value=_c.get("phone_display", ""), placeholder="+38 067 574 56 62")
c_tg_link = gr.Textbox(label="Telegram посилання", value=_c.get("tg_link", ""), placeholder="https://t.me/...")
c_tg_display = gr.Textbox(label="Telegram підпис кнопки", value=_c.get("tg_display", ""), placeholder="Написати в Telegram")
c_address = gr.Textbox(label="Адреса (HTML дозволено)", value=_c.get("address", ""), placeholder="м. Харків, ...")
c_map_src = gr.Textbox(label="Src для <iframe> карти", value=_c.get("map_iframe", ""), lines=2)
contacts_status = gr.Markdown("")
save_contacts_btn = gr.Button("💾 ЗБЕРЕГТИ КОНТАКТИ", variant="primary", size="lg")
# ── ОБРОБНИКИ: ТОВАРИ ──────────────────────────────────────────────
def load_model_data(sel_name):
if not sel_name:
return "", "", "", "", "", "", ""
for m in get_models_data(BASE_DIR):
if m['name'] == sel_name:
return m['name'], m['price'], m['title'], m['subtitle'], m.get('folder', ''), m['opis_raw'], ""
return "", "", "", "", "", "", ""
model_selector.change(
load_model_data, inputs=model_selector,
outputs=[m_name, m_price, m_title, m_subtitle, m_folder, m_opis, status_msg]
)
def save_model_data(sel_name, name, price, title, sub, folder, opis):
if not sel_name:
return gr.update(), "⚠️ Немає моделі для збереження"
models = get_models_data(BASE_DIR)
for m in models:
if m['name'] == sel_name:
m['name'], m['price'], m['title'], m['subtitle'], m['folder'], m['opis_raw'] = \
name, price, title, sub, folder, opis
break
save_config(BASE_DIR, models)
return gr.update(choices=[x['name'] for x in models], value=name), "✅ Збережено! Оновіть вітрину (F5)."
save_btn.click(
save_model_data,
inputs=[model_selector, m_name, m_price, m_title, m_subtitle, m_folder, m_opis],
outputs=[model_selector, status_msg]
)
def add_new_model():
models = get_models_data(BASE_DIR)
new_name = f"Новий товар {len(models) + 1}"
models.append({"id": f"m_{int(time.time())}", "folder": "", "name": new_name,
"title": "Новий заголовок", "subtitle": "Короткий опис",
"price": "0 грн", "opis_raw": ""})
save_config(BASE_DIR, models)
return gr.update(choices=[x['name'] for x in models], value=new_name), f"✅ Додано «{new_name}»!"
add_btn.click(add_new_model, outputs=[model_selector, status_msg])
def delete_selected_model(sel_name):
if not sel_name:
return gr.update(), "⚠️ Не обрано товар"
models = [m for m in get_models_data(BASE_DIR) if m['name'] != sel_name]
save_config(BASE_DIR, models)
new_sel = models[0]['name'] if models else None
return gr.update(choices=[x['name'] for x in models], value=new_sel), f"🗑 «{sel_name}» видалено!"
del_btn.click(delete_selected_model, inputs=[model_selector], outputs=[model_selector, status_msg])
def handle_upload(sel_name, current_folder, files):
if not files:
return current_folder, "⚠️ Виберіть файли."
target_folder = current_folder.strip() or "".join(c if c.isalnum() else "_" for c in sel_name)
target_dir = os.path.join(BASE_DIR, "models", target_folder)
os.makedirs(target_dir, exist_ok=True)
count = 0
for f in files:
shutil.copy(f, os.path.join(target_dir, os.path.basename(f)))
count += 1
models = get_models_data(BASE_DIR)
for m in models:
if m['name'] == sel_name:
m['folder'] = target_folder
save_config(BASE_DIR, models)
return gr.update(value=target_folder), f"✅ Завантажено {count} фото у '{target_folder}'!"
upload_btn.click(handle_upload, inputs=[model_selector, m_folder, m_images], outputs=[m_folder, status_msg])
def handle_clear_images(current_folder):
target_folder = current_folder.strip()
if not target_folder:
return "⚠️ Папка не вказана."
target_dir = os.path.join(BASE_DIR, "models", target_folder)
count = 0
if os.path.exists(target_dir):
for f in os.listdir(target_dir):
if f.lower().endswith(('.webp', '.png', '.jpg', '.jpeg')):
try:
os.remove(os.path.join(target_dir, f)); count += 1
except Exception:
pass
return f"🗑 Видалено {count} фото!"
clear_img_btn.click(handle_clear_images, inputs=[m_folder], outputs=[status_msg])
# ── ОБРОБНИКИ: КОНТАКТИ ────────────────────────────────────────────
def do_save_contacts(phone, phone_display, tg_link, tg_display, address, map_src):
save_contacts(BASE_DIR, {
"phone": phone.strip(),
"phone_display": phone_display.strip(),
"tg_link": tg_link.strip(),
"tg_display": tg_display.strip(),
"address": address.strip(),
"map_iframe": map_src.strip(),
})
return "✅ Контакти збережено! Оновіть вітрину (F5)."
save_contacts_btn.click(
do_save_contacts,
inputs=[c_phone, c_phone_display, c_tg_link, c_tg_display, c_address, c_map_src],
outputs=[contacts_status]
)
return app_admin