const { useState, useMemo, useRef, useEffect } = React;

// ===== связь с бэкендом: авторизация, загрузка при старте, автосохранение =====
const API = (name) => `/api/${name}`;
let TOKEN = (typeof localStorage !== "undefined" && localStorage.getItem("sensora_token")) || null;
let ON_AUTH_FAIL = () => {};
function authFetch(url, opts = {}) {
  const headers = Object.assign({}, opts.headers, TOKEN ? { Authorization: "Bearer " + TOKEN } : {});
  return fetch(url, Object.assign({}, opts, { headers })).then((r) => { if (r.status === 401) ON_AUTH_FAIL(); return r; });
}
function usePersistentCollection(name, seed) {
  const [data, setData] = useState(seed);
  const ready = useRef(false);
  useEffect(() => {
    let on = true;
    authFetch(API(name)).then((r) => (r.ok ? r.json() : seed)).then((d) => { if (on) { setData(Array.isArray(d) ? d : seed); ready.current = true; } }).catch(() => { ready.current = true; });
    return () => { on = false; };
  }, []);
  useEffect(() => {
    if (!ready.current) return;
    const t = setTimeout(() => { authFetch(API(name), { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }).catch(() => {}); }, 400);
    return () => clearTimeout(t);
  }, [data]);
  return [data, setData];
}
function usePersistentObject(name, seed) {
  const [data, setData] = useState(seed);
  const ready = useRef(false);
  useEffect(() => {
    let on = true;
    authFetch(API(name)).then((r) => (r.ok ? r.json() : seed)).then((d) => { if (on) { setData(d && typeof d === "object" ? d : seed); ready.current = true; } }).catch(() => { ready.current = true; });
    return () => { on = false; };
  }, []);
  useEffect(() => {
    if (!ready.current) return;
    const t = setTimeout(() => { authFetch(API(name), { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }).catch(() => {}); }, 400);
    return () => clearTimeout(t);
  }, [data]);
  return [data, setData];
}
const ROLE_LABEL = { owner: "Владелец", admin: "Администратор", sales: "Продажи", supply: "Снабжение", accountant: "Бухгалтерия" };
const ROLES = [["owner", "Владелец"], ["admin", "Администратор"], ["sales", "Продажи"], ["supply", "Снабжение"], ["accountant", "Бухгалтерия"]];

const C = {
  bg: "#f4f5f7", ink: "#172b4d", sub: "#5e6c84", line: "#dfe1e6", white: "#fff",
  blue: "#0079bf", green: "#61bd4f", yellow: "#f2d600", orange: "#ff9f1a",
  red: "#eb5a46", purple: "#c377e0", teal: "#00c2e0", darkgreen: "#519839",
};
const nf0 = new Intl.NumberFormat("ru-RU");
const nfU = new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 2 });
const nfM = new Intl.NumberFormat("ru-RU", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const money = (n) => nfM.format(n || 0);
const fmt = (n) => nf0.format(Math.round(n || 0));
const short = (n) => { const a = Math.abs(n || 0); if (a >= 1e9) return (n / 1e9).toFixed(2) + " млрд"; if (a >= 1e6) return (n / 1e6).toFixed(2) + " млн"; if (a >= 1e3) return (n / 1e3).toFixed(0) + " тыс"; return fmt(n); };

const TODAY = new Date(2026, 5, 6);
const TODAYS = "2026-06-06";
const ymd = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
const parse = (s) => { const [y, m, d] = s.split("-").map(Number); return new Date(y, m - 1, d); };
const monthsBetween = (s, b = TODAY) => (b - parse(s)) / (1000 * 60 * 60 * 24 * 30.44);
const daysUntil = (s) => s ? Math.round((parse(s) - TODAY) / 86400000) : null;
const dshort = (s) => s ? parse(s).toLocaleDateString("ru-RU", { day: "2-digit", month: "2-digit", year: "2-digit" }) : "—";
const monLabel = (d) => d.toLocaleDateString("ru-RU", { month: "long", year: "numeric" });
const expired = (s) => s && parse(s) < TODAY;
const dateColor = (s) => { const d = daysUntil(s); if (d == null) return C.sub; if (d < 0) return C.red; if (d <= 45) return C.orange; return C.ink; };
const WD = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"];

const STAGES = [
  { id: "preorder", t: "Пред-приход", c: C.sub }, { id: "paid", t: "Оплачен", c: C.teal },
  { id: "arrived", t: "Приход", c: C.blue }, { id: "customs", t: "Растаможка", c: C.orange },
  { id: "cert", t: "Сертификация", c: C.purple }, { id: "ready", t: "На складе", c: C.green },
  { id: "sold", t: "Продан", c: C.darkgreen },
];
const SEED_PRODUCTS = [
  { sku: "BT04", name: "Датчик температуры TZ-BT04", listPrice: 820000, serviceFee: 120000 },
  { sku: "RH10", name: "Логгер влажности TZ-RH10", listPrice: 1050000, serviceFee: 150000 },
  { sku: "G1", name: "Шлюз LoRa Gateway TZ-G1", listPrice: 1150000, serviceFee: 250000 },
];
let CATALOG = SEED_PRODUCTS; // живой каталог из базы; синхронизируется в ERP
const prod = (s) => CATALOG.find((p) => p.sku === s);
const skuName = (s) => prod(s)?.name || s;
const DOC_KINDS = ["Договор", "Приказ", "Доверенность", "Сертификат", "Прочее"];
const STATUS = { delivered: { l: "Доставлено", c: C.darkgreen }, presale: { l: "В воздухе", c: C.orange }, prepaid: { l: "Предоплата", c: C.blue } };

const seedArrivals = [
  { id: 1, title: "TZ-09/2025", supplier: "Tzone Digital", stage: "ready", items: [{ sku: "BT04", qty: 30, unitUSD: 42 }, { sku: "RH10", qty: 15, unitUSD: 58 }, { sku: "G1", qty: 5, unitUSD: 103 }], costs: { supplier: { usd: 2645, rate: 12450, date: "2025-09-19" }, customs: { amount: 5791200.50, date: "2025-10-08" }, cert: { amount: 3800000, date: "2025-10-18" }, declarant: { amount: 2200000, date: "2026-06-15" } } },
  { id: 2, title: "TZ-05/2026", supplier: "Tzone Digital", stage: "customs", items: [{ sku: "BT04", qty: 50, unitUSD: 40 }, { sku: "RH10", qty: 20, unitUSD: 56 }], costs: { supplier: { usd: 3120, rate: 12575, date: "2026-05-20" }, customs: { amount: 4100000, date: "2026-06-20" }, cert: { amount: 0, date: "" }, declarant: { amount: 1900000, date: "2026-06-28" } } },
  { id: 3, title: "TZ-02/2025", supplier: "Tzone Digital", stage: "sold", items: [{ sku: "RH10", qty: 12, unitUSD: 55 }], costs: { supplier: { usd: 660, rate: 12320, date: "2025-02-10" }, customs: { amount: 1450000, date: "2025-02-25" }, cert: { amount: 2900000, date: "2025-03-04" }, declarant: { amount: 1800000, date: "2025-03-10" } } },
];
const seedClients = [
  { id: 1, name: "Фарм-Склад №1", early: true, inn: "301234567", address: "Ташкент, Чиланзар, ул. Бунёдкор 12", phone: "+998 71 200-30-40", email: "info@farmsklad.uz", director: "Каримов А. А.", directorOrderNo: "01-К", directorOrderDate: "2023-01-10", directorOrderValid: "2028-01-10", contractNo: "ДГ-2025/14", contractDate: "2025-01-15", contractValid: "2026-12-31", contactPerson: "Рустам (снабжение)", contactPhone: "+998 90 123-45-67", personalDiscount: 5, personalMarkup: 0, notes: "Платят вовремя, день оплаты — пятница.", documents: [{ id: 1, name: "Договор ДГ-2025/14", kind: "Договор" }, { id: 2, name: "Приказ 01-К", kind: "Приказ" }], branches: [{ id: 1, name: "Филиал Чиланзар", address: "ул. Бунёдкор 12, склад А", contactPerson: "Рустам", contactPhone: "+998 90 123-45-67" }, { id: 2, name: "Филиал Юнусабад", address: "ул. Амира Темура 108", contactPerson: "Дилноза", contactPhone: "+998 93 555-22-11" }] },
  { id: 2, name: "MedLogistic", early: true, inn: "302998877", address: "Ташкент, Мирзо-Улугбек, ул. Буюк Ипак Йули 5", phone: "+998 71 233-11-22", email: "office@medlogistic.uz", director: "Ахмедова Н. Р.", directorOrderNo: "07", directorOrderDate: "2024-03-01", directorOrderValid: "2027-03-01", contractNo: "ДГ-2025/31", contractDate: "2025-03-20", contractValid: "2026-03-20", contactPerson: "Жасур", contactPhone: "+998 97 700-80-90", personalDiscount: 0, personalMarkup: 0, notes: "Растущий клиент, планируют филиал в Самарканде.", documents: [{ id: 1, name: "Договор ДГ-2025/31", kind: "Договор" }], branches: [{ id: 1, name: "Центральный склад", address: "ул. Буюк Ипак Йули 5", contactPerson: "Жасур", contactPhone: "+998 97 700-80-90" }] },
  { id: 3, name: "ColdChain UZ", early: false, inn: "303445566", address: "Ташкентская обл., Зангиата, склад. комплекс 3", phone: "+998 70 144-55-66", email: "info@coldchain.uz", director: "Эргашев Б. Т.", directorOrderNo: "12-П", directorOrderDate: "2022-06-15", directorOrderValid: "2025-06-15", contractNo: "ДГ-2026/02", contractDate: "2026-01-10", contractValid: "2027-01-10", contactPerson: "Шерзод", contactPhone: "+998 99 321-00-11", personalDiscount: 0, personalMarkup: 3, notes: "Часто платят заранее, до прихода в город.", documents: [{ id: 1, name: "Договор ДГ-2026/02", kind: "Договор" }], branches: [{ id: 1, name: "Склад Зангиата", address: "складской комплекс 3", contactPerson: "Шерзод", contactPhone: "+998 99 321-00-11" }] },
];
const seedOrders = [
  { id: 1, clientId: 1, branchId: 1, date: "2026-02-10", status: "delivered", discountPct: 5, paid: 8075000, note: "", prepaid: null, items: [{ sku: "BT04", qty: 10, price: 850000 }] },
  { id: 2, clientId: 3, branchId: 1, date: "2026-03-01", status: "delivered", discountPct: 0, paid: 4000000, note: "", prepaid: null, items: [{ sku: "RH10", qty: 6, price: 1080000 }] },
  { id: 3, clientId: 1, branchId: 2, date: "2026-04-05", status: "delivered", discountPct: 5, paid: 3990000, note: "", prepaid: null, items: [{ sku: "RH10", qty: 4, price: 1050000 }] },
  { id: 4, clientId: 1, branchId: 1, date: "2025-04-01", status: "delivered", discountPct: 0, paid: 12600000, note: "", prepaid: null, items: [{ sku: "RH10", qty: 12, price: 1050000 }] },
  { id: 5, clientId: 2, branchId: 1, date: "2025-11-15", status: "delivered", discountPct: 0, paid: 3000000, note: "", prepaid: null, items: [{ sku: "G1", qty: 3, price: 1150000 }] },
  { id: 6, clientId: 2, branchId: null, date: "2026-05-25", status: "presale", discountPct: 0, paid: 0, note: "Ждём партию TZ-05/2026, едет из Гонконга", prepaid: null, items: [{ sku: "BT04", qty: 20, price: 820000 }, { sku: "RH10", qty: 10, price: 1050000 }] },
  { id: 7, clientId: 3, branchId: 1, date: "2026-06-01", status: "prepaid", discountPct: 0, paid: 4400000, note: "Оплачено заранее, доставка после растаможки", prepaid: { amount: 4400000, date: "2026-06-01" }, items: [{ sku: "G1", qty: 4, price: 1100000 }] },
];
const seedEquipment = [
  { id: 1, serial: "RH10-0001", eui: "70E476FFFE001201", lora: "26011A01", sku: "RH10", clientId: 1, branchId: 1, install: "2025-04-01", poverka: "2026-08-15", validation: "2026-07-20", tariff: 150000, status: "online", lastSeen: "5 мин назад", freeUntil: "2025-10-01" },
  { id: 2, serial: "RH10-0002", eui: "70E476FFFE001202", lora: "26011A02", sku: "RH10", clientId: 1, branchId: 2, install: "2026-04-05", poverka: "2027-04-05", validation: "2026-06-20", tariff: 150000, status: "online", lastSeen: "12 мин назад", freeUntil: "2026-10-05" },
  { id: 3, serial: "BT04-0001", eui: "70E476FFFE001210", lora: "26011B10", sku: "BT04", clientId: 1, branchId: 1, install: "2026-02-10", poverka: "2026-06-25", validation: "2026-08-10", tariff: 120000, status: "offline", lastSeen: "2 дня назад", freeUntil: "2026-08-10" },
  { id: 4, serial: "BT04-0002", eui: "70E476FFFE001211", lora: "26011B11", sku: "BT04", clientId: 1, branchId: 1, install: "2026-02-10", poverka: "2026-07-01", validation: "2026-08-10", tariff: 120000, status: "online", lastSeen: "3 мин назад", freeUntil: "2026-08-10" },
  { id: 5, serial: "G1-0001", eui: "70E476FFFE0012F0", lora: "GATE-01", sku: "G1", clientId: 2, branchId: 1, install: "2025-11-15", poverka: "2026-11-15", validation: "2026-09-01", tariff: 250000, status: "online", lastSeen: "1 мин назад", freeUntil: "2026-05-15" },
  { id: 6, serial: "RH10-0010", eui: "70E476FFFE001220", lora: "26011A20", sku: "RH10", clientId: 3, branchId: 1, install: "2026-03-01", poverka: "2026-09-10", validation: "2026-10-01", tariff: 150000, status: "online", lastSeen: "8 мин назад", freeUntil: "" },
  { id: 7, serial: "G1-0002", eui: "70E476FFFE0012F1", lora: "GATE-02", sku: "G1", clientId: 3, branchId: 1, install: "2026-03-01", poverka: "2026-09-10", validation: "2026-10-01", tariff: 250000, status: "online", lastSeen: "8 мин назад", freeUntil: "" },
  { id: 8, serial: "RH10-0011", eui: "70E476FFFE001221", lora: "26011A21", sku: "RH10", clientId: 1, branchId: 1, install: "2025-04-01", poverka: "2026-07-10", validation: "2026-07-20", tariff: 150000, status: "online", lastSeen: "6 мин назад", freeUntil: "2025-10-01" },
];

const orderSub = (o) => o.items.reduce((s, i) => s + i.qty * i.price, 0);
const orderTotal = (o) => orderSub(o) * (1 - (o.discountPct || 0) / 100);

function ERP({ me, onLogout }) {
  const [tab, setTab] = useState("dash");
  const [arrivals, setArrivals] = usePersistentCollection("arrivals", seedArrivals);
  const [clients, setClients] = usePersistentCollection("clients", seedClients);
  const [orders, setOrders] = usePersistentCollection("orders", seedOrders);
  const [equipment, setEquipment] = usePersistentCollection("equipment", seedEquipment);
  const [recurring, setRecurring] = usePersistentObject("settings", { rent: 6500000, salaries: 14000000 });
  const [planned, setPlanned] = usePersistentCollection("planned", [{ id: 1, date: "2026-06-25", label: "Аванс за TZ-07/2026", amount: 15000000, type: "expense" }]);
  const [products, setProducts] = usePersistentCollection("products", SEED_PRODUCTS);
  CATALOG = products;

  const arr = useMemo(() => arrivals.map((a) => {
    const extras = a.costs.customs.amount + a.costs.cert.amount + a.costs.declarant.amount;
    const baseTotal = a.items.reduce((s, i) => s + i.qty * i.unitUSD * a.costs.supplier.rate, 0);
    const items = a.items.map((i) => { const base = i.qty * i.unitUSD * a.costs.supplier.rate; const share = baseTotal > 0 ? base / baseTotal : 0; const landed = base + extras * share; return { ...i, base, landed, unit: i.qty ? landed / i.qty : 0 }; });
    return { ...a, items, total: items.reduce((s, i) => s + i.landed, 0) };
  }), [arrivals]);

  const warehouse = useMemo(() => products.map((p) => {
    let received = 0, costSum = 0;
    arr.forEach((a) => a.items.forEach((i) => { if (i.sku === p.sku) { received += i.qty; costSum += i.landed; } }));
    const sold = orders.filter((o) => o.status === "delivered").flatMap((o) => o.items).filter((i) => i.sku === p.sku).reduce((s, i) => s + i.qty, 0);
    const reserved = orders.filter((o) => o.status !== "delivered").flatMap((o) => o.items).filter((i) => i.sku === p.sku).reduce((s, i) => s + i.qty, 0);
    const onHand = received - sold; const w = received ? costSum / received : 0;
    return { ...p, received, sold, reserved, onHand, free: onHand - reserved, wac: w, value: onHand * w };
  }), [arr, orders, products]);
  const stockValue = warehouse.reduce((s, w) => s + w.value, 0);
  const wac = (sku) => warehouse.find((w) => w.sku === sku)?.wac || 0;

  const mrrAt = (date) => equipment.filter((e) => e.status !== "decom" && parse(e.install) <= date && (!e.freeUntil || parse(e.freeUntil) <= date)).reduce((s, e) => s + e.tariff, 0);

  const allEvents = useMemo(() => {
    const ev = []; const add = (date, label, amount, type, kind) => { if (date && amount) ev.push({ date, label, amount, type, kind, planned: parse(date) > TODAY }); };
    arr.forEach((a) => { add(a.costs.supplier.date, `Поставщик · ${a.title}`, a.costs.supplier.usd * a.costs.supplier.rate, "expense", "supplier"); add(a.costs.customs.date, `Растаможка · ${a.title}`, a.costs.customs.amount, "expense", "customs"); add(a.costs.cert.date, `Сертификация · ${a.title}`, a.costs.cert.amount, "expense", "cert"); add(a.costs.declarant.date, `Декларант · ${a.title}`, a.costs.declarant.amount, "expense", "declarant"); });
    orders.forEach((o) => { const cl = clients.find((c) => c.id === o.clientId); if (o.prepaid) add(o.prepaid.date, `Предоплата · ${cl?.name || ""}`, o.prepaid.amount, "income", "prepaid"); if (o.status === "delivered") { const rem = orderTotal(o) - (o.prepaid?.amount || 0); if (rem > 0) add(o.date, `Продажа · ${cl?.name || ""}`, rem, "income", "sale"); } });
    planned.forEach((p) => ev.push({ ...p, kind: "planned", planned: parse(p.date) > TODAY }));
    for (let y = 2025; y <= 2026; y++) for (let m = 0; m < 12; m++) { add(ymd(new Date(y, m, 1)), "Аренда", recurring.rent, "expense", "rent"); add(ymd(new Date(y, m, 5)), "Зарплаты", recurring.salaries, "expense", "salary"); add(ymd(new Date(y, m, 5)), "Абонплата (обслуживание)", mrrAt(new Date(y, m, 5)), "income", "service"); }
    return ev.sort((a, b) => parse(a.date) - parse(b.date));
  }, [arr, orders, clients, planned, recurring, equipment]);

  const sumR = (from, to, type) => allEvents.filter((e) => e.type === type && parse(e.date) >= from && parse(e.date) <= to).reduce((s, e) => s + e.amount, 0);
  const mS = new Date(TODAY.getFullYear(), TODAY.getMonth(), 1), mE = new Date(TODAY.getFullYear(), TODAY.getMonth() + 1, 0);

  // ===== Финансы: дебиторка / кредиторка / P&L =====
  const finance = useMemo(() => {
    const receivables = orders.filter((o) => o.status === "delivered" && orderTotal(o) - o.paid > 0.5).map((o) => { const cl = clients.find((c) => c.id === o.clientId); const due = new Date(parse(o.date)); due.setDate(due.getDate() + 14); const overdue = Math.max(0, Math.round((TODAY - due) / 86400000)); return { id: o.id, client: cl?.name, debt: orderTotal(o) - o.paid, total: orderTotal(o), date: o.date, overdue }; });
    const payables = []; arr.forEach((a) => { const c = a.costs; const ck = (g, label) => { if (c[g].amount > 0 && (!c[g].date || parse(c[g].date) > TODAY)) payables.push({ supplier: a.title, label, amount: c[g].amount, due: c[g].date }); }; ck("customs", "Растаможка"); ck("cert", "Сертификация"); ck("declarant", "Декларант"); });
    const pnl = []; for (let i = 5; i >= 0; i--) { const d = new Date(TODAY.getFullYear(), TODAY.getMonth() - i, 1); const mEnd = new Date(d.getFullYear(), d.getMonth() + 1, 0); const mo = orders.filter((o) => o.status === "delivered" && parse(o.date) >= d && parse(o.date) <= mEnd); const goods = mo.reduce((s, o) => s + orderTotal(o), 0); const service = mrrAt(mEnd); const revenue = goods + service; const cogs = mo.reduce((s, o) => s + o.items.reduce((a, i) => a + i.qty * wac(i.sku), 0), 0); const other = planned.filter((p) => p.type === "expense" && parse(p.date) >= d && parse(p.date) <= mEnd).reduce((s, p) => s + p.amount, 0); const gross = revenue - cogs; const net = gross - recurring.salaries - recurring.rent - other; pnl.push({ d, revenue, cogs, gross, salaries: recurring.salaries, rent: recurring.rent, other, net }); }
    return { receivables, payables, pnl, totalRec: receivables.reduce((s, r) => s + r.debt, 0), totalPay: payables.reduce((s, p) => s + p.amount, 0) };
  }, [orders, clients, arr, recurring, planned, equipment]);

  // ===== Подписки / оборудование =====
  const subs = useMemo(() => {
    const active = equipment.filter((e) => e.status !== "decom");
    const mrr = mrrAt(TODAY); const free = equipment.filter((e) => e.freeUntil && parse(e.freeUntil) > TODAY).length;
    const newM = equipment.filter((e) => { const d = parse(e.install); return d.getFullYear() === TODAY.getFullYear() && d.getMonth() === TODAY.getMonth(); }).length;
    const offline = equipment.filter((e) => e.status === "offline").length;
    return { activeN: active.length, mrr, arr: mrr * 12, free, newM, offline };
  }, [equipment]);

  // ===== Контрольные события =====
  const controlEvents = useMemo(() => {
    const ev = [];
    equipment.forEach((e) => { const dp = daysUntil(e.poverka); if (dp != null && dp <= 45) ev.push({ date: e.poverka, label: `Поверка: ${e.serial}`, sev: dp < 0 ? "red" : "orange" }); const dv = daysUntil(e.validation); if (dv != null && dv <= 45) ev.push({ date: e.validation, label: `Валидация: ${e.serial}`, sev: dv < 0 ? "red" : "orange" }); if (e.freeUntil) { const df = daysUntil(e.freeUntil); if (df != null && df >= 0 && df <= 45) ev.push({ date: e.freeUntil, label: `Конец акции: ${e.serial}`, sev: "orange" }); } });
    clients.forEach((c) => { if (c.directorOrderValid && daysUntil(c.directorOrderValid) <= 45) ev.push({ date: c.directorOrderValid, label: `Приказ директора: ${c.name}`, sev: expired(c.directorOrderValid) ? "red" : "orange" }); if (c.contractValid && daysUntil(c.contractValid) <= 45) ev.push({ date: c.contractValid, label: `Договор: ${c.name}`, sev: expired(c.contractValid) ? "red" : "orange" }); });
    finance.receivables.forEach((r) => { if (r.overdue > 0) ev.push({ date: r.date, label: `Просрочка оплаты: ${r.client} (${short(r.debt)})`, sev: "red" }); });
    return ev.sort((a, b) => parse(a.date) - parse(b.date));
  }, [equipment, clients, finance]);

  const kpi = { in: sumR(mS, mE, "income"), out: sumR(mS, mE, "expense"), mrr: subs.mrr, inAir: orders.filter((o) => o.status !== "delivered").reduce((s, o) => s + orderTotal(o), 0) };

  const setArr = (id, fn) => setArrivals((cs) => cs.map((a) => (a.id === id ? fn(a) : a)));
  const move = (id, dir) => setArr(id, (a) => { const i = STAGES.findIndex((s) => s.id === a.stage); return { ...a, stage: STAGES[Math.min(6, Math.max(0, i + dir))].id }; });
  const addArrival = () => { const id = Date.now(); setArrivals((cs) => [{ id, title: "Новая партия", supplier: "", stage: "preorder", items: [], costs: { supplier: { usd: 0, rate: 0, date: "" }, customs: { amount: 0, date: "" }, cert: { amount: 0, date: "" }, declarant: { amount: 0, date: "" } } }, ...cs]); return id; };
  const delArrival = (id) => setArrivals((cs) => cs.filter((a) => a.id !== id));
  const setClient = (id, fn) => setClients((cs) => cs.map((c) => (c.id === id ? fn(c) : c)));
  const addOrder = (o) => setOrders((os) => [...os, { ...o, id: Date.now(), paid: o.prepaid ? o.prepaid.amount : 0 }]);
  const setOrderStatus = (id, status) => setOrders((os) => os.map((o) => (o.id === id ? { ...o, status } : o)));
  const delOrder = (id) => setOrders((os) => os.filter((o) => o.id !== id));
  const setEquip = (id, fn) => setEquipment((es) => es.map((e) => (e.id === id ? fn(e) : e)));

  const NAV = [["dash", "📊 Обзор"], ["arr", "📦 Приход"], ["wh", "🏬 Склад"], ["catalog", "🏷️ Товары"], ["clients", "👥 Клиенты"], ["sales", "🤝 Продажи"], ["equip", "📡 Оборудование"], ["fin", "💰 Финансы"], ["docs", "📑 Документы"], ["cal", "📅 Календарь"], ...(me && me.role === "owner" ? [["users", "👤 Пользователи"]] : [])];

  return (
    <div style={{ fontFamily: "'Segoe UI', system-ui, sans-serif", background: C.bg, minHeight: "100vh", color: C.ink, display: "flex" }}>
      <div style={{ width: 172, background: "#1d2b45", padding: "14px 10px", flexShrink: 0, minHeight: "100vh" }}>
        <div style={{ color: C.white, fontWeight: 800, fontSize: 15, padding: "4px 8px 16px" }}>⚙️ Sensora ERP</div>
        {NAV.map(([id, l]) => <button key={id} onClick={() => setTab(id)} style={{ display: "block", width: "100%", textAlign: "left", padding: "10px 12px", marginBottom: 4, borderRadius: 8, border: "none", cursor: "pointer", fontSize: 13.5, fontWeight: 600, background: tab === id ? "rgba(255,255,255,.16)" : "transparent", color: tab === id ? C.white : "rgba(255,255,255,.7)" }}>{l}</button>)}
        {me && <div style={{ marginTop: 16, paddingTop: 12, borderTop: "1px solid rgba(255,255,255,.12)" }}>
          <div style={{ color: C.white, fontSize: 12.5, fontWeight: 700, padding: "0 8px" }}>{me.name}</div>
          <div style={{ color: "rgba(255,255,255,.5)", fontSize: 11, padding: "0 8px 8px" }}>{ROLE_LABEL[me.role] || me.role}</div>
          <button onClick={onLogout} style={{ width: "100%", textAlign: "left", padding: "8px 12px", borderRadius: 8, border: "none", cursor: "pointer", fontSize: 12.5, fontWeight: 600, background: "transparent", color: "rgba(255,255,255,.7)" }}>⎋ Выйти</button>
        </div>}
      </div>
      <div style={{ flex: 1, padding: 18, overflowX: "hidden", maxWidth: "calc(100% - 172px)" }}>
        {tab === "dash" && <Dashboard kpi={kpi} stockValue={stockValue} warehouse={warehouse} events={allEvents} controlEvents={controlEvents} subs={subs} finance={finance} />}
        {tab === "arr" && <Kanban arr={arr} move={move} setArr={setArr} onAdd={addArrival} onDel={delArrival} />}
        {tab === "wh" && <Warehouse warehouse={warehouse} arr={arr} stockValue={stockValue} />}
        {tab === "catalog" && <Catalog products={products} setProducts={setProducts} />}
        {tab === "clients" && <Clients clients={clients} setClients={setClients} setClient={setClient} orders={orders} />}
        {tab === "sales" && <Sales clients={clients} warehouse={warehouse} orders={orders} addOrder={addOrder} setOrderStatus={setOrderStatus} delOrder={delOrder} wac={wac} mrr={subs.mrr} />}
        {tab === "equip" && <Equipment equipment={equipment} clients={clients} subs={subs} setEquip={setEquip} setEquipment={setEquipment} />}
        {tab === "fin" && <Finance finance={finance} />}
        {tab === "docs" && <Documents clients={clients} />}
        {tab === "cal" && <Calendar events={allEvents} planned={planned} setPlanned={setPlanned} recurring={recurring} setRecurring={setRecurring} />}
        {tab === "users" && me && me.role === "owner" && <Users />}
      </div>
    </div>
  );
}

// ===== ОБЗОР =====
function Dashboard({ kpi, stockValue, warehouse, events, controlEvents, subs, finance }) {
  const alerts = warehouse.filter((w) => w.free < 0 || w.onHand <= 6);
  const upcoming = events.filter((e) => parse(e.date) >= TODAY).slice(0, 5);
  return (
    <div style={{ display: "grid", gap: 16 }}>
      <H>Обзор</H>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(150px,1fr))", gap: 12 }}>
        <Stat l="Капитал в товаре" v={stockValue} c={C.purple} sub="на складе" />
        <Stat l="Приход / месяц" v={kpi.in} c={C.green} sub="продажи + обслуж." />
        <Stat l="Расход / месяц" v={kpi.out} c={C.red} sub="закуп + операц." />
        <Stat l="MRR" v={subs.mrr} c={C.teal} sub={`ARR ${short(subs.arr)}`} />
        <Stat l="Дебиторка" v={finance.totalRec} c={C.orange} sub="клиенты должны" />
        <Stat l="Кредиторка" v={finance.totalPay} c={C.red} sub="мы должны" />
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
        <Block title="⚠️ Контрольные события">
          {controlEvents.length === 0 ? <Muted>Нет ближайших событий.</Muted> : controlEvents.slice(0, 8).map((e, i) => (
            <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, padding: "7px 0", borderBottom: `1px solid ${C.line}`, fontSize: 13 }}>
              <Dot c={e.sev === "red" ? C.red : C.orange} /><span style={{ flex: 1 }}>{e.label}</span><span style={{ fontSize: 11, color: e.sev === "red" ? C.red : C.sub, fontWeight: 600 }}>{dshort(e.date)}</span>
            </div>
          ))}
        </Block>
        <div style={{ display: "grid", gap: 16 }}>
          <Block title="Ближайшие движения денег">
            {upcoming.map((e, i) => <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 0", borderBottom: `1px solid ${C.line}`, fontSize: 13 }}><span style={{ width: 50, fontSize: 11, color: C.sub }}>{dshort(e.date)}</span><span style={{ flex: 1 }}>{e.label}</span><span style={{ fontWeight: 700, color: e.type === "income" ? C.darkgreen : C.red }}>{e.type === "income" ? "+" : "−"}{short(e.amount)}</span></div>)}
          </Block>
          <Block title="Склад: внимание">
            {alerts.length === 0 ? <Muted>Всё в норме.</Muted> : alerts.map((w) => <div key={w.sku} style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 0", borderBottom: `1px solid ${C.line}`, fontSize: 13 }}><span style={{ flex: 1 }}>{w.name}</span><Badge c={w.free < 0 ? C.red : C.orange}>{w.free < 0 ? `Не хватает ${-w.free}` : `Осталось ${w.onHand}`}</Badge></div>)}
          </Block>
        </div>
      </div>
    </div>
  );
}

// ===== ПРИХОД =====
function Kanban({ arr, move, setArr, onAdd, onDel }) {
  const [open, setOpen] = useState(null); const oc = open != null ? arr.find((a) => a.id === open) : null;
  return (
    <div><div style={{ display: "flex", alignItems: "center", gap: 12 }}><H>Приход — жизненный цикл</H><button onClick={() => setOpen(onAdd())} style={{ ...addBtn, marginLeft: "auto" }}>+ Партия</button></div>
      <div style={{ display: "flex", gap: 10, overflowX: "auto", paddingBottom: 8, marginTop: 8 }}>
        {STAGES.map((st) => { const list = arr.filter((a) => a.stage === st.id); return (
          <div key={st.id} style={{ background: "#ebecf0", borderRadius: 10, minWidth: 190, maxWidth: 190, padding: 8, flexShrink: 0 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 7, padding: "2px 6px 8px" }}><span style={{ width: 8, height: 8, borderRadius: "50%", background: st.c }} /><span style={{ fontSize: 12, fontWeight: 700, flex: 1 }}>{st.t}</span><span style={{ fontSize: 11, color: C.sub }}>{list.length}</span></div>
            {list.map((a) => (
              <div key={a.id} onClick={() => setOpen(a.id)} style={{ background: C.white, borderRadius: 8, padding: 9, marginBottom: 7, boxShadow: "0 1px 0 rgba(9,30,66,.25)", cursor: "pointer", borderLeft: `4px solid ${st.c}` }}>
                <div style={{ fontSize: 13, fontWeight: 700 }}>{a.title}</div>
                <div style={{ fontSize: 10.5, color: C.sub, margin: "2px 0 6px" }}>{a.items.reduce((s, i) => s + i.qty, 0)} шт</div>
                <div style={{ fontSize: 12.5, fontWeight: 800 }}>{short(a.total)} <span style={{ fontSize: 9, color: C.sub }}>сум</span></div>
                <div style={{ display: "flex", justifyContent: "flex-end", gap: 4, marginTop: 6 }} onClick={(e) => e.stopPropagation()}><button onClick={() => move(a.id, -1)} style={navBtn}>◀</button><button onClick={() => move(a.id, 1)} style={navBtn}>▶</button></div>
              </div>
            ))}
          </div>
        ); })}
      </div>
      {oc && <ArrivalModal a={oc} setArr={setArr} onClose={() => setOpen(null)} onDelete={() => { onDel(oc.id); setOpen(null); }} />}
    </div>
  );
}
function ArrivalModal({ a, setArr, onClose, onDelete }) {
  const st = STAGES.find((s) => s.id === a.stage);
  const setField = (f, v) => setArr(a.id, (x) => ({ ...x, [f]: v }));
  const setCost = (g, f, v) => setArr(a.id, (x) => ({ ...x, costs: { ...x.costs, [g]: { ...x.costs[g], [f]: v } } }));
  const setItem = (idx, f, v) => setArr(a.id, (x) => ({ ...x, items: x.items.map((it, i) => (i === idx ? { ...it, [f]: v } : it)) }));
  const addItem = () => setArr(a.id, (x) => ({ ...x, items: [...x.items, { sku: CATALOG[0]?.sku || "", qty: 1, unitUSD: 0 }] }));
  const rmItem = (idx) => setArr(a.id, (x) => ({ ...x, items: x.items.filter((_, i) => i !== idx) }));
  return (
    <Modal onClose={onClose} color={st.c} title={`Партия ${a.title}`} sub={`${a.supplier || "—"} · ${st.t}`}>
      <Block title="Партия">
        <div style={{ display: "flex", flexWrap: "wrap", gap: 12 }}>
          <div style={{ flex: 1, minWidth: 180 }}><Label2>Номер партии</Label2><input value={a.title} onChange={(e) => setField("title", e.target.value)} style={{ ...txt, width: "100%", boxSizing: "border-box" }} /></div>
          <div style={{ flex: 1, minWidth: 180 }}><Label2>Поставщик</Label2><input value={a.supplier} onChange={(e) => setField("supplier", e.target.value)} style={{ ...txt, width: "100%", boxSizing: "border-box" }} /></div>
        </div>
        <Muted>Этап меняется кнопками ◀ ▶ на карточке партии.</Muted>
      </Block>
      <Block title="Товар">
        {a.items.length === 0 && <Muted>Нет позиций. Добавьте товары кнопкой ниже.</Muted>}
        {a.items.map((it, i) => <div key={i} style={{ display: "flex", gap: 8, alignItems: "flex-end", marginBottom: 6, flexWrap: "wrap" }}>
          <div style={{ flex: 1, minWidth: 160 }}><Label2>Товар</Label2><select value={it.sku} onChange={(e) => setItem(i, "sku", e.target.value)} style={{ ...sel, width: "100%" }}>{CATALOG.map((p) => <option key={p.sku} value={p.sku}>{p.name}</option>)}</select></div>
          <div><Label2>Кол-во</Label2><NumInput value={it.qty} onChange={(v) => setItem(i, "qty", v)} suffix="шт" w={80} /></div>
          <div><Label2>Цена $/шт</Label2><NumInput value={it.unitUSD} onChange={(v) => setItem(i, "unitUSD", v)} suffix="$" decimal accent={C.teal} w={90} /></div>
          <span style={{ fontSize: 12, color: C.blue, fontWeight: 700, width: 100, textAlign: "right", paddingBottom: 8 }}>{money(it.unit || 0)}/шт</span>
          <Xb on={() => rmItem(i)} pad />
        </div>)}
        <button onClick={addItem} style={{ fontSize: 13, fontWeight: 600, color: C.blue, background: "transparent", border: "none", cursor: "pointer", padding: "2px 0 4px" }}>+ Позиция</button>
      </Block>
      <Block title="Расходы по приходу — даты и курс">
        <CostRow label="Поставщик ($ × курс)" usd={a.costs.supplier} setCost={(f, v) => setCost("supplier", f, v)} fx />
        <CostRow label="Растаможка (пошлина+НДС)" g={a.costs.customs} setCost={(f, v) => setCost("customs", f, v)} />
        <CostRow label="Сертификация" g={a.costs.cert} setCost={(f, v) => setCost("cert", f, v)} />
        <CostRow label="Декларант" g={a.costs.declarant} setCost={(f, v) => setCost("declarant", f, v)} />
      </Block>
      <div style={{ background: C.ink, borderRadius: 10, padding: 16, color: C.white, display: "flex", justifyContent: "space-between", alignItems: "center" }}><span style={{ fontSize: 14, fontWeight: 700 }}>Полная себестоимость партии</span><span style={{ fontSize: 19, fontWeight: 800, color: C.teal }}>{money(a.total || 0)} сум</span></div>
      {onDelete && <div style={{ marginTop: 12, textAlign: "right" }}><button onClick={() => { if (window.confirm("Удалить эту партию?")) onDelete(); }} style={{ background: "transparent", border: `1px solid ${C.red}`, color: C.red, borderRadius: 8, padding: "7px 14px", fontWeight: 600, cursor: "pointer", fontSize: 12.5 }}>Удалить партию</button></div>}
    </Modal>
  );
}
function CostRow({ label, g, usd, setCost, fx }) {
  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 10, alignItems: "flex-end", padding: "8px 0", borderBottom: `1px solid ${C.line}` }}>
      <div style={{ flex: 1, minWidth: 150, fontSize: 13, fontWeight: 600 }}>{label}</div>
      {fx ? <><div><Label2>Сумма $</Label2><NumInput value={usd.usd} onChange={(v) => setCost("usd", v)} suffix="$" decimal accent={C.teal} w={100} /></div><div><Label2>Курс</Label2><NumInput value={usd.rate} onChange={(v) => setCost("rate", v)} suffix="сум/$" accent={C.teal} w={110} /></div><div><Label2>Дата</Label2><DF value={usd.date} on={(v) => setCost("date", v)} /></div></> : <><div><Label2>Сумма</Label2><NumInput value={g.amount} onChange={(v) => setCost("amount", v)} suffix="сум" decimal accent={C.orange} w={150} /></div><div><Label2>Дата</Label2><DF value={g.date} on={(v) => setCost("date", v)} /></div></>}
    </div>
  );
}

// ===== СКЛАД =====
function Warehouse({ warehouse, arr, stockValue }) {
  const [open, setOpen] = useState(null);
  return (
    <div style={{ display: "grid", gap: 16 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 14 }}><H>Виртуальный склад</H><div style={{ marginLeft: "auto", background: C.white, borderRadius: 10, padding: "10px 16px", boxShadow: "0 1px 3px rgba(9,30,66,.13)" }}><span style={{ fontSize: 12, color: C.sub }}>Капитал в остатках: </span><span style={{ fontSize: 17, fontWeight: 800, color: C.purple }}>{money(stockValue)} сум</span></div></div>
      <Block title="Остатки (средневзвешенная себестоимость · резерв под пред-продажи)">
        <div style={{ display: "grid", gridTemplateColumns: "1fr 70px 70px 80px 80px 110px 120px 90px", gap: 6, fontSize: 10.5, color: C.sub, fontWeight: 700, padding: "0 4px 8px", textTransform: "uppercase" }}><span>Товар</span><span style={{ textAlign: "right" }}>Приход</span><span style={{ textAlign: "right" }}>Прод.</span><span style={{ textAlign: "right" }}>Склад</span><span style={{ textAlign: "right" }}>Возд.</span><span style={{ textAlign: "right" }}>Своб.</span><span style={{ textAlign: "right" }}>Стоимость</span><span style={{ textAlign: "center" }}>Статус</span></div>
        {warehouse.map((w) => { const op = open === w.sku; const batches = arr.flatMap((a) => a.items.filter((i) => i.sku === w.sku).map((i) => ({ title: a.title, qty: i.qty, unit: i.unit, date: a.costs.supplier.date }))); return (
          <div key={w.sku} style={{ borderBottom: `1px solid ${C.line}` }}>
            <div onClick={() => setOpen(op ? null : w.sku)} style={{ display: "grid", gridTemplateColumns: "1fr 70px 70px 80px 80px 110px 120px 90px", gap: 6, alignItems: "center", padding: "9px 4px", cursor: "pointer", fontSize: 13 }}>
              <span style={{ fontWeight: 600 }}>▸ {w.name}</span><span style={{ textAlign: "right", color: C.sub }}>{w.received}</span><span style={{ textAlign: "right", color: C.sub }}>{w.sold}</span><span style={{ textAlign: "right", fontWeight: 700 }}>{w.onHand}</span><span style={{ textAlign: "right", color: w.reserved ? C.orange : C.sub }}>{w.reserved || "—"}</span><span style={{ textAlign: "right", fontWeight: 800, color: w.free < 0 ? C.red : C.darkgreen }}>{w.free}{w.free < 0 && " ⚠"}</span><span style={{ textAlign: "right", fontWeight: 700, color: C.purple }}>{short(w.value)}</span><span style={{ textAlign: "center" }}><Badge c={w.onHand === 0 ? C.red : w.onHand <= 6 ? C.orange : C.green}>{w.onHand === 0 ? "Нет" : w.onHand <= 6 ? "Мало" : "ОК"}</Badge></span>
            </div>
            {op && <div style={{ padding: "4px 4px 12px 18px", background: "#fafbfc" }}><div style={{ fontSize: 11, color: C.sub, fontWeight: 700, marginBottom: 4 }}>ПАРТИИ ПРИХОДА:</div>{batches.map((b, i) => <div key={i} style={{ display: "flex", justifyContent: "space-between", fontSize: 12.5, padding: "3px 0", color: C.sub }}><span>{b.title} · {dshort(b.date)}</span><span>{b.qty} шт × {fmt(b.unit)} сум</span></div>)}</div>}
          </div>
        ); })}
        <Muted>⚠ «своб.» отрицательно = обещано больше, чем на складе → товар должен прийти.</Muted>
      </Block>
    </div>
  );
}

// ===== КЛИЕНТЫ =====
function Clients({ clients, setClients, setClient, orders }) {
  const [open, setOpen] = useState(null); const oc = open != null ? clients.find((c) => c.id === open) : null;
  const addClient = () => { const id = Date.now(); setClients((cs) => [...cs, { id, name: "Новый клиент", early: false, inn: "", address: "", phone: "", email: "", director: "", directorOrderNo: "", directorOrderDate: "", directorOrderValid: "", contractNo: "", contractDate: "", contractValid: "", contactPerson: "", contactPhone: "", personalDiscount: 0, personalMarkup: 0, notes: "", documents: [], branches: [] }]); setOpen(id); };
  return (
    <div>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}><H>Клиенты</H><button onClick={addClient} style={addBtn}>+ Клиент</button></div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(290px,1fr))", gap: 12 }}>
        {clients.map((c) => { const co = orders.filter((o) => o.clientId === c.id); return (
          <div key={c.id} onClick={() => setOpen(c.id)} style={{ background: C.white, borderRadius: 12, padding: 14, boxShadow: "0 1px 3px rgba(9,30,66,.13)", cursor: "pointer", borderTop: `3px solid ${c.early ? C.orange : C.blue}` }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }}><span style={{ width: 40, height: 40, borderRadius: "50%", background: c.early ? C.orange : C.blue, color: C.white, fontWeight: 800, fontSize: 16, display: "grid", placeItems: "center" }}>{c.name[0]}</span><div style={{ flex: 1 }}><div style={{ fontSize: 14.5, fontWeight: 800 }}>{c.name}</div><div style={{ fontSize: 11, color: C.sub }}>ИНН {c.inn || "—"}</div></div></div>
            <div style={{ display: "flex", flexWrap: "wrap", gap: 5, marginBottom: 8 }}>{c.early && <Badge c={C.orange}>★ Акция</Badge>}{c.personalDiscount > 0 && <Badge c={C.red}>−{c.personalDiscount}%</Badge>}{c.personalMarkup > 0 && <Badge c={C.teal}>+{c.personalMarkup}%</Badge>}{c.branches.length > 0 && <Badge c={C.purple}>{c.branches.length} объект(а)</Badge>}{expired(c.directorOrderValid) && <Badge c={C.red}>приказ истёк</Badge>}</div>
            <div style={{ fontSize: 12, color: C.sub, display: "flex", justifyContent: "space-between", borderTop: `1px solid ${C.line}`, paddingTop: 8 }}><span>📞 {c.contactPerson || "—"}</span><span>{co.length} заказ(ов)</span></div>
          </div>
        ); })}
      </div>
      {oc && <ClientModal c={oc} setClient={setClient} onClose={() => setOpen(null)} />}
    </div>
  );
}
function ClientModal({ c, setClient, onClose }) {
  const upd = (f, v) => setClient(c.id, (x) => ({ ...x, [f]: v }));
  const setBranch = (id, f, v) => setClient(c.id, (x) => ({ ...x, branches: x.branches.map((b) => (b.id === id ? { ...b, [f]: v } : b)) }));
  const addBranch = () => setClient(c.id, (x) => ({ ...x, branches: [...x.branches, { id: Date.now(), name: "Новый объект", address: "", contactPerson: "", contactPhone: "" }] }));
  const rmBranch = (id) => setClient(c.id, (x) => ({ ...x, branches: x.branches.filter((b) => b.id !== id) }));
  const setDoc = (id, f, v) => setClient(c.id, (x) => ({ ...x, documents: x.documents.map((d) => (d.id === id ? { ...d, [f]: v } : d)) }));
  const addDoc = () => setClient(c.id, (x) => ({ ...x, documents: [...x.documents, { id: Date.now(), name: "Новый документ", kind: "Прочее" }] }));
  const rmDoc = (id) => setClient(c.id, (x) => ({ ...x, documents: x.documents.filter((d) => d.id !== id) }));
  return (
    <Modal onClose={onClose} color={c.early ? C.orange : C.blue} titleEl={<input value={c.name} onChange={(e) => upd("name", e.target.value)} style={{ border: "none", background: "transparent", color: C.white, fontSize: 19, fontWeight: 800, width: "100%", outline: "none" }} />} sub="Карточка клиента" extra={<button onClick={() => upd("early", !c.early)} style={{ border: "none", background: "rgba(255,255,255,.25)", color: C.white, borderRadius: 6, padding: "6px 10px", cursor: "pointer", fontSize: 12, fontWeight: 600 }}>{c.early ? "★ Акция" : "Обычный"}</button>}>
      <Block title="Реквизиты"><Grid><TF label="ИНН" value={c.inn} on={(v) => upd("inn", v)} /><TF label="Телефон" value={c.phone} on={(v) => upd("phone", v)} /><TF label="E-mail" value={c.email} on={(v) => upd("email", v)} /><TF label="Адрес" value={c.address} on={(v) => upd("address", v)} wide /></Grid></Block>
      <Block title="Руководитель и приказ"><Grid><TF label="Директор (ФИО)" value={c.director} on={(v) => upd("director", v)} /><TF label="№ приказа на директора" value={c.directorOrderNo} on={(v) => upd("directorOrderNo", v)} /><DFf label="Дата приказа" value={c.directorOrderDate} on={(v) => upd("directorOrderDate", v)} /><DFf label="Действует до" value={c.directorOrderValid} on={(v) => upd("directorOrderValid", v)} warn={expired(c.directorOrderValid)} /></Grid>{expired(c.directorOrderValid) && <Muted style={{ color: C.red }}>⚠ Срок приказа истёк — запросить актуальный.</Muted>}</Block>
      <Block title="Договор"><Grid><TF label="№ договора" value={c.contractNo} on={(v) => upd("contractNo", v)} /><DFf label="Дата договора" value={c.contractDate} on={(v) => upd("contractDate", v)} /><DFf label="Действует до" value={c.contractValid} on={(v) => upd("contractValid", v)} warn={expired(c.contractValid)} /></Grid></Block>
      <Block title="Контактное лицо и заметки"><Grid><TF label="Контактное лицо" value={c.contactPerson} on={(v) => upd("contactPerson", v)} /><TF label="Телефон контакта" value={c.contactPhone} on={(v) => upd("contactPhone", v)} /></Grid><Label2>Личные заметки</Label2><textarea value={c.notes} onChange={(e) => upd("notes", e.target.value)} style={{ ...txt, width: "100%", minHeight: 54, resize: "vertical", boxSizing: "border-box" }} /></Block>
      <Block title="Персональные условия"><Grid><div><Label2>Личная скидка</Label2><NumInput value={c.personalDiscount} onChange={(v) => upd("personalDiscount", v)} suffix="%" accent={C.red} w="100%" /></div><div><Label2>Личная наценка</Label2><NumInput value={c.personalMarkup} onChange={(v) => upd("personalMarkup", v)} suffix="%" accent={C.teal} w="100%" /></div></Grid></Block>
      <Block title={`Документы (${c.documents.length})`}>{c.documents.map((d) => <div key={d.id} style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 8 }}><select value={d.kind} onChange={(e) => setDoc(d.id, "kind", e.target.value)} style={{ ...sel, minWidth: 130 }}>{DOC_KINDS.map((k) => <option key={k}>{k}</option>)}</select><input value={d.name} onChange={(e) => setDoc(d.id, "name", e.target.value)} style={{ ...txt, flex: 1 }} /><Xb on={() => rmDoc(d.id)} /></div>)}<button onClick={addDoc} style={addBtn}>+ Документ</button></Block>
      <Block title={`Объекты и склады (${c.branches.length})`}>{c.branches.map((b) => <div key={b.id} style={{ border: `1px solid ${C.line}`, borderRadius: 8, padding: 10, marginBottom: 8 }}><div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 8 }}><span style={{ color: C.purple, fontSize: 16 }}>📍</span><input value={b.name} onChange={(e) => setBranch(b.id, "name", e.target.value)} style={{ ...txt, flex: 1, fontWeight: 700 }} /><Xb on={() => rmBranch(b.id)} /></div><Grid><TF label="Адрес" value={b.address} on={(v) => setBranch(b.id, "address", v)} wide /><TF label="Контактное лицо" value={b.contactPerson} on={(v) => setBranch(b.id, "contactPerson", v)} /><TF label="Телефон" value={b.contactPhone} on={(v) => setBranch(b.id, "contactPhone", v)} /></Grid></div>)}<button onClick={addBranch} style={addBtn}>+ Объект</button></Block>
    </Modal>
  );
}

// ===== ПРОДАЖИ =====
function Sales({ clients, warehouse, orders, addOrder, setOrderStatus, delOrder, wac, mrr }) {
  const delivered = orders.filter((o) => o.status === "delivered").reduce((s, o) => s + orderTotal(o), 0);
  const inAir = orders.filter((o) => o.status !== "delivered").reduce((s, o) => s + orderTotal(o), 0);
  const prepaid = orders.filter((o) => o.prepaid).reduce((s, o) => s + o.prepaid.amount, 0);
  const cogs = orders.filter((o) => o.status === "delivered").reduce((s, o) => s + o.items.reduce((a, i) => a + i.qty * wac(i.sku), 0), 0);
  return (
    <div style={{ display: "grid", gap: 16 }}>
      <H>Продажи</H>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(170px,1fr))", gap: 12 }}>
        <Stat l="Доставлено (факт)" v={delivered} c={C.darkgreen} sub={`прибыль ${short(delivered - cogs)}`} />
        <Stat l="В воздухе" v={inAir} c={C.orange} sub="пред-продажи + предопл." />
        <Stat l="Получено предоплат" v={prepaid} c={C.blue} sub="оплачено до доставки" />
        <Stat l="MRR обслуживания" v={mrr} c={C.teal} sub="в месяц" />
      </div>
      <OrderBuilder clients={clients} warehouse={warehouse} onCreate={addOrder} />
      <Block title="Заказы">
        {orders.slice().reverse().map((o) => { const cl = clients.find((c) => c.id === o.clientId); const br = cl?.branches.find((b) => b.id === o.branchId); const stt = STATUS[o.status]; return (
          <div key={o.id} style={{ border: `1px solid ${C.line}`, borderRadius: 10, padding: 12, marginBottom: 10 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap", marginBottom: 8 }}><Badge c={stt.c}>{stt.l}</Badge><span style={{ fontWeight: 700 }}>{cl?.name}</span>{br && <span style={{ fontSize: 12, color: C.purple }}>· {br.name}</span>}<span style={{ fontSize: 12, color: C.sub }}>{dshort(o.date)}</span><span style={{ marginLeft: "auto", fontSize: 15, fontWeight: 800, color: C.darkgreen }}>{money(orderTotal(o))} сум</span></div>
            <div style={{ display: "grid", gap: 3, marginBottom: o.note || o.prepaid ? 8 : 0 }}>{o.items.map((i, j) => <div key={j} style={{ display: "flex", justifyContent: "space-between", fontSize: 13 }}><span>{skuName(i.sku)} × {i.qty}</span><span style={{ color: C.sub }}>{fmt(i.price)} сум</span></div>)}{o.discountPct > 0 && <div style={{ fontSize: 12, color: C.red, textAlign: "right" }}>скидка −{o.discountPct}%</div>}</div>
            {o.note && <div style={{ fontSize: 12.5, color: "#7a5d00", background: "#fff8e6", borderRadius: 6, padding: "6px 10px", marginBottom: 6 }}>📝 {o.note}</div>}
            {o.prepaid && <div style={{ fontSize: 12.5, color: C.blue, background: "#eef6ff", borderRadius: 6, padding: "6px 10px", marginBottom: 6 }}>💳 Предоплата {money(o.prepaid.amount)} сум от {dshort(o.prepaid.date)}</div>}
            <div style={{ display: "flex", gap: 6 }}>{o.status !== "delivered" && <button onClick={() => setOrderStatus(o.id, "delivered")} style={{ fontSize: 12, fontWeight: 600, color: C.darkgreen, background: "#eafaea", border: "none", borderRadius: 6, padding: "5px 12px", cursor: "pointer" }}>✓ Отметить доставку</button>}<button onClick={() => delOrder(o.id)} style={{ fontSize: 12, color: C.sub, background: "transparent", border: "none", cursor: "pointer", marginLeft: "auto" }}>Удалить</button></div>
          </div>
        ); })}
      </Block>
    </div>
  );
}
function OrderBuilder({ clients, warehouse, onCreate }) {
  const suggest = (clientId, sku) => { const c = clients.find((x) => x.id === clientId); return Math.round((prod(sku)?.listPrice || 0) * (1 + (c?.personalMarkup || 0) / 100)); };
  const [d, setD] = useState({ clientId: clients[0]?.id, branchId: null, status: "delivered", discountPct: clients[0]?.personalDiscount || 0, note: "", prepaidAmount: 0, prepaidDate: TODAYS, lines: [{ sku: CATALOG[0]?.sku || "", qty: 1, price: 0 }] });
  const cl = clients.find((c) => c.id === d.clientId);
  const onClient = (id) => { const c = clients.find((x) => x.id === id); setD((p) => ({ ...p, clientId: id, branchId: null, discountPct: c?.personalDiscount || 0, lines: p.lines.map((l) => ({ ...l, price: suggest(id, l.sku) })) })); };
  const setLine = (i, f, v) => setD((p) => ({ ...p, lines: p.lines.map((l, j) => (j === i ? { ...l, [f]: v, ...(f === "sku" ? { price: suggest(p.clientId, v) } : {}) } : l)) }));
  const addLine = () => setD((p) => ({ ...p, lines: [...p.lines, { sku: CATALOG[0]?.sku || "", qty: 1, price: suggest(p.clientId, CATALOG[0]?.sku) }] }));
  const rmLine = (i) => setD((p) => ({ ...p, lines: p.lines.filter((_, j) => j !== i) }));
  const sub = d.lines.reduce((s, l) => s + l.qty * l.price, 0); const total = sub * (1 - d.discountPct / 100);
  const create = () => { if (!d.lines.length) return; onCreate({ clientId: d.clientId, branchId: d.branchId, date: TODAYS, status: d.status, discountPct: d.discountPct, note: d.note, prepaid: d.status === "prepaid" ? { amount: d.prepaidAmount || total, date: d.prepaidDate } : null, items: d.lines.map((l) => ({ sku: l.sku, qty: l.qty, price: l.price })) }); setD((p) => ({ ...p, note: "", prepaidAmount: 0, lines: [{ sku: CATALOG[0]?.sku || "", qty: 1, price: suggest(p.clientId, CATALOG[0]?.sku) }] })); };
  return (
    <Block title="Новый заказ">
      <div style={{ display: "flex", flexWrap: "wrap", gap: 12, marginBottom: 12 }}>
        <div><Label2>Клиент</Label2><select value={d.clientId} onChange={(e) => onClient(+e.target.value)} style={sel}>{clients.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}</select></div>
        <div><Label2>Объект</Label2><select value={d.branchId || ""} onChange={(e) => setD((p) => ({ ...p, branchId: e.target.value ? +e.target.value : null }))} style={sel}><option value="">— основной —</option>{(cl?.branches || []).map((b) => <option key={b.id} value={b.id}>{b.name}</option>)}</select></div>
        <div><Label2>Статус</Label2><select value={d.status} onChange={(e) => setD((p) => ({ ...p, status: e.target.value }))} style={sel}><option value="delivered">Доставлено</option><option value="presale">Пред-продажа (в воздухе)</option><option value="prepaid">Предоплата без доставки</option></select></div>
        <div><Label2>Скидка %</Label2><NumInput value={d.discountPct} onChange={(v) => setD((p) => ({ ...p, discountPct: v }))} suffix="%" accent={C.red} w={90} /></div>
      </div>
      <div style={{ fontSize: 11, color: C.sub, fontWeight: 700, marginBottom: 6, textTransform: "uppercase" }}>Позиции</div>
      {d.lines.map((l, i) => <div key={i} style={{ display: "flex", gap: 8, alignItems: "flex-end", marginBottom: 8, flexWrap: "wrap" }}><div style={{ flex: 1, minWidth: 180 }}><select value={l.sku} onChange={(e) => setLine(i, "sku", e.target.value)} style={{ ...sel, width: "100%" }}>{CATALOG.map((p) => <option key={p.sku} value={p.sku}>{p.name} (своб. {warehouse.find((s) => s.sku === p.sku)?.free})</option>)}</select></div><div><Label2>Кол-во</Label2><NumInput value={l.qty} onChange={(v) => setLine(i, "qty", v)} w={80} /></div><div><Label2>Цена/шт</Label2><NumInput value={l.price} onChange={(v) => setLine(i, "price", v)} suffix="сум" accent={C.darkgreen} w={150} decimal /></div><span style={{ width: 100, textAlign: "right", fontSize: 13, fontWeight: 700, paddingBottom: 8 }}>{short(l.qty * l.price)}</span><Xb on={() => rmLine(i)} pad /></div>)}
      <button onClick={addLine} style={{ fontSize: 13, fontWeight: 600, color: C.blue, background: "transparent", border: "none", cursor: "pointer", padding: "2px 0 10px" }}>+ Позиция</button>
      {d.status === "presale" && <div style={{ marginBottom: 10 }}><Label2>Заметка (что «в воздухе»)</Label2><input value={d.note} onChange={(e) => setD((p) => ({ ...p, note: e.target.value }))} placeholder="ждём партию TZ-05/2026" style={{ ...txt, width: "100%", boxSizing: "border-box" }} /></div>}
      {d.status === "prepaid" && <div style={{ display: "flex", gap: 10, marginBottom: 10, flexWrap: "wrap" }}><div><Label2>Сумма предоплаты</Label2><NumInput value={d.prepaidAmount} onChange={(v) => setD((p) => ({ ...p, prepaidAmount: v }))} suffix="сум" accent={C.blue} w={160} decimal /></div><div><Label2>Дата оплаты</Label2><DF value={d.prepaidDate} on={(v) => setD((p) => ({ ...p, prepaidDate: v }))} /></div><div style={{ flex: 1, minWidth: 180 }}><Label2>Заметка</Label2><input value={d.note} onChange={(e) => setD((p) => ({ ...p, note: e.target.value }))} placeholder="доставка после растаможки" style={{ ...txt, width: "100%", boxSizing: "border-box" }} /></div></div>}
      <div style={{ display: "flex", alignItems: "center", gap: 16, borderTop: `1px solid ${C.line}`, paddingTop: 12 }}><span style={{ fontSize: 13, color: C.sub }}>Сумма: <b style={{ color: C.ink }}>{money(sub)}</b>{d.discountPct > 0 && <> · −{d.discountPct}% · итого <b style={{ color: C.darkgreen }}>{money(total)}</b></>}</span><button onClick={create} style={{ marginLeft: "auto", background: C.darkgreen, color: C.white, border: "none", borderRadius: 8, padding: "10px 20px", fontWeight: 700, cursor: "pointer", fontSize: 14 }}>Создать заказ</button></div>
    </Block>
  );
}

// ===== ОБОРУДОВАНИЕ =====
function Equipment({ equipment, clients, subs, setEquip, setEquipment }) {
  const cName = (id) => clients.find((c) => c.id === id)?.name || "—";
  const oName = (cid, bid) => clients.find((c) => c.id === cid)?.branches.find((b) => b.id === bid)?.name || "основной";
  const byObject = useMemo(() => { const m = {}; equipment.forEach((e) => { const key = `${cName(e.clientId)} · ${oName(e.clientId, e.branchId)}`; (m[key] = m[key] || []).push(e); }); return Object.entries(m); }, [equipment]);
  const addUnit = () => { if (!clients.length) { alert("Сначала добавьте хотя бы одного клиента в разделе «Клиенты»."); return; } setEquipment((es) => [...es, { id: Date.now(), serial: "НОВЫЙ-0000", eui: "", lora: "", sku: CATALOG[0]?.sku || "", clientId: clients[0].id, branchId: clients[0].branches[0]?.id || null, install: TODAYS, poverka: "", validation: "", tariff: 150000, status: "online", lastSeen: "—", freeUntil: "" }]); };
  return (
    <div style={{ display: "grid", gap: 16 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12 }}><H>Оборудование и подписки</H><button onClick={addUnit} style={{ ...addBtn, marginLeft: "auto" }}>+ Устройство</button></div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(150px,1fr))", gap: 12 }}>
        <Stat l="MRR" v={subs.mrr} c={C.teal} sub="абонплата / мес" />
        <Stat l="ARR" v={subs.arr} c={C.darkgreen} sub="годовая выручка" />
        <Stat l="Активных устройств" v={subs.activeN} c={C.blue} sub={`новых за месяц: ${subs.newM}`} valueRaw />
        <Stat l="На акции (бесплатно)" v={subs.free} c={C.orange} sub="ещё не платят" valueRaw />
        <Stat l="Офлайн / тревоги" v={subs.offline} c={subs.offline ? C.red : C.green} sub="нет связи" valueRaw />
      </div>

      <Block title="Объекты клиентов">
        {byObject.map(([key, list]) => (
          <div key={key} style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 0", borderBottom: `1px solid ${C.line}`, fontSize: 13 }}>
            <span style={{ color: C.purple }}>📍</span><span style={{ flex: 1, fontWeight: 600 }}>{key}</span>
            <span style={{ fontSize: 12, color: C.sub }}>{list.length} устр.</span>
            <span style={{ display: "flex", gap: 4 }}>{list.some((e) => e.status === "offline") && <Badge c={C.red}>тревога</Badge>}<Badge c={C.teal}>{short(list.reduce((s, e) => s + e.tariff, 0))}/мес</Badge></span>
          </div>
        ))}
      </Block>

      <Block title="Реестр устройств (серийные номера)">
        <div style={{ display: "grid", gridTemplateColumns: "1.5fr 1.3fr 90px 110px 110px 100px 90px", gap: 8, fontSize: 10.5, color: C.sub, fontWeight: 700, padding: "0 4px 8px", textTransform: "uppercase" }}><span>Серийный / EUI</span><span>Клиент · объект</span><span style={{ textAlign: "right" }}>Монтаж</span><span style={{ textAlign: "right" }}>Поверка</span><span style={{ textAlign: "right" }}>Валидация</span><span style={{ textAlign: "right" }}>Тариф</span><span style={{ textAlign: "center" }}>Связь</span></div>
        {equipment.map((e) => (
          <div key={e.id} style={{ display: "grid", gridTemplateColumns: "1.5fr 1.3fr 90px 110px 110px 100px 90px", gap: 8, alignItems: "center", padding: "9px 4px", borderBottom: `1px solid ${C.line}`, fontSize: 12.5 }}>
            <span><b style={{ fontSize: 13 }}>{e.serial}</b><br /><span style={{ fontSize: 10.5, color: C.sub, fontFamily: "monospace" }}>EUI {e.eui || "—"} · {e.lora}</span></span>
            <span>{cName(e.clientId)}<br /><span style={{ fontSize: 11, color: C.purple }}>{oName(e.clientId, e.branchId)}</span></span>
            <span style={{ textAlign: "right", color: C.sub }}>{dshort(e.install)}</span>
            <span style={{ textAlign: "right", fontWeight: 700, color: dateColor(e.poverka) }}>{dshort(e.poverka)}</span>
            <span style={{ textAlign: "right", fontWeight: 700, color: dateColor(e.validation) }}>{dshort(e.validation)}</span>
            <span style={{ textAlign: "right" }}>{fmt(e.tariff)}{e.freeUntil && parse(e.freeUntil) > TODAY && <><br /><span style={{ fontSize: 10, color: C.orange }}>беспл. до {dshort(e.freeUntil)}</span></>}</span>
            <span style={{ textAlign: "center" }}><button onClick={() => setEquip(e.id, (x) => ({ ...x, status: x.status === "online" ? "offline" : "online" }))} style={{ border: "none", background: "transparent", cursor: "pointer" }}><Badge c={e.status === "online" ? C.green : C.red}>{e.status === "online" ? "● онлайн" : "● офлайн"}</Badge></button></span>
          </div>
        ))}
        <Muted>Цвет даты: 🔴 просрочено · 🟠 ≤45 дней · клик по «связь» переключает онлайн/офлайн (демо IoT).</Muted>
      </Block>
    </div>
  );
}

// ===== ФИНАНСЫ =====
function Finance({ finance }) {
  return (
    <div style={{ display: "grid", gap: 16 }}>
      <H>Финансы</H>
      <Block title="P&L — отчёт о прибылях и убытках по месяцам">
        <div style={{ overflowX: "auto" }}>
          <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 12.5, minWidth: 560 }}>
            <thead><tr style={{ color: C.sub, textAlign: "right" }}><th style={{ textAlign: "left", padding: "4px 8px", fontWeight: 700 }}>Показатель</th>{finance.pnl.map((p, i) => <th key={i} style={{ padding: "4px 8px", fontWeight: 700, textTransform: "capitalize", whiteSpace: "nowrap" }}>{p.d.toLocaleDateString("ru-RU", { month: "short", year: "2-digit" })}</th>)}</tr></thead>
            <tbody>
              {[["Выручка", "revenue", C.darkgreen], ["Себестоимость", "cogs", C.sub], ["Валовая прибыль", "gross", C.blue], ["Зарплаты", "salaries", C.sub], ["Аренда", "rent", C.sub], ["Прочее", "other", C.sub], ["Чистая прибыль", "net", null]].map(([label, key, color], r) => (
                <tr key={key} style={{ borderTop: key === "net" || key === "gross" ? `1px solid ${C.line}` : "none" }}>
                  <td style={{ textAlign: "left", padding: "6px 8px", fontWeight: key === "net" || key === "gross" ? 800 : 500 }}>{label}</td>
                  {finance.pnl.map((p, i) => <td key={i} style={{ textAlign: "right", padding: "6px 8px", fontWeight: key === "net" || key === "gross" ? 800 : 500, color: key === "net" ? (p.net >= 0 ? C.darkgreen : C.red) : (color || C.ink) }}>{["cogs", "salaries", "rent", "other"].includes(key) && p[key] ? "−" : ""}{short(p[key])}</td>)}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Block>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
        <Block title={`Дебиторка — кто должен вам (${short(finance.totalRec)})`}>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 110px 90px", gap: 8, fontSize: 10.5, color: C.sub, fontWeight: 700, padding: "0 4px 6px", textTransform: "uppercase" }}><span>Клиент</span><span style={{ textAlign: "right" }}>Долг</span><span style={{ textAlign: "right" }}>Просрочка</span></div>
          {finance.receivables.length === 0 ? <Muted>Долгов нет.</Muted> : finance.receivables.map((r) => (
            <div key={r.id} style={{ display: "grid", gridTemplateColumns: "1fr 110px 90px", gap: 8, alignItems: "center", padding: "8px 4px", borderBottom: `1px solid ${C.line}`, fontSize: 13 }}>
              <span style={{ fontWeight: 600 }}>{r.client}</span><span style={{ textAlign: "right", fontWeight: 700, color: C.orange }}>{fmt(r.debt)}</span><span style={{ textAlign: "right" }}>{r.overdue > 0 ? <Badge c={C.red}>{r.overdue} дн</Badge> : <Badge c={C.green}>в срок</Badge>}</span>
            </div>
          ))}
        </Block>
        <Block title={`Кредиторка — кому должны вы (${short(finance.totalPay)})`}>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 110px 90px", gap: 8, fontSize: 10.5, color: C.sub, fontWeight: 700, padding: "0 4px 6px", textTransform: "uppercase" }}><span>Партия</span><span>Назначение</span><span style={{ textAlign: "right" }}>Сумма</span><span style={{ textAlign: "right" }}>Срок</span></div>
          {finance.payables.length === 0 ? <Muted>Долгов нет.</Muted> : finance.payables.map((p, i) => (
            <div key={i} style={{ display: "grid", gridTemplateColumns: "1fr 1fr 110px 90px", gap: 8, alignItems: "center", padding: "8px 4px", borderBottom: `1px solid ${C.line}`, fontSize: 13 }}>
              <span style={{ fontWeight: 600 }}>{p.supplier}</span><span style={{ color: C.sub }}>{p.label}</span><span style={{ textAlign: "right", fontWeight: 700, color: C.red }}>{fmt(p.amount)}</span><span style={{ textAlign: "right", fontSize: 11, color: dateColor(p.due) }}>{dshort(p.due)}</span>
            </div>
          ))}
        </Block>
      </div>
    </div>
  );
}

// ===== КАЛЕНДАРЬ =====
function Calendar({ events, planned, setPlanned, recurring, setRecurring }) {
  const [mode, setMode] = useState("month");
  const [cursor, setCursor] = useState(new Date(TODAY.getFullYear(), TODAY.getMonth(), 1));
  const [np, setNp] = useState({ date: TODAYS, label: "", amount: 0, type: "expense" });
  const evOn = (d) => events.filter((e) => e.date === ymd(d));
  const step = (dir) => { const c = new Date(cursor); if (mode === "week") c.setDate(c.getDate() + dir * 7); else c.setMonth(c.getMonth() + dir); setCursor(c); };
  const addP = () => { if (np.label && np.amount) { setPlanned((p) => [...p, { id: Date.now(), ...np }]); setNp((p) => ({ ...p, label: "", amount: 0 })); } };
  return (
    <div style={{ display: "grid", gap: 14 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
        <H>Календарь движения денег</H>
        <div style={{ display: "flex", gap: 4, background: "#e4e6ea", borderRadius: 8, padding: 3 }}>{[["week", "Неделя"], ["month", "Месяц"], ["overview", "Обзор"]].map(([m, l]) => <button key={m} onClick={() => setMode(m)} style={{ border: "none", borderRadius: 6, padding: "6px 14px", cursor: "pointer", fontSize: 13, fontWeight: 600, background: mode === m ? C.white : "transparent", color: mode === m ? C.ink : C.sub }}>{l}</button>)}</div>
        {mode !== "overview" && <div style={{ display: "flex", alignItems: "center", gap: 8, marginLeft: "auto" }}><button onClick={() => step(-1)} style={navBtn2}>◀</button><span style={{ fontSize: 14, fontWeight: 700, minWidth: 150, textAlign: "center", textTransform: "capitalize" }}>{mode === "week" ? `Неделя ${dshort(ymd(weekDays(cursor)[0]))}` : monLabel(cursor)}</span><button onClick={() => step(1)} style={navBtn2}>▶</button></div>}
      </div>
      <div style={{ display: "flex", gap: 14, fontSize: 12, color: C.sub }}><span><Dot c={C.darkgreen} /> приход</span><span><Dot c={C.red} /> расход</span><span><Dot c={C.orange} /> план</span></div>
      {mode === "month" && <MonthView cursor={cursor} evOn={evOn} events={events} />}
      {mode === "week" && <WeekView cursor={cursor} evOn={evOn} />}
      {mode === "overview" && <OverviewView events={events} />}
      <Block title="Добавить плановое движение">
        <div style={{ display: "flex", flexWrap: "wrap", gap: 10, alignItems: "flex-end" }}>
          <div><Label2>Дата</Label2><DF value={np.date} on={(v) => setNp((p) => ({ ...p, date: v }))} /></div>
          <div style={{ flex: 1, minWidth: 180 }}><Label2>Описание</Label2><input value={np.label} onChange={(e) => setNp((p) => ({ ...p, label: e.target.value }))} style={{ ...txt, width: "100%", boxSizing: "border-box" }} /></div>
          <div><Label2>Сумма</Label2><NumInput value={np.amount} onChange={(v) => setNp((p) => ({ ...p, amount: v }))} suffix="сум" w={150} decimal /></div>
          <div><Label2>Тип</Label2><select value={np.type} onChange={(e) => setNp((p) => ({ ...p, type: e.target.value }))} style={sel}><option value="expense">Расход</option><option value="income">Приход</option></select></div>
          <button onClick={addP} style={{ background: C.blue, color: C.white, border: "none", borderRadius: 8, padding: "9px 16px", fontWeight: 700, cursor: "pointer" }}>+ В план</button>
        </div>
        <div style={{ display: "flex", gap: 16, marginTop: 12, fontSize: 12.5, color: C.sub, flexWrap: "wrap" }}><span style={{ display: "flex", alignItems: "center", gap: 6 }}>Аренда/мес: <NumInput value={recurring.rent} onChange={(v) => setRecurring((r) => ({ ...r, rent: v }))} suffix="сум" w={140} /></span><span style={{ display: "flex", alignItems: "center", gap: 6 }}>Зарплаты/мес: <NumInput value={recurring.salaries} onChange={(v) => setRecurring((r) => ({ ...r, salaries: v }))} suffix="сум" w={150} /></span></div>
      </Block>
    </div>
  );
}
function MonthView({ cursor, evOn, events }) {
  const y = cursor.getFullYear(), m = cursor.getMonth(); const first = new Date(y, m, 1); const sd = (first.getDay() + 6) % 7; const start = new Date(y, m, 1 - sd);
  const days = Array.from({ length: 42 }, (_, i) => { const d = new Date(start); d.setDate(start.getDate() + i); return d; });
  const me = events.filter((e) => { const d = parse(e.date); return d.getFullYear() === y && d.getMonth() === m; });
  const inc = me.filter((e) => e.type === "income").reduce((s, e) => s + e.amount, 0), exp = me.filter((e) => e.type === "expense").reduce((s, e) => s + e.amount, 0);
  return (
    <div style={{ background: C.white, borderRadius: 12, padding: 12, boxShadow: "0 1px 3px rgba(9,30,66,.13)" }}>
      <div style={{ display: "flex", gap: 18, marginBottom: 10, padding: "0 4px", fontSize: 13 }}><span style={{ color: C.darkgreen, fontWeight: 700 }}>Приход: {money(inc)}</span><span style={{ color: C.red, fontWeight: 700 }}>Расход: {money(exp)}</span><span style={{ fontWeight: 800, color: inc - exp >= 0 ? C.darkgreen : C.red }}>Нетто: {money(inc - exp)}</span></div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(7,1fr)", gap: 4 }}>
        {WD.map((w) => <div key={w} style={{ fontSize: 11, fontWeight: 700, color: C.sub, textAlign: "center", padding: 4 }}>{w}</div>)}
        {days.map((d, i) => { const inM = d.getMonth() === m; const isT = ymd(d) === ymd(TODAY); const de = evOn(d); return (
          <div key={i} style={{ minHeight: 76, border: `1px solid ${isT ? C.blue : C.line}`, borderRadius: 8, padding: 5, background: inM ? C.white : "#fafbfc", opacity: inM ? 1 : .5 }}>
            <div style={{ fontSize: 11, fontWeight: isT ? 800 : 600, color: isT ? C.blue : C.sub, marginBottom: 3 }}>{d.getDate()}</div>
            {de.slice(0, 3).map((e, j) => <div key={j} style={{ fontSize: 9.5, fontWeight: 600, marginBottom: 2, padding: "1px 4px", borderRadius: 3, color: C.white, background: e.type === "income" ? C.darkgreen : (e.planned ? C.orange : C.red), whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{e.type === "income" ? "+" : "−"}{short(e.amount)}</div>)}
            {de.length > 3 && <div style={{ fontSize: 9, color: C.sub }}>+{de.length - 3}…</div>}
          </div>
        ); })}
      </div>
    </div>
  );
}
function WeekView({ cursor, evOn }) {
  return (
    <div style={{ display: "grid", gap: 8 }}>
      {weekDays(cursor).map((d, i) => { const de = evOn(d); const isT = ymd(d) === ymd(TODAY); const net = de.reduce((s, e) => s + (e.type === "income" ? e.amount : -e.amount), 0); return (
        <div key={i} style={{ background: C.white, borderRadius: 10, padding: 12, boxShadow: "0 1px 2px rgba(9,30,66,.1)", borderLeft: `3px solid ${isT ? C.blue : "transparent"}` }}>
          <div style={{ display: "flex", justifyContent: "space-between", marginBottom: de.length ? 8 : 0 }}><span style={{ fontSize: 13, fontWeight: 700, color: isT ? C.blue : C.ink }}>{WD[i]}, {d.toLocaleDateString("ru-RU", { day: "2-digit", month: "long" })}</span>{de.length > 0 && <span style={{ fontSize: 13, fontWeight: 800, color: net >= 0 ? C.darkgreen : C.red }}>{net >= 0 ? "+" : ""}{money(net)}</span>}</div>
          {de.map((e, j) => <div key={j} style={{ display: "flex", alignItems: "center", gap: 8, padding: "4px 0", fontSize: 13 }}><Dot c={e.type === "income" ? C.darkgreen : (e.planned ? C.orange : C.red)} /><span style={{ flex: 1 }}>{e.label}{e.planned && <span style={{ fontSize: 10, color: C.orange }}> · план</span>}</span><span style={{ fontWeight: 700, color: e.type === "income" ? C.darkgreen : C.red }}>{e.type === "income" ? "+" : "−"}{money(e.amount)}</span></div>)}
          {de.length === 0 && <Muted>—</Muted>}
        </div>
      ); })}
    </div>
  );
}
function OverviewView({ events }) {
  const from = new Date(TODAY.getFullYear(), TODAY.getMonth(), 1); const fut = events.filter((e) => parse(e.date) >= from);
  const byMonth = {}; fut.forEach((e) => { const d = parse(e.date); const k = `${d.getFullYear()}-${d.getMonth()}`; (byMonth[k] = byMonth[k] || []).push(e); });
  let bal = 20000000;
  return (
    <div style={{ display: "grid", gap: 12 }}>
      <Muted>Прогноз с {monLabel(from)}. Стартовый баланс условно 20 млн. MRR растёт по мере окончания акций.</Muted>
      {Object.entries(byMonth).map(([k, evs]) => { const [yy, mm] = k.split("-").map(Number); const inc = evs.filter((e) => e.type === "income").reduce((s, e) => s + e.amount, 0); const exp = evs.filter((e) => e.type === "expense").reduce((s, e) => s + e.amount, 0); bal += inc - exp; return (
        <div key={k} style={{ background: C.white, borderRadius: 12, padding: 14, boxShadow: "0 1px 3px rgba(9,30,66,.13)" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 10, flexWrap: "wrap", gap: 8 }}><span style={{ fontSize: 15, fontWeight: 800, textTransform: "capitalize" }}>{new Date(yy, mm, 1).toLocaleDateString("ru-RU", { month: "long", year: "numeric" })}</span><span style={{ display: "flex", gap: 14, fontSize: 12.5 }}><span style={{ color: C.darkgreen, fontWeight: 700 }}>+{short(inc)}</span><span style={{ color: C.red, fontWeight: 700 }}>−{short(exp)}</span><span style={{ fontWeight: 700, color: inc - exp >= 0 ? C.darkgreen : C.red }}>нетто {short(inc - exp)}</span><span style={{ fontWeight: 800, color: C.blue }}>баланс {short(bal)}</span></span></div>
          {evs.sort((a, b) => parse(a.date) - parse(b.date)).map((e, j) => <div key={j} style={{ display: "flex", alignItems: "center", gap: 8, padding: "4px 0", fontSize: 12.5, borderTop: j ? `1px solid ${C.line}` : "none" }}><span style={{ width: 50, color: C.sub }}>{dshort(e.date)}</span><Dot c={e.type === "income" ? C.darkgreen : (e.planned ? C.orange : C.red)} /><span style={{ flex: 1 }}>{e.label}</span><span style={{ fontWeight: 700, color: e.type === "income" ? C.darkgreen : C.red }}>{e.type === "income" ? "+" : "−"}{money(e.amount)}</span></div>)}
        </div>
      ); })}
    </div>
  );
}

// ===== helpers =====
function weekDays(anchor) { const dow = (anchor.getDay() + 6) % 7; const start = new Date(anchor); start.setDate(anchor.getDate() - dow); return Array.from({ length: 7 }, (_, i) => { const d = new Date(start); d.setDate(start.getDate() + i); return d; }); }
function Modal({ onClose, color, title, titleEl, sub, extra, children }) {
  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(9,30,66,.55)", display: "grid", placeItems: "start center", zIndex: 50, padding: 16, overflowY: "auto" }}>
      <div onClick={(e) => e.stopPropagation()} style={{ background: C.bg, borderRadius: 14, width: 720, maxWidth: "100%", margin: "16px 0", boxShadow: "0 20px 60px rgba(0,0,0,.35)" }}>
        <div style={{ background: color, padding: "16px 20px", borderRadius: "14px 14px 0 0", display: "flex", alignItems: "center", gap: 12 }}><div style={{ flex: 1 }}>{titleEl || <div style={{ fontSize: 18, fontWeight: 800, color: C.white }}>{title}</div>}<div style={{ fontSize: 12, color: "rgba(255,255,255,.9)" }}>{sub}</div></div>{extra}<button onClick={onClose} style={{ border: "none", background: "rgba(255,255,255,.25)", color: C.white, width: 32, height: 32, borderRadius: 8, cursor: "pointer" }}>✕</button></div>
        <div style={{ padding: 18, display: "grid", gap: 14 }}>{children}</div>
      </div>
    </div>
  );
}
function NumInput({ value, onChange, suffix, accent, decimal, w }) {
  const display = value === "" || value == null ? "" : (decimal ? nfU.format(value) : nf0.format(Math.round(value)));
  const handle = (e) => { const raw = e.target.value.replace(/\s/g, "").replace(/[^\d.,]/g, "").replace(",", "."); const n = decimal ? parseFloat(raw) : parseInt(raw, 10); onChange(isNaN(n) ? 0 : n); };
  return <div style={{ display: "flex", alignItems: "center", background: C.white, border: `1px solid ${C.line}`, borderRadius: 6, overflow: "hidden", width: w, boxSizing: "border-box" }}><input type="text" inputMode="numeric" value={display} onChange={handle} style={{ flex: 1, border: "none", outline: "none", padding: "8px 10px", fontSize: 13, fontWeight: 700, color: accent || C.ink, background: "transparent", textAlign: "right", width: "100%" }} />{suffix && <span style={{ fontSize: 10.5, color: C.sub, padding: "0 8px", whiteSpace: "nowrap" }}>{suffix}</span>}</div>;
}
const TF = ({ label, value, on, wide }) => <div style={{ gridColumn: wide ? "1 / -1" : "auto" }}><Label2>{label}</Label2><input value={value || ""} onChange={(e) => on(e.target.value)} style={{ ...txt, width: "100%", boxSizing: "border-box" }} /></div>;
const DFf = ({ label, value, on, warn }) => <div><Label2>{label}</Label2><input type="date" value={value || ""} onChange={(e) => on(e.target.value)} style={{ ...txt, width: "100%", boxSizing: "border-box", borderColor: warn ? C.red : C.line, color: warn ? C.red : C.ink }} /></div>;
const DF = ({ value, on }) => <input type="date" value={value || ""} onChange={(e) => on(e.target.value)} style={{ ...txt }} />;
const Xb = ({ on, pad }) => <button onClick={on} style={{ border: "none", background: "transparent", color: C.sub, cursor: "pointer", fontSize: 16, paddingBottom: pad ? 6 : 0 }}>✕</button>;
const Grid = ({ children }) => <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, marginBottom: 4 }}>{children}</div>;
const H = ({ children }) => <div style={{ fontSize: 21, fontWeight: 800 }}>{children}</div>;
const Block = ({ title, children }) => <div style={{ background: C.white, borderRadius: 12, padding: 16, boxShadow: "0 1px 3px rgba(9,30,66,.13)" }}><div style={{ fontSize: 14, fontWeight: 800, marginBottom: 12 }}>{title}</div>{children}</div>;
const Label2 = ({ children }) => <div style={{ fontSize: 10.5, color: C.sub, fontWeight: 600, marginBottom: 3 }}>{children}</div>;
const Muted = ({ children, style }) => <div style={{ fontSize: 12, color: C.sub, marginTop: 8, ...style }}>{children}</div>;
const Stat = ({ l, v, c, sub, valueRaw }) => <div style={{ background: C.white, borderRadius: 12, padding: 16, boxShadow: "0 1px 3px rgba(9,30,66,.13)", borderTop: `3px solid ${c}` }}><div style={{ fontSize: 12, color: C.sub, fontWeight: 600 }}>{l}</div><div style={{ fontSize: 20, fontWeight: 800, color: c, margin: "4px 0 2px" }}>{valueRaw ? v : short(v)} {!valueRaw && <span style={{ fontSize: 10.5, color: C.sub }}>сум</span>}</div><div style={{ fontSize: 10.5, color: "#8993a4" }}>{sub}</div></div>;
const Badge = ({ c, children }) => <span style={{ fontSize: 11, fontWeight: 600, color: c, background: c + "1f", padding: "2px 9px", borderRadius: 10, whiteSpace: "nowrap" }}>{children}</span>;
const Dot = ({ c }) => <span style={{ width: 9, height: 9, borderRadius: "50%", background: c, display: "inline-block", flexShrink: 0 }} />;
const navBtn = { border: "none", background: "#ebecf0", color: C.sub, width: 24, height: 20, borderRadius: 5, cursor: "pointer", fontSize: 9 };
const navBtn2 = { border: "none", background: C.white, color: C.ink, width: 32, height: 32, borderRadius: 8, cursor: "pointer", fontSize: 13, boxShadow: "0 1px 2px rgba(9,30,66,.13)" };
const txt = { border: `1px solid ${C.line}`, borderRadius: 6, padding: "8px 10px", fontSize: 13, fontWeight: 600, color: C.ink, outline: "none" };
const sel = { border: `1px solid ${C.line}`, borderRadius: 6, padding: "8px 10px", fontSize: 13.5, fontWeight: 600, color: C.ink, background: C.white, minWidth: 150 };
const addBtn = { fontSize: 13, fontWeight: 600, color: C.blue, background: "#eef6ff", border: "none", borderRadius: 6, padding: "7px 14px", cursor: "pointer" };

// ============ ДОКУМЕНТЫ ============
const nowStr = () => new Date().toLocaleString("ru-RU", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
const dlong = (s) => s ? parse(s).toLocaleDateString("ru-RU", { day: "2-digit", month: "long", year: "numeric" }) : "—";
const OUR = { name: "ООО «LORA GATE»", inn: "312412801", address: "г. Ташкент, Мирзо Улугбек 52Б", director: "Рахимов А. А.", bank: "Капиталбанк, ф-л Мирзо Улугбек", account: "2020 8000 9004 4933 4001", mfo: "01018" };
const BANK = { 1: { bank: "Ипотека Банк", account: "2020 8000 1234 5678 9001", mfo: "00491" }, 2: { bank: "Hamkorbank", account: "2020 8000 9988 7766 5001", mfo: "00873" }, 3: { bank: "Asia Alliance Bank", account: "2020 8000 4455 6677 8001", mfo: "01095" } };
const bankOf = (id) => BANK[id] || { bank: "—", account: "—", mfo: "—" };
const DSTATUS = { draft: { l: "Черновик", c: C.sub }, sent: { l: "Отправлен", c: C.blue }, signed_client: { l: "Подписан клиентом", c: C.teal }, signed_us: { l: "Подписан нами", c: C.purple }, done: { l: "Завершён", c: C.darkgreen }, rejected: { l: "Отклонён", c: C.red } };
const CHAIN = ["КП", "Договор", "Счёт", "Счёт-фактура", "Акт"];
const SIGNED = ["signed_client", "signed_us", "done"];
const goods = () => [...CATALOG.map((p) => ({ name: p.name, price: p.listPrice })), { name: "Монтаж и калибровка", price: 500000 }, { name: "Абонентское обслуживание (мес)", price: 150000 }];
const TYPES = { "КП": { title: "КОММЕРЧЕСКОЕ ПРЕДЛОЖЕНИЕ", prefix: "КП-", vat: false, body: "offer" }, "Договор": { title: "ДОГОВОР ПОСТАВКИ", prefix: "ДГ-2026/", vat: false, body: "contract" }, "Счёт": { title: "СЧЁТ НА ОПЛАТУ", prefix: "Счёт №", vat: true }, "Счёт-фактура": { title: "СЧЁТ-ФАКТУРА", prefix: "СФ-", vat: true }, "Спецификация": { title: "СПЕЦИФИКАЦИЯ", prefix: "СП-" }, "Акт": { title: "АКТ приёма-передачи", prefix: "Акт-", body: "act" } };
const TYPE_LIST = Object.keys(TYPES);
const dF = (name, version, by, at, size) => ({ id: name + version + at + Math.random(), name, version, by, at, size });
const dD = (type, status, didox, files) => ({ type, status, didox, files });
const draftSub = (dr) => dr.items.reduce((s, i) => s + i.qty * i.price, 0);
const draftVat = (dr) => (TYPES[dr.type].vat ? draftSub(dr) * 0.12 : 0);
const draftTotal = (dr) => draftSub(dr) + draftVat(dr);
const oSub = (o) => o.items.reduce((s, i) => s + i.qty * i.price, 0);
const oTotal = (o) => oSub(o) * (1 - (o.discountPct || 0) / 100);

const seedDeals = [
  { id: 1, clientId: 1, branch: "Филиал Чиланзар", title: "Поставка датчиков температуры", amount: 8075000, paid: 8075000, paymentDue: "2026-02-24", docs: [dD("КП", "done", false, [dF("КП_Фарм.pdf", 1, "Алишер", "20.01.26", 240)]), dD("Договор", "done", false, [dF("Договор_ДГ-2025-14.pdf", 2, "Алишер", "25.01.26", 520), dF("Договор_ДГ-2025-14.pdf", 1, "Алишер", "22.01.26", 515)]), dD("Счёт", "done", true, [dF("Счёт_118.pdf", 1, "Дилшод", "01.02.26", 180)]), dD("Счёт-фактура", "done", true, [dF("СФ_0231.pdf", 1, "Дилшод", "10.02.26", 190)]), dD("Акт", "done", true, [dF("Акт_077.pdf", 1, "Дилшод", "12.02.26", 170)])] },
  { id: 2, clientId: 2, branch: null, title: "Оснащение центрального склада", amount: 38000000, paid: 0, paymentDue: "2026-06-10", docs: [dD("КП", "done", false, [dF("КП_MedLogistic.pdf", 1, "Алишер", "10.05.26", 260)]), dD("Договор", "signed_us", false, [dF("Договор_проект.pdf", 1, "Алишер", "22.05.26", 540)]), dD("Счёт", "sent", true, [dF("Счёт_205.pdf", 1, "Дилшод", "25.05.26", 185)]), dD("Счёт-фактура", "draft", false, []), dD("Акт", "draft", false, [])] },
  { id: 3, clientId: 3, branch: "Склад Зангиата", title: "Поставка шлюзов LoRa", amount: 4400000, paid: 4400000, paymentDue: "2026-06-01", docs: [dD("КП", "done", false, []), dD("Договор", "done", false, [dF("Договор_ДГ-2026-02.pdf", 1, "Алишер", "12.01.26", 500)]), dD("Счёт", "done", true, [dF("Счёт_210.pdf", 1, "Дилшод", "28.05.26", 180)]), dD("Счёт-фактура", "signed_us", true, [dF("СФ_0240.pdf", 1, "Дилшод", "01.06.26", 195)]), dD("Акт", "sent", true, [dF("Акт_проект.pdf", 1, "Дилшод", "02.06.26", 160)])] },
  { id: 4, clientId: 1, branch: "Филиал Чиланзар", title: "Мониторинг филиалов (логгеры)", amount: 12600000, paid: 6000000, paymentDue: "2026-04-19", docs: [dD("КП", "done", false, []), dD("Договор", "done", false, []), dD("Счёт", "done", true, [dF("Счёт_119.pdf", 1, "Дилшод", "05.04.26", 180)]), dD("Счёт-фактура", "done", true, [dF("СФ_0235.pdf", 1, "Дилшод", "05.04.26", 190)]), dD("Акт", "done", true, [dF("Акт_080.pdf", 1, "Дилшод", "10.04.26", 170)])] },
];
const seedDrafts = [
  { id: 1, clientId: 2, type: "Счёт", number: "Счёт №205", date: "2026-05-25", status: "draft", createdAt: "25.05.26 10:12", items: [{ name: "Датчик температуры TZ-BT04", qty: 20, price: 820000 }, { name: "Логгер влажности TZ-RH10", qty: 10, price: 1050000 }] },
  { id: 2, clientId: 1, type: "Договор", number: "ДГ-2026/14", date: "2026-06-04", status: "draft", createdAt: "04.06.26 16:40", items: [{ name: "Логгер влажности TZ-RH10", qty: 4, price: 1050000 }] },
  { id: 3, clientId: 3, type: "Счёт-фактура", number: "СФ-0240", date: "2026-06-01", status: "didox", createdAt: "01.06.26 09:30", items: [{ name: "Шлюз LoRa Gateway TZ-G1", qty: 4, price: 1100000 }] },
];

function dealStage(deal) { return CHAIN.find((t) => { const doc = deal.docs.find((x) => x.type === t); return !doc || doc.status !== "done"; }) || null; }
function dealRisks(deal, clients) { const r = []; const c = clients.find((x) => x.id === deal.clientId); const akt = deal.docs.find((x) => x.type === "Акт"); const sf = deal.docs.find((x) => x.type === "Счёт-фактура"); if (akt && akt.status !== "done" && sf && SIGNED.includes(sf.status)) r.push("Не подписан акт"); if (deal.paid < deal.amount - 0.5 && parse(deal.paymentDue) < TODAY) r.push("Просрочка оплаты"); if (c && expired(c.directorOrderValid)) r.push("Истёк приказ директора"); return r; }
function payStatus(deal) { const akt = deal.docs.find((x) => x.type === "Акт"); if (deal.paid >= deal.amount - 0.5 && akt && akt.status === "done") return { l: "Завершено · акт подписан", c: C.darkgreen }; if (deal.paid >= deal.amount - 0.5) return { l: "Оплачено", c: C.green }; if (deal.paid > 0) return { l: parse(deal.paymentDue) < TODAY ? "Частично · просрочка" : "Частичная оплата", c: parse(deal.paymentDue) < TODAY ? C.red : C.orange }; return { l: parse(deal.paymentDue) < TODAY ? "Просрочка оплаты" : "Ожидаем оплату", c: parse(deal.paymentDue) < TODAY ? C.red : C.orange }; }

function Documents({ clients }) {
  const [sub, setSub] = useState("drafts");
  const [drafts, setDrafts] = usePersistentCollection("drafts", seedDrafts);
  const [deals, setDeals] = usePersistentCollection("deals", seedDeals);
  return (
    <div style={{ display: "grid", gap: 14 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
        <H>Документы</H>
        <div style={{ display: "flex", gap: 4, background: "#e4e6ea", borderRadius: 8, padding: 3 }}>
          {[["drafts", "Черновики"], ["deals", "Документооборот"]].map(([k, l]) => <button key={k} onClick={() => setSub(k)} style={{ border: "none", borderRadius: 6, padding: "6px 14px", cursor: "pointer", fontSize: 13, fontWeight: 600, background: sub === k ? C.white : "transparent", color: sub === k ? C.ink : C.sub }}>{l}</button>)}
        </div>
      </div>
      {sub === "drafts" && <DraftsView clients={clients} drafts={drafts} setDrafts={setDrafts} />}
      {sub === "deals" && <DocFlowView clients={clients} deals={deals} setDeals={setDeals} />}
    </div>
  );
}

function DraftsView({ clients, drafts, setDrafts }) {
  const [open, setOpen] = useState(null);
  const [d, setD] = useState({ clientId: clients[0]?.id, type: "Счёт", number: "", date: TODAYS, items: [{ name: goods()[0].name, qty: 1, price: goods()[0].price }] });
  const oc = open != null ? drafts.find((x) => x.id === open) : null;
  const suggestNo = (type) => TYPES[type].prefix + (100 + drafts.length + 1);
  const setLine = (i, f, v) => setD((p) => ({ ...p, items: p.items.map((l, j) => (j === i ? { ...l, [f]: v } : l)) }));
  const pickGood = (i, name) => { const g = goods().find((x) => x.name === name); setD((p) => ({ ...p, items: p.items.map((l, j) => (j === i ? { ...l, name, price: g ? g.price : l.price } : l)) })); };
  const create = () => { if (!d.items.length) return; const id = Date.now(); setDrafts((ds) => [{ id, clientId: d.clientId, type: d.type, number: d.number || suggestNo(d.type), date: d.date, status: "draft", createdAt: nowStr(), items: d.items.map((l) => ({ ...l })) }, ...ds]); setD((p) => ({ ...p, number: "", items: [{ name: goods()[0].name, qty: 1, price: goods()[0].price }] })); setOpen(id); };
  const toDidox = (id) => setDrafts((ds) => ds.map((x) => (x.id === id ? { ...x, status: "didox" } : x)));
  const del = (id) => setDrafts((ds) => ds.filter((x) => x.id !== id));
  const unsigned = drafts.filter((x) => x.status === "draft"); const sent = drafts.filter((x) => x.status === "didox");
  const sub = draftSub(d), vat = TYPES[d.type].vat ? sub * 0.12 : 0;
  return (
    <div style={{ display: "grid", gap: 14 }}>
      <Block title="Создать документ по шаблону">
        <div style={{ display: "flex", flexWrap: "wrap", gap: 12, marginBottom: 12 }}>
          <div><Label2>Клиент</Label2><select value={d.clientId} onChange={(e) => setD((p) => ({ ...p, clientId: +e.target.value }))} style={sel}>{clients.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}</select></div>
          <div><Label2>Тип документа</Label2><select value={d.type} onChange={(e) => setD((p) => ({ ...p, type: e.target.value, number: "" }))} style={sel}>{TYPE_LIST.map((t) => <option key={t} value={t}>{t}</option>)}</select></div>
          <div><Label2>Номер</Label2><input value={d.number} onChange={(e) => setD((p) => ({ ...p, number: e.target.value }))} placeholder={suggestNo(d.type)} style={{ ...txt, width: 150, boxSizing: "border-box" }} /></div>
          <div><Label2>Дата</Label2><input type="date" value={d.date} onChange={(e) => setD((p) => ({ ...p, date: e.target.value }))} style={txt} /></div>
        </div>
        <div style={{ fontSize: 11, color: C.sub, fontWeight: 700, marginBottom: 6, textTransform: "uppercase" }}>Позиции / услуги</div>
        {d.items.map((l, i) => (
          <div key={i} style={{ display: "flex", gap: 8, alignItems: "flex-end", marginBottom: 8, flexWrap: "wrap" }}>
            <div style={{ flex: 1, minWidth: 200 }}><select value={l.name} onChange={(e) => pickGood(i, e.target.value)} style={{ ...sel, width: "100%" }}>{goods().map((g) => <option key={g.name} value={g.name}>{g.name}</option>)}{!goods().some((g) => g.name === l.name) && <option value={l.name}>{l.name}</option>}</select></div>
            <div><Label2>Кол-во</Label2><NumInput value={l.qty} onChange={(v) => setLine(i, "qty", v)} w={80} /></div>
            <div><Label2>Цена/шт</Label2><NumInput value={l.price} onChange={(v) => setLine(i, "price", v)} suffix="сум" w={150} decimal /></div>
            <span style={{ width: 100, textAlign: "right", fontSize: 13, fontWeight: 700, paddingBottom: 8 }}>{short(l.qty * l.price)}</span>
            <button onClick={() => setD((p) => ({ ...p, items: p.items.filter((_, j) => j !== i) }))} style={{ border: "none", background: "transparent", color: C.sub, cursor: "pointer", fontSize: 16, paddingBottom: 6 }}>✕</button>
          </div>
        ))}
        <button onClick={() => setD((p) => ({ ...p, items: [...p.items, { name: goods()[0].name, qty: 1, price: goods()[0].price }] }))} style={{ fontSize: 13, fontWeight: 600, color: C.blue, background: "transparent", border: "none", cursor: "pointer", padding: "2px 0 10px" }}>+ Позиция</button>
        <div style={{ display: "flex", alignItems: "center", gap: 16, borderTop: `1px solid ${C.line}`, paddingTop: 12 }}>
          <span style={{ fontSize: 13, color: C.sub }}>Сумма: <b style={{ color: C.ink }}>{money(sub)}</b>{vat > 0 && <> · НДС 12% {money(vat)} · итого <b style={{ color: C.darkgreen }}>{money(sub + vat)}</b></>}</span>
          <button onClick={create} style={{ marginLeft: "auto", background: C.darkgreen, color: C.white, border: "none", borderRadius: 8, padding: "10px 20px", fontWeight: 700, cursor: "pointer", fontSize: 14 }}>Создать черновик</button>
        </div>
      </Block>
      <div style={{ fontSize: 15, fontWeight: 800 }}>Неподписанные черновики ({unsigned.length})</div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(300px,1fr))", gap: 12 }}>
        {unsigned.length === 0 && <Muted>Черновиков нет.</Muted>}
        {unsigned.map((dr) => <DraftCard key={dr.id} dr={dr} clients={clients} onOpen={() => setOpen(dr.id)} onDidox={() => toDidox(dr.id)} onDel={() => del(dr.id)} />)}
      </div>
      {sent.length > 0 && <>
        <div style={{ fontSize: 15, fontWeight: 800 }}>Отправлено в Didox ({sent.length})</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(300px,1fr))", gap: 12 }}>{sent.map((dr) => <DraftCard key={dr.id} dr={dr} clients={clients} onOpen={() => setOpen(dr.id)} onDel={() => del(dr.id)} sent />)}</div>
      </>}
      {oc && <DocModal dr={oc} clients={clients} onClose={() => setOpen(null)} onDidox={() => toDidox(oc.id)} />}
    </div>
  );
}

function DraftCard({ dr, clients, onOpen, onDidox, onDel, sent }) {
  const c = clients.find((x) => x.id === dr.clientId);
  return (
    <div style={{ background: C.white, borderRadius: 12, padding: 14, boxShadow: "0 1px 3px rgba(9,30,66,.13)", borderTop: `3px solid ${sent ? "#0094b3" : C.sub}` }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 6 }}>
        <div><div style={{ fontSize: 14, fontWeight: 800 }}>{dr.type}</div><div style={{ fontSize: 11.5, color: C.sub }}>{dr.number} · {dshort(dr.date)}</div></div>
        {sent ? <Badge c="#0094b3">✓ В Didox</Badge> : <Badge c={C.sub}>Не подписан</Badge>}
      </div>
      <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 4 }}>{c?.name}</div>
      <div style={{ fontSize: 13, fontWeight: 800, color: C.darkgreen, marginBottom: 10 }}>{money(draftTotal(dr))} сум</div>
      <div style={{ display: "flex", gap: 6 }}>
        <button onClick={onOpen} style={{ fontSize: 12.5, fontWeight: 600, color: C.blue, background: "#eef6ff", border: "none", borderRadius: 6, padding: "6px 12px", cursor: "pointer" }}>Открыть</button>
        {!sent && <button onClick={onDidox} style={{ fontSize: 12.5, fontWeight: 700, color: C.white, background: "#0094b3", border: "none", borderRadius: 6, padding: "6px 12px", cursor: "pointer" }}>↑ В Didox</button>}
        <button onClick={onDel} style={{ fontSize: 12.5, color: C.sub, background: "transparent", border: "none", cursor: "pointer", marginLeft: "auto" }}>Удалить</button>
      </div>
    </div>
  );
}

function DocModal({ dr, clients, onClose, onDidox }) {
  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(9,30,66,.6)", display: "grid", placeItems: "start center", zIndex: 50, padding: 16, overflowY: "auto" }}>
      <div onClick={(e) => e.stopPropagation()} style={{ width: 760, maxWidth: "100%", margin: "16px 0" }}>
        <div style={{ display: "flex", gap: 8, marginBottom: 10 }}>
          {dr.status !== "didox" && <button onClick={onDidox} style={{ background: "#0094b3", color: C.white, border: "none", borderRadius: 8, padding: "10px 18px", fontWeight: 700, cursor: "pointer", fontSize: 14 }}>↑ Отправить в Didox</button>}
          {dr.status === "didox" && <span style={{ background: "#e3f7fb", color: "#0094b3", borderRadius: 8, padding: "10px 18px", fontWeight: 700, fontSize: 14 }}>✓ Отправлен в Didox · ожидает подписания</span>}
          <button onClick={onClose} style={{ marginLeft: "auto", background: C.white, color: C.ink, border: "none", borderRadius: 8, padding: "10px 16px", fontWeight: 700, cursor: "pointer" }}>Закрыть</button>
        </div>
        <DocPreview dr={dr} clients={clients} />
      </div>
    </div>
  );
}

function DocPreview({ dr, clients }) {
  const c = clients.find((x) => x.id === dr.clientId); const t = TYPES[dr.type]; const cb = bankOf(dr.clientId);
  const sub = draftSub(dr), vat = draftVat(dr), total = draftTotal(dr);
  const Req = ({ title, name, inn, address, bank }) => (<div style={{ flex: 1 }}><div style={{ fontSize: 11, fontWeight: 700, color: "#888", marginBottom: 4 }}>{title}</div><div style={{ fontSize: 12.5, fontWeight: 700 }}>{name}</div><div style={{ fontSize: 11.5, color: "#444", lineHeight: 1.5 }}>ИНН {inn} · {address}{bank ? <><br />{bank.bank}, р/с {bank.account}, МФО {bank.mfo}</> : null}</div></div>);
  return (
    <div style={{ background: C.white, borderRadius: 8, padding: "34px 40px", fontFamily: "Georgia, 'Times New Roman', serif", color: "#1a1a1a", boxShadow: "0 10px 40px rgba(0,0,0,.25)" }}>
      <div style={{ display: "flex", gap: 30, paddingBottom: 16, borderBottom: "2px solid #1a1a1a", marginBottom: 18 }}>
        <Req title="ПОСТАВЩИК" name={OUR.name} inn={OUR.inn} address={OUR.address} bank={{ bank: OUR.bank, account: OUR.account, mfo: OUR.mfo }} />
        <Req title="ПОКУПАТЕЛЬ" name={c?.name} inn={c?.inn} address={c?.address} bank={cb} />
      </div>
      <div style={{ textAlign: "center", marginBottom: 4 }}><div style={{ fontSize: 19, fontWeight: 800, letterSpacing: .5 }}>{t.title}</div><div style={{ fontSize: 13, color: "#444", marginTop: 4 }}>{dr.number} от {dlong(dr.date)}</div></div>
      {t.body === "contract" && <p style={{ fontSize: 13, lineHeight: 1.7, marginTop: 16 }}>г. Ташкент. {OUR.name}, именуемое «Поставщик», в лице директора {OUR.director}, и {c?.name}, именуемое «Покупатель», в лице директора {c?.director || "—"}, заключили настоящий договор о поставке оборудования для систем мониторинга на сумму <b>{money(total)} сум</b>. Срок поставки — 30 рабочих дней, оплата — в течение 10 банковских дней.</p>}
      {t.body === "offer" && <p style={{ fontSize: 13, lineHeight: 1.7, marginTop: 16 }}>Уважаемый(ая) {c?.director || "клиент"}! {OUR.name} предлагает {c?.name} поставку и обслуживание оборудования для мониторинга температуры и влажности на следующих условиях. Предложение действительно 15 дней.</p>}
      {t.body === "act" && <p style={{ fontSize: 13, lineHeight: 1.7, marginTop: 16 }}>Настоящим актом {OUR.name} и {c?.name} подтверждают, что Поставщик передал, а Покупатель принял перечисленное оборудование на сумму <b>{money(total)} сум</b>. Стороны взаимных претензий не имеют.</p>}
      <table style={{ width: "100%", borderCollapse: "collapse", marginTop: 18, fontSize: 12.5 }}>
        <thead><tr style={{ background: "#f0f0f0" }}><th style={thd}>№</th><th style={{ ...thd, textAlign: "left" }}>Наименование</th><th style={thd}>Кол-во</th><th style={{ ...thd, textAlign: "right" }}>Цена</th><th style={{ ...thd, textAlign: "right" }}>Сумма</th></tr></thead>
        <tbody>{dr.items.map((it, i) => <tr key={i}><td style={tdd}>{i + 1}</td><td style={{ ...tdd, textAlign: "left" }}>{it.name}</td><td style={{ ...tdd, textAlign: "center" }}>{it.qty}</td><td style={{ ...tdd, textAlign: "right" }}>{fmt(it.price)}</td><td style={{ ...tdd, textAlign: "right", fontWeight: 700 }}>{fmt(it.qty * it.price)}</td></tr>)}</tbody>
      </table>
      <div style={{ display: "flex", justifyContent: "flex-end", marginTop: 10 }}><div style={{ minWidth: 260, fontSize: 13 }}>
        <PRow l="Итого" v={fmt(sub)} />{t.vat && <PRow l="НДС 12%" v={fmt(vat)} />}
        <div style={{ display: "flex", justifyContent: "space-between", borderTop: "2px solid #1a1a1a", paddingTop: 6, marginTop: 4 }}><b>Всего к оплате</b><b>{money(total)} сум</b></div>
      </div></div>
      <div style={{ display: "flex", gap: 40, marginTop: 40, fontSize: 12.5 }}>
        <div style={{ flex: 1 }}><div style={{ color: "#888", marginBottom: 28 }}>Поставщик</div><div style={{ borderTop: "1px solid #1a1a1a", paddingTop: 4 }}>{OUR.director} ______ <span style={{ color: "#888" }}>М.П.</span></div></div>
        <div style={{ flex: 1 }}><div style={{ color: "#888", marginBottom: 28 }}>Покупатель</div><div style={{ borderTop: "1px solid #1a1a1a", paddingTop: 4 }}>{c?.director || ""} ______ <span style={{ color: "#888" }}>М.П.</span></div></div>
      </div>
    </div>
  );
}

function DocFlowView({ clients, deals, setDeals }) {
  const [open, setOpen] = useState(null);
  const oc = open != null ? deals.find((x) => x.id === open) : null;
  const setDeal = (id, fn) => setDeals((ds) => ds.map((x) => (x.id === id ? fn(x) : x)));
  const total = deals.reduce((s, x) => s + x.amount, 0);
  const done = deals.filter((x) => !dealStage(x)).reduce((s, x) => s + x.amount, 0);
  const awaiting = deals.filter((x) => x.paid < x.amount - 0.5).reduce((s, x) => s + (x.amount - x.paid), 0);
  const riskN = deals.filter((x) => dealRisks(x, clients).length).length;
  const dealRiskList = deals.map((x) => ({ deal: x, risks: dealRisks(x, clients) })).filter((x) => x.risks.length);
  const docRiskList = []; clients.forEach((c) => { if (c.contractValid && daysUntil(c.contractValid) <= 60) docRiskList.push({ label: `Договор: ${c.name}`, date: c.contractValid, sev: expired(c.contractValid) ? "red" : "orange" }); }); deals.forEach((dl) => { const akt = dl.docs.find((x) => x.type === "Акт"); if (akt && akt.status !== "done") docRiskList.push({ label: `Не подписан акт: ${clients.find((c) => c.id === dl.clientId)?.name}`, date: dl.paymentDue, sev: "orange" }); });
  docRiskList.sort((a, b) => parse(a.date) - parse(b.date));
  return (
    <div style={{ display: "grid", gap: 14 }}>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(165px,1fr))", gap: 12 }}>
        <Stat l="Сделок на сумму" v={total} c={C.blue} sub={`${deals.length} сделок`} />
        <Stat l="Завершено" v={done} c={C.darkgreen} sub="документы закрыты" />
        <Stat l="Ожидаем оплату" v={awaiting} c={C.red} sub="не поступило" />
        <Stat l="Под риском" v={riskN} c={riskN ? C.red : C.green} sub="требуют внимания" valueRaw />
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
        <Block title="🚨 Сделки под риском">
          {dealRiskList.length === 0 ? <Muted>Рисков нет.</Muted> : dealRiskList.map(({ deal, risks }) => (<div key={deal.id} onClick={() => setOpen(deal.id)} style={{ padding: "9px 4px", borderBottom: `1px solid ${C.line}`, cursor: "pointer" }}><div style={{ display: "flex", justifyContent: "space-between", fontSize: 13, marginBottom: 4 }}><span style={{ fontWeight: 700 }}>{clients.find((c) => c.id === deal.clientId)?.name}</span><span style={{ color: C.sub }}>{short(deal.amount)} сум</span></div><div style={{ display: "flex", flexWrap: "wrap", gap: 5 }}>{risks.map((rr, i) => <Badge key={i} c={C.red}>{rr}</Badge>)}</div></div>))}
        </Block>
        <Block title="📄 Документы под риском">
          {docRiskList.length === 0 ? <Muted>Всё в порядке.</Muted> : docRiskList.map((x, i) => <div key={i} style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 4px", borderBottom: `1px solid ${C.line}`, fontSize: 13 }}><Dot c={x.sev === "red" ? C.red : C.orange} /><span style={{ flex: 1 }}>{x.label}</span><span style={{ fontSize: 11, color: x.sev === "red" ? C.red : C.sub, fontWeight: 600 }}>{dshort(x.date)}</span></div>)}
        </Block>
      </div>
      <div style={{ fontSize: 15, fontWeight: 800 }}>Сделки и цепочки документов</div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(330px,1fr))", gap: 12 }}>
        {deals.map((deal) => { const stage = dealStage(deal); const ps = payStatus(deal); const c = clients.find((x) => x.id === deal.clientId); return (
          <div key={deal.id} onClick={() => setOpen(deal.id)} style={{ background: C.white, borderRadius: 12, padding: 14, boxShadow: "0 1px 3px rgba(9,30,66,.13)", cursor: "pointer", borderTop: `3px solid ${stage ? C.orange : C.darkgreen}` }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 4 }}><div><div style={{ fontSize: 14, fontWeight: 800 }}>{c?.name}</div><div style={{ fontSize: 11.5, color: C.sub }}>{deal.title}{deal.branch ? ` · ${deal.branch}` : ""}</div></div><span style={{ fontSize: 15, fontWeight: 800, whiteSpace: "nowrap" }}>{short(deal.amount)}</span></div>
            <Stepper deal={deal} />
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 8, fontSize: 12 }}><span style={{ color: C.sub }}>{stage ? <>Этап: <b style={{ color: C.orange }}>{stage}</b></> : <b style={{ color: C.darkgreen }}>Завершена</b>}</span><Badge c={ps.c}>{ps.l}</Badge></div>
          </div>
        ); })}
      </div>
      {oc && <DealModal deal={oc} clients={clients} setDeal={setDeal} onClose={() => setOpen(null)} />}
    </div>
  );
}

function Stepper({ deal }) {
  return (<div style={{ display: "flex", gap: 3, marginTop: 8 }}>{CHAIN.map((t) => { const doc = deal.docs.find((x) => x.type === t); const st = doc ? DSTATUS[doc.status] : DSTATUS.draft; return (<div key={t} style={{ flex: 1, textAlign: "center" }} title={`${t}: ${st.l}`}><div style={{ height: 5, borderRadius: 3, background: st.c, opacity: doc && doc.status !== "draft" ? 1 : .25 }} /><div style={{ fontSize: 8.5, color: C.sub, marginTop: 3, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{t}</div></div>); })}</div>);
}

function DealModal({ deal, clients, setDeal, onClose }) {
  const stage = dealStage(deal); const c = clients.find((x) => x.id === deal.clientId); const risks = dealRisks(deal, clients);
  const setDoc = (type, fn) => setDeal(deal.id, (x) => ({ ...x, docs: x.docs.map((dd) => (dd.type === type ? fn(dd) : dd)) }));
  const addFiles = (type, metas) => setDoc(type, (dd) => { const maxV = dd.files.reduce((m, ff) => Math.max(m, ff.version), 0); return { ...dd, files: [...metas.map((m, i) => ({ ...m, version: maxV + i + 1 })), ...dd.files] }; });
  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(9,30,66,.55)", display: "grid", placeItems: "start center", zIndex: 50, padding: 16, overflowY: "auto" }}>
      <div onClick={(e) => e.stopPropagation()} style={{ background: C.bg, borderRadius: 14, width: 760, maxWidth: "100%", margin: "16px 0", boxShadow: "0 20px 60px rgba(0,0,0,.35)" }}>
        <div style={{ background: stage ? C.orange : C.darkgreen, padding: "16px 20px", borderRadius: "14px 14px 0 0", display: "flex", alignItems: "center", gap: 12 }}><div style={{ flex: 1, color: C.white }}><div style={{ fontSize: 18, fontWeight: 800 }}>{c?.name} · {short(deal.amount)} сум</div><div style={{ fontSize: 12, opacity: .9 }}>{deal.title}{deal.branch ? ` · ${deal.branch}` : ""}</div></div><button onClick={onClose} style={{ border: "none", background: "rgba(255,255,255,.25)", color: C.white, width: 32, height: 32, borderRadius: 8, cursor: "pointer" }}>✕</button></div>
        <div style={{ padding: 18, display: "grid", gap: 14 }}>
          {risks.length > 0 && <div style={{ background: "#fff1ef", border: `1px solid ${C.red}`, borderRadius: 10, padding: "10px 14px", display: "flex", flexWrap: "wrap", gap: 8, alignItems: "center" }}><span style={{ fontSize: 13, fontWeight: 700, color: C.red }}>🚨 Под риском:</span>{risks.map((rr, i) => <Badge key={i} c={C.red}>{rr}</Badge>)}</div>}
          <Block title="Цепочка документов">
            {CHAIN.map((t, idx) => { const doc = deal.docs.find((x) => x.type === t) || { type: t, status: "draft", didox: false, files: [] }; const st = DSTATUS[doc.status]; return (
              <div key={t} style={{ border: `1px solid ${C.line}`, borderRadius: 10, padding: 12, marginBottom: 8 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
                  <span style={{ width: 22, height: 22, borderRadius: "50%", background: st.c, color: C.white, fontSize: 11, fontWeight: 700, display: "grid", placeItems: "center" }}>{idx + 1}</span>
                  <span style={{ fontSize: 14, fontWeight: 700, flex: 1 }}>{t}</span>
                  <select value={doc.status} onChange={(e) => setDoc(t, (dd) => ({ ...dd, status: e.target.value }))} style={{ ...sel, borderColor: st.c, color: st.c, fontWeight: 700, minWidth: 170 }}>{Object.entries(DSTATUS).map(([k, v]) => <option key={k} value={k}>{v.l}</option>)}</select>
                  <button onClick={() => setDoc(t, (dd) => ({ ...dd, didox: !dd.didox }))} style={{ border: "none", cursor: "pointer", borderRadius: 6, padding: "6px 10px", fontSize: 11.5, fontWeight: 700, background: doc.didox ? "#e3f7fb" : C.bg, color: doc.didox ? "#0094b3" : C.sub }}>{doc.didox ? "✓ Didox" : "↑ в Didox"}</button>
                </div>
                <FileArchive files={doc.files} onAdd={(m) => addFiles(t, m)} />
              </div>
            ); })}
          </Block>
          <div style={{ background: C.ink, borderRadius: 10, padding: 16, color: C.white }}>
            <div style={{ display: "flex", justifyContent: "space-between", fontSize: 13, marginBottom: 6 }}><span style={{ opacity: .85 }}>Сумма сделки</span><b>{fmt(deal.amount)} сум</b></div>
            <div style={{ display: "flex", justifyContent: "space-between", fontSize: 13, marginBottom: 6 }}><span style={{ opacity: .85 }}>Оплачено</span><b style={{ color: C.green }}>{fmt(deal.paid)} сум</b></div>
            <div style={{ display: "flex", justifyContent: "space-between", fontSize: 13, borderTop: "1px solid rgba(255,255,255,.2)", paddingTop: 6 }}><span style={{ opacity: .85 }}>Остаток · срок {dshort(deal.paymentDue)}</span><b style={{ color: deal.amount - deal.paid > 0 ? "#ffb3a8" : "#7bd87b" }}>{fmt(deal.amount - deal.paid)} сум</b></div>
          </div>
        </div>
      </div>
    </div>
  );
}

function FileArchive({ files, onAdd }) {
  const [drag, setDrag] = useState(false); const ref = useRef();
  const take = (list) => { if (!list || !list.length) return; onAdd(Array.from(list).map((file) => ({ name: file.name, by: "Вы (Алишер)", at: nowStr(), size: Math.max(1, Math.round((file.size || 102400) / 1024)) }))); };
  return (
    <div style={{ marginTop: 10, paddingLeft: 32 }}>
      <div onDragOver={(e) => { e.preventDefault(); setDrag(true); }} onDragLeave={() => setDrag(false)} onDrop={(e) => { e.preventDefault(); setDrag(false); take(e.dataTransfer.files); }} onClick={() => ref.current && ref.current.click()} style={{ border: `1.5px dashed ${drag ? C.blue : C.line}`, borderRadius: 8, padding: "10px 12px", textAlign: "center", fontSize: 12, color: drag ? C.blue : C.sub, cursor: "pointer", background: drag ? "#eef6ff" : "#fafbfc" }}>
        <input ref={ref} type="file" multiple style={{ display: "none" }} onChange={(e) => { take(e.target.files); e.target.value = ""; }} />
        📎 Перетащите оригинал сюда или нажмите для загрузки
      </div>
      {files.length > 0 && <div style={{ marginTop: 8 }}>{files.map((ff, i) => <div key={ff.id} style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 8px", borderRadius: 6, background: i === 0 ? "#f4fbf4" : "transparent", fontSize: 12 }}><span style={{ fontSize: 14 }}>📄</span><span style={{ flex: 1, fontWeight: 600, color: C.ink }}>{ff.name} <span style={{ fontSize: 10.5, color: C.sub }}>({ff.size} КБ)</span></span><Badge c={i === 0 ? C.darkgreen : C.sub}>v{ff.version}{i === 0 ? " · текущая" : ""}</Badge><span style={{ fontSize: 10.5, color: C.sub, whiteSpace: "nowrap" }}>{ff.by} · {ff.at}</span></div>)}</div>}
    </div>
  );
}

const PRow = ({ l, v }) => <div style={{ display: "flex", justifyContent: "space-between", padding: "2px 0" }}><span style={{ color: "#444" }}>{l}</span><span>{v} сум</span></div>;
const thd = { border: "1px solid #ccc", padding: "6px 8px", fontSize: 11.5, textAlign: "center", fontWeight: 500 };
const tdd = { border: "1px solid #ddd", padding: "6px 8px" };

// ===== ТОВАРЫ (каталог) =====
function Catalog({ products, setProducts }) {
  const [f, setF] = useState({ sku: "", name: "", listPrice: 0, serviceFee: 0 });
  const setRow = (i, k, v) => setProducts((ps) => ps.map((p, j) => (j === i ? { ...p, [k]: v } : p)));
  const add = () => { if (!f.sku || !f.name) { alert("Укажите код (SKU) и название товара."); return; } if (products.some((p) => p.sku === f.sku)) { alert("Товар с таким кодом уже есть."); return; } setProducts((ps) => [...ps, { sku: f.sku.trim(), name: f.name.trim(), listPrice: f.listPrice || 0, serviceFee: f.serviceFee || 0 }]); setF({ sku: "", name: "", listPrice: 0, serviceFee: 0 }); };
  const del = (sku) => { if (window.confirm("Удалить товар из каталога? На уже созданные приходы и заказы это не повлияет.")) setProducts((ps) => ps.filter((p) => p.sku !== sku)); };
  return (
    <div style={{ display: "grid", gap: 16 }}>
      <H>Товары (каталог)</H>
      <Block title="Каталог">
        <div style={{ display: "grid", gridTemplateColumns: "120px 1fr 150px 150px 40px", gap: 8, fontSize: 10.5, color: C.sub, fontWeight: 700, padding: "0 4px 8px", textTransform: "uppercase" }}><span>Код (SKU)</span><span>Наименование</span><span style={{ textAlign: "right" }}>Цена прайса</span><span style={{ textAlign: "right" }}>Абонплата/мес</span><span /></div>
        {products.length === 0 && <Muted>Каталог пуст. Добавьте товары ниже.</Muted>}
        {products.map((p, i) => (
          <div key={p.sku} style={{ display: "grid", gridTemplateColumns: "120px 1fr 150px 150px 40px", gap: 8, alignItems: "center", padding: "7px 4px", borderBottom: `1px solid ${C.line}` }}>
            <span style={{ fontWeight: 700, fontSize: 12.5, fontFamily: "monospace" }}>{p.sku}</span>
            <input value={p.name} onChange={(e) => setRow(i, "name", e.target.value)} style={{ ...txt, width: "100%", boxSizing: "border-box" }} />
            <NumInput value={p.listPrice} onChange={(v) => setRow(i, "listPrice", v)} suffix="сум" decimal accent={C.darkgreen} w={140} />
            <NumInput value={p.serviceFee} onChange={(v) => setRow(i, "serviceFee", v)} suffix="сум" decimal accent={C.teal} w={140} />
            <Xb on={() => del(p.sku)} />
          </div>
        ))}
      </Block>
      <Block title="Добавить товар">
        <div style={{ display: "flex", flexWrap: "wrap", gap: 12, alignItems: "flex-end" }}>
          <div><Label2>Код (SKU)</Label2><input value={f.sku} onChange={(e) => setF((p) => ({ ...p, sku: e.target.value }))} placeholder="напр. BT05" style={{ ...txt, width: 120 }} /></div>
          <div><Label2>Наименование</Label2><input value={f.name} onChange={(e) => setF((p) => ({ ...p, name: e.target.value }))} placeholder="Датчик…" style={{ ...txt, width: 240 }} /></div>
          <div><Label2>Цена прайса</Label2><NumInput value={f.listPrice} onChange={(v) => setF((p) => ({ ...p, listPrice: v }))} suffix="сум" decimal accent={C.darkgreen} w={150} /></div>
          <div><Label2>Абонплата/мес</Label2><NumInput value={f.serviceFee} onChange={(v) => setF((p) => ({ ...p, serviceFee: v }))} suffix="сум" decimal accent={C.teal} w={150} /></div>
          <button onClick={add} style={{ ...addBtn }}>+ Добавить</button>
        </div>
        <Muted>Код (SKU) — уникальный код товара; по нему он связан с приходами, складом и заказами. Менять код у уже используемого товара не стоит. Цена прайса подставляется в заказ, абонплата — рекомендуемый тариф обслуживания.</Muted>
      </Block>
    </div>
  );
}

// ===== Вход =====
function Login({ onLogin }) {
  const [login, setLogin] = useState("");
  const [password, setPassword] = useState("");
  const [err, setErr] = useState("");
  const [busy, setBusy] = useState(false);
  const submit = () => {
    if (!login || !password) return;
    setErr(""); setBusy(true);
    fetch("/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ login, password }) })
      .then((r) => r.json().then((d) => ({ ok: r.ok, d })))
      .then(({ ok, d }) => { setBusy(false); if (ok) onLogin(d.token, d.user); else setErr(d.error || "Ошибка входа"); })
      .catch(() => { setBusy(false); setErr("Нет связи с сервером"); });
  };
  return (
    <div style={{ minHeight: "100vh", background: C.bg, display: "grid", placeItems: "center", fontFamily: "'Segoe UI', system-ui, sans-serif" }}>
      <div style={{ background: C.white, borderRadius: 14, padding: 32, width: 340, boxShadow: "0 10px 40px rgba(9,30,66,.18)" }}>
        <div style={{ fontSize: 22, fontWeight: 800, color: C.ink, marginBottom: 4 }}>⚙️ Sensora ERP</div>
        <div style={{ fontSize: 13, color: C.sub, marginBottom: 20 }}>Вход в систему</div>
        <Label2>Логин</Label2>
        <input value={login} onChange={(e) => setLogin(e.target.value)} onKeyDown={(e) => e.key === "Enter" && submit()} style={{ ...txt, width: "100%", boxSizing: "border-box", marginBottom: 12 }} />
        <Label2>Пароль</Label2>
        <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} onKeyDown={(e) => e.key === "Enter" && submit()} style={{ ...txt, width: "100%", boxSizing: "border-box", marginBottom: 16 }} />
        {err && <div style={{ color: C.red, fontSize: 12.5, marginBottom: 12 }}>{err}</div>}
        <button onClick={submit} disabled={busy} style={{ width: "100%", background: C.blue, color: C.white, border: "none", borderRadius: 8, padding: "11px", fontWeight: 700, cursor: busy ? "default" : "pointer", fontSize: 14, opacity: busy ? .7 : 1 }}>{busy ? "Вход…" : "Войти"}</button>
        <div style={{ fontSize: 11, color: C.sub, marginTop: 14, textAlign: "center" }}>По умолчанию: admin / admin</div>
      </div>
    </div>
  );
}

// ===== Пользователи (только владелец) =====
function Users() {
  const [users, setUsers] = useState([]);
  const [f, setF] = useState({ login: "", name: "", role: "sales", password: "" });
  const [err, setErr] = useState("");
  const load = () => authFetch("/api/users").then((r) => (r.ok ? r.json() : [])).then(setUsers).catch(() => {});
  useEffect(() => { load(); }, []);
  const add = () => {
    setErr("");
    authFetch("/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(f) })
      .then((r) => r.json().then((d) => ({ ok: r.ok, d })))
      .then(({ ok, d }) => { if (ok) { setF({ login: "", name: "", role: "sales", password: "" }); load(); } else setErr(d.error || "Ошибка"); });
  };
  const del = (id) => { if (window.confirm("Удалить пользователя?")) authFetch("/api/users/" + id, { method: "DELETE" }).then(load); };
  return (
    <div style={{ display: "grid", gap: 16 }}>
      <H>Пользователи</H>
      <Block title="Команда">
        {users.map((u) => (
          <div key={u.id} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 4px", borderBottom: `1px solid ${C.line}` }}>
            <span style={{ width: 34, height: 34, borderRadius: "50%", background: u.role === "owner" ? C.purple : C.blue, color: C.white, fontWeight: 800, display: "grid", placeItems: "center" }}>{(u.name || u.login)[0].toUpperCase()}</span>
            <div style={{ flex: 1 }}><div style={{ fontWeight: 700, fontSize: 13.5 }}>{u.name}</div><div style={{ fontSize: 11.5, color: C.sub }}>@{u.login}</div></div>
            <Badge c={u.role === "owner" ? C.purple : C.teal}>{ROLE_LABEL[u.role] || u.role}</Badge>
            <button onClick={() => del(u.id)} style={{ fontSize: 12, color: C.sub, background: "transparent", border: "none", cursor: "pointer" }}>Удалить</button>
          </div>
        ))}
      </Block>
      <Block title="Добавить пользователя">
        <div style={{ display: "flex", flexWrap: "wrap", gap: 12, alignItems: "flex-end" }}>
          <div><Label2>Логин</Label2><input value={f.login} onChange={(e) => setF((p) => ({ ...p, login: e.target.value }))} style={{ ...txt, width: 140 }} /></div>
          <div><Label2>Имя</Label2><input value={f.name} onChange={(e) => setF((p) => ({ ...p, name: e.target.value }))} style={{ ...txt, width: 180 }} /></div>
          <div><Label2>Роль</Label2><select value={f.role} onChange={(e) => setF((p) => ({ ...p, role: e.target.value }))} style={sel}>{ROLES.map(([v, l]) => <option key={v} value={v}>{l}</option>)}</select></div>
          <div><Label2>Пароль</Label2><input type="password" value={f.password} onChange={(e) => setF((p) => ({ ...p, password: e.target.value }))} style={{ ...txt, width: 140 }} /></div>
          <button onClick={add} style={{ background: C.darkgreen, color: C.white, border: "none", borderRadius: 8, padding: "9px 16px", fontWeight: 700, cursor: "pointer" }}>+ Добавить</button>
        </div>
        {err && <Muted style={{ color: C.red }}>{err}</Muted>}
        <Muted>Роли пока разграничивают доступ к управлению пользователями (владелец). Детальные права по модулям — следующий шаг.</Muted>
      </Block>
    </div>
  );
}

// ===== Обёртка с авторизацией =====
function App() {
  const [token, setToken] = useState(TOKEN);
  const [me, setMe] = useState(null);
  const [checking, setChecking] = useState(!!TOKEN);
  useEffect(() => { ON_AUTH_FAIL = () => { TOKEN = null; try { localStorage.removeItem("sensora_token"); } catch (e) {} setToken(null); setMe(null); }; }, []);
  useEffect(() => {
    if (!token) { setChecking(false); return; }
    TOKEN = token;
    authFetch("/api/me").then((r) => (r.ok ? r.json() : null)).then((u) => { setMe(u); setChecking(false); }).catch(() => setChecking(false));
  }, [token]);
  const onLogin = (t, u) => { TOKEN = t; try { localStorage.setItem("sensora_token", t); } catch (e) {} setMe(u); setToken(t); };
  const onLogout = () => { TOKEN = null; try { localStorage.removeItem("sensora_token"); } catch (e) {} setToken(null); setMe(null); };
  if (checking) return <div style={{ padding: 48, color: C.sub, fontFamily: "'Segoe UI', system-ui, sans-serif" }}>Загрузка…</div>;
  if (!token || !me) return <Login onLogin={onLogin} />;
  return <ERP me={me} onLogout={onLogout} />;
}

// ===== монтаж приложения =====
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
