Spaces:
Running
Running
File size: 10,140 Bytes
f7b9253 a96d4f1 f7b9253 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | 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
|