import { useState, useEffect, useRef } from "react"; import styles from "./Adminpage.module.scss"; // ─── PASSWORD — change this ─────────────────────────────────────────────────── const ADMIN_PASSWORD = "shumoff2024"; // ─── LOGIN ──────────────────────────────────────────────────────────────────── function LoginScreen({ onLogin }) { const [input, setInput] = useState(""); const [error, setError] = useState(false); const [shake, setShake] = useState(false); function handleSubmit(e) { e.preventDefault(); if (input === ADMIN_PASSWORD) { onLogin(); } else { setError(true); setShake(true); setTimeout(() => setShake(false), 500); } } return (
⚙️

Панель администратора

Введите пароль для доступа

{ setInput(e.target.value); setError(false); }} autoFocus /> {error &&

Неверный пароль. Попробуйте ещё раз.

}
); } // ─── TABS ───────────────────────────────────────────────────────────────────── const ADMIN_TABS = [ { id: "products", label: "📦 Товары и цены" }, { id: "bodytypes", label: "🚗 Типы кузова" }, ]; // ─── ADMIN PANEL ───────────────────────────────────────────────────────────── function AdminPanel({ onLogout }) { const [data, setData] = useState(null); const [tab, setTab] = useState("products"); const [zone, setZone] = useState(null); const [bodyType, setBodyType] = useState(null); const [pkg, setPkg] = useState(null); const [saved, setSaved] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Load data.json from /public useEffect(() => { fetch("/frontend-api/data") .then((r) => r.json()) .then((d) => { setData(d); setZone(Object.keys(d.zones)[0]); setBodyType(d.bodyTypes[0]?.id); setPkg(d.packages[0]); setLoading(false); }) .catch((e) => { setError(e.message); setLoading(false); }); }, []); // ── helpers ──────────────────────────────────────────────── function updateData(fn) { setData((prev) => { const next = JSON.parse(JSON.stringify(prev)); fn(next); return next; }); } // Products tab function getProducts() { return data?.zones?.[zone]?.products?.[bodyType]?.[pkg] || []; } function setProducts(products) { updateData((d) => { if (!d.zones[zone].products[bodyType]) d.zones[zone].products[bodyType] = {}; d.zones[zone].products[bodyType][pkg] = products; }); } function updateProduct(idx, field, val) { const ps = JSON.parse(JSON.stringify(getProducts())); if (field === "price" || field === "qty") ps[idx][field] = Number(val) || 0; else ps[idx][field] = val; setProducts(ps); } function deleteProduct(idx) { const ps = JSON.parse(JSON.stringify(getProducts())); ps.splice(idx, 1); setProducts(ps); } function addProduct() { const ps = JSON.parse(JSON.stringify(getProducts())); ps.push({ name: "Новый товар", price: 0, qty: 1, unit: "Л" }); setProducts(ps); } // Body types tab function updateBodyType(idx, field, val) { updateData((d) => { d.bodyTypes[idx][field] = val; }); } function deleteBodyType(idx) { const bt = data.bodyTypes[idx]; updateData((d) => { d.bodyTypes.splice(idx, 1); // remove products for this body type in all zones Object.values(d.zones).forEach((z) => { delete z.products[bt.id]; }); }); if (bodyType === bt.id) setBodyType(data.bodyTypes[0]?.id); } function addBodyType() { const newId = `body_${Date.now()}`; updateData((d) => { d.bodyTypes.push({ id: newId, label: "Новый тип", icon: "🚗" }); }); } // Zone label function updateZoneLabel(val) { updateData((d) => { d.zones[zone].label = val; }); } function handleImageUpload(e, idx) { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { updateBodyType(idx, "image", event.target.result); updateBodyType(idx, "icon", null); // Eski icon verisini temizle }; reader.readAsDataURL(file); } // ── Save = download updated data.json ────────────────────── // Since there's no backend, admin downloads the JSON and replaces public/data.json async function handleSave() { const res = await fetch("/frontend-api/data", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); const result = await res.json(); if (!result.ok) { alert("Hata: " + result.error); return; } setSaved(true); setTimeout(() => setSaved(false), 3000); } // ── Render ───────────────────────────────────────────────── if (loading) return
Загрузка...
; if (error) return
Ошибка: {error}
; const products = getProducts(); return (
{/* Header */}
⚙️

Панель администратора

Управление товарами, ценами и типами кузова

{saved && ( ✓ Сохранено )}
{/* Tab bar */}
{ADMIN_TABS.map((t) => ( ))}
{/* ── PRODUCTS TAB ─────────────────────────────────────── */} {tab === "products" && (
{/* Sidebar: zones */} {/* Main content */}
{/* Zone label */}
updateZoneLabel(e.target.value)} />
{/* Body type selector */}
Тип кузова
{data.bodyTypes.map((b) => ( ))}
{/* Package tabs */}
{data.packages.map((p) => ( ))}
{/* Products table */}
{products.map((p, i) => ( ))} {products.length === 0 && ( )}
Название товара Цена (m) Кол-во Ед. Итого
updateProduct(i, "name", e.target.value)} /> updateProduct(i, "price", e.target.value)} /> updateProduct(i, "qty", e.target.value)} /> updateProduct(i, "unit", e.target.value)} /> {(p.price * p.qty).toLocaleString("ru")} m
Нет товаров для этой комбинации
)} {/* ── BODY TYPES TAB ───────────────────────────────────── */} {tab === "bodytypes" && (

Типы кузова

Добавляйте, удаляйте и редактируйте типы кузова. Изменения применяются ко всем зонам и пакетам.

{data.bodyTypes.map((b, i) => (
{b.image ? ( Preview ) : ( b.icon && {b.icon} )}
updateBodyType(i, "label", e.target.value)} placeholder="Название типа" />
))} {/* Add new */}

После всех изменений нажмите «Сохранить» в шапке.

)} {/* Floating save */}
); } // ─── PAGE EXPORT ───────────────────────────────────────────────────────────── export default function AdminPage() { const [authed, setAuthed] = useState(false); return authed ? setAuthed(false)} /> : setAuthed(true)} />; }