// screens-work.jsx — Telemetry, Microclimate, Recommendations, Tasks (Kanban + List), Commands, OTA, Reports, Settings

/* ─── CP-8 Live telemetry ─────────────────────────────────── */
function Telemetry() {
  const TelemetryChart = window.TelemetryChart;
  const devices = Array.isArray(DATA?.devices) ? DATA.devices : [];
  const [selected, setSelected] = React.useState(devices[0]?.id || "");

  React.useEffect(() => {
    if (!devices.length) return;
    if (!selected || !devices.some((d) => d.id === selected)) {
      setSelected(devices[0].id);
    }
  }, [selected, devices]);

  const dev = devices.find((d) => d.id === selected) || devices[0] || null;
  const events = [
    { t:"14:38:12", lvl:"info", msg:"sample EC_sub=2,4 EC_drain=2,0 flow=1,2 CO2=812 lux=18400 t=21,4°C" },
    { t:"14:38:08", lvl:"info", msg:"sample EC_sub=2,3 EC_drain=1,9 flow=1,1 CO2=808 lux=18350 t=21,5°C" },
    { t:"14:38:04", lvl:"warn", msg:"battery dropped to 12% — schedule replacement" },
    { t:"14:38:00", lvl:"info", msg:"sample EC_sub=2,4 EC_drain=2,1 flow=1,2 CO2=815 lux=18420 t=21,4°C" },
    { t:"14:37:56", lvl:"info", msg:"heartbeat ok rssi=−62 fw=v1.8.0" },
    { t:"14:37:48", lvl:"crit", msg:"packet dropped seq=4421 (3rd in 30s)" },
    { t:"14:37:40", lvl:"info", msg:"sample EC_sub=2,4 EC_drain=2,0 flow=1,3 CO2=810 lux=18380 t=21,3°C" },
    { t:"14:37:32", lvl:"ok", msg:"calibration check passed" },
    { t:"14:37:24", lvl:"info", msg:"sample EC_sub=2,3 EC_drain=1,8 flow=1,1 CO2=805 lux=18310 t=21,4°C" },
  ];

  return (
    <div className="page" data-screen-label="08 Телеметрия">
      <div className="page-head">
        <div>
          <div className="page-title">Живая телеметрия</div>
          <div className="page-sub">История датчиков и микроклимата по выбранному устройству</div>
        </div>
        <div className="actions">
          <span className="chip on"><i className="ri-time-line"></i>Автообновление 60с (1h)</span>
        </div>
      </div>

      <div style={{display:"flex",gap:8,flexWrap:"wrap"}}>
        <span className="chip on"><i className="ri-flask-line"></i>EC субстрата</span>
        <span className="chip on"><i className="ri-flask-line"></i>EC дренажа</span>
        <span className="chip on"><i className="ri-water-flash-line"></i>Расход полива</span>
        <span className="chip on"><i className="ri-temp-hot-line"></i>Температура</span>
        <span className="chip on"><i className="ri-drop-line"></i>Влажность</span>
        <span className="chip on"><i className="ri-bubble-chart-line"></i>CO₂</span>
        <span className="chip"><i className="ri-sun-line"></i>Освещённость</span>
        <span className="chip"><i className="ri-pulse-line"></i>Активность</span>
        <span className="chip"><i className="ri-battery-line"></i>Батарея</span>
        <span style={{flex:1}}></span>
        <span className="chip"><i className="ri-map-pin-line"></i>Все зоны</span>
      </div>

      <div className="tele-grid">
        <div className="tele-list">
          <div className="tele-list-head">Устройства<span style={{color:"var(--fg)"}}>{devices.length}</span></div>
          {devices.map(d=>(
            <div key={d.id} className={"tele-item"+(selected===d.id?" on":"")} onClick={()=>setSelected(d.id)}>
              <i className={d.icon} style={{fontSize:14,color:"var(--fg-muted)"}}></i>
              <div style={{flex:1,minWidth:0}}>
                <b style={{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"}}>{d.name}</b>
                <span className="id">{d.zone}</span>
              </div>
              <span className={"status status--"+d.status} style={{padding:"0 6px"}}><span className="dot"></span></span>
            </div>
          ))}
        </div>

        <div className="tele-stream">
          {dev && (
            <div className="tele-stream-head">
              <i className={dev.icon} style={{fontSize:18,color:"var(--fg-muted)"}}></i>
              <div style={{flex:1}}>
                <div style={{font:"600 14px Inter",color:"var(--fg-strong)"}}>{dev.name}</div>
                <div className="id-mono" style={{fontSize:11}}>{dev.id} · {dev.zone}</div>
              </div>
              <span className={"status status--"+dev.status}><span className="dot"></span>{dev.statusLbl}</span>
            </div>
          )}

          <div className="tele-charts">
            {!dev && (
              <div className="tele-chart tele-chart--empty">Устройства не найдены</div>
            )}
            {/* [TASK_START:T006] */}
            {dev && TelemetryChart && [
              { title: "Температура", metric_key: "temperature_c", unit: "°C", accent: "#E5342B" },
              { title: "Влажность", metric_key: "humidity_pct", unit: "%", accent: "#007BFB" },
              { title: "CO₂", metric_key: "co2_ppm", unit: "ppm", accent: "#7FB539" },
              { title: "Освещённость", metric_key: "insolation_lux", unit: "lux", accent: "#E8B84A" },
              { title: "Влажность субстрата", metric_key: "substrate_moisture_pct", unit: "%", accent: "#00A3A3" },
              { title: "EC дренажа", metric_key: "drain_ec", unit: "mS/cm", accent: "#FF7A00" },
            ].map((chart) => (
              <TelemetryChart
                key={chart.metric_key}
                title={chart.title}
                source="device"
                deviceId={dev.id}
                metricKey={chart.metric_key}
                unitLabel={chart.unit}
                accent={chart.accent}
                defaultRange="1h"
                ranges={["1h", "6h", "24h", "7d"]}
              />
            ))}
            {/* [TASK_COMPLETE:T006] */}
          </div>

          <div className="tele-event-log">
            {events.map((e,i)=>(
              <div key={i} className="row">
                <time>{e.t}</time>
                <span className={"lvl "+e.lvl}>{e.lvl}</span>
                <span style={{color:"var(--fg)"}}>{e.msg}</span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ─── Microclimate dashboard ──────────────────────────────── */
function metricDisplayName(metric) {
  if (metric === "temperature_c") return "Температура";
  if (metric === "humidity_pct") return "Влажность";
  if (metric === "co2_ppm") return "CO₂";
  if (metric === "insolation_lux") return "Освещённость";
  return metric || "—";
}

function toMicroclimateFallback(zoneId, range) {
  const micro = (DATA && DATA.telemetry && DATA.telemetry.microclimate) || {};
  return {
    composite: {
      zone_id: zoneId,
      range,
      generated_at: new Date().toISOString(),
      data_available: true,
      composite_score: null,
      metrics: [
        { metric: "temperature_c", unit: "°C", current_value: micro.temperature_c ?? null, status: "unknown" },
        { metric: "humidity_pct", unit: "%", current_value: micro.humidity_pct ?? null, status: "unknown" },
        { metric: "co2_ppm", unit: "ppm", current_value: micro.co2_ppm ?? null, status: "unknown" },
        { metric: "insolation_lux", unit: "lux", current_value: micro.insolation_lux ?? null, status: "unknown" },
      ],
    },
    history_comparison: { metrics: [] },
    zone_comparison: { metrics: [] },
  };
}

function Microclimate() {
  const zoneOptions = React.useMemo(() => {
    const byGreenhouse = (DATA && DATA.zones) || {};
    return Object.keys(byGreenhouse).flatMap((greenhouseId) => {
      const rows = Array.isArray(byGreenhouse[greenhouseId]) ? byGreenhouse[greenhouseId] : [];
      return rows.map((row) => ({
        greenhouseId,
        zoneId: row.id,
        label: row.row_label || row.id,
      }));
    });
  }, []);

  const [selectedZoneId, setSelectedZoneId] = React.useState(() => zoneOptions[0]?.zoneId || "");
  const [range, setRange] = React.useState("24h");
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState("");
  const [payload, setPayload] = React.useState(null);

  React.useEffect(() => {
    if (!selectedZoneId) return;
    let cancelled = false;

    async function loadMicroclimate() {
      setLoading(true);
      setError("");
      try {
        const response = await Keeper.api.zoneMicroclimateDashboard(selectedZoneId, { range });
        if (!cancelled) setPayload(response);
      } catch (_) {
        if (!cancelled) {
          setError("Модуль микроклимата временно недоступен");
          setPayload(toMicroclimateFallback(selectedZoneId, range));
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    }

    loadMicroclimate();
    return () => {
      cancelled = true;
    };
  }, [selectedZoneId, range]);

  const composite = payload && payload.composite ? payload.composite : null;
  const history = payload && payload.history_comparison ? payload.history_comparison : null;
  const zoneComparison = payload && payload.zone_comparison ? payload.zone_comparison : null;
  const metrics = composite && Array.isArray(composite.metrics) ? composite.metrics : [];

  return (
    <div className="page" data-screen-label="09 Микроклимат">
      <div className="page-head">
        <div>
          <div className="page-title">Микроклимат</div>
          <div className="page-sub">Сводные метрики зоны, динамика и сравнение с соседними зонами</div>
        </div>
        <div className="actions">
          <select className="select" value={selectedZoneId} onChange={(e) => setSelectedZoneId(e.target.value)}>
            {zoneOptions.map((zone) => (
              <option key={zone.zoneId} value={zone.zoneId}>
                {zone.label}
              </option>
            ))}
          </select>
          <div className="segmented">
            {["24h", "48h", "7d"].map((option) => (
              <button key={option} className={range === option ? "on" : ""} onClick={() => setRange(option)}>
                {option}
              </button>
            ))}
          </div>
        </div>
      </div>

      {error && <div className="reco-banner-error">{error}</div>}

      <div className="kpi-grid" style={{ marginBottom: 14 }}>
        <div className="kpi">
          <div className="kpi-label">Индекс микроклимата</div>
          <div className="kpi-value">
            {composite && composite.composite_score != null
              ? `${Math.round(Number(composite.composite_score) * 100)}%`
              : "—"}
          </div>
          <div className="kpi-sub">Диапазон: {composite?.range || range}</div>
        </div>
        <div className="kpi">
          <div className="kpi-label">Точек данных</div>
          <div className="kpi-value">{composite?.data_points ?? 0}</div>
          <div className="kpi-sub">Зона: {composite?.zone_id || selectedZoneId || "—"}</div>
        </div>
        <div className="kpi">
          <div className="kpi-label">Сгенерировано</div>
          <div className="kpi-value" style={{ fontSize: 18 }}>
            {composite?.generated_at ? new Date(composite.generated_at).toLocaleString("ru-RU") : "—"}
          </div>
          <div className="kpi-sub">{loading ? "Обновление..." : "Актуально"}</div>
        </div>
      </div>

      <div className="card">
        <h3 className="card-title">Текущие метрики</h3>
        {!loading && metrics.length === 0 && <div className="placeholder">Нет данных для выбранной зоны</div>}
        <table>
          <thead>
            <tr>
              <th>Метрика</th>
              <th>Значение</th>
              <th>База</th>
              <th>Оптимум</th>
              <th>Статус</th>
            </tr>
          </thead>
          <tbody>
            {metrics.map((row) => (
              <tr key={row.metric}>
                <td>{metricDisplayName(row.metric)}</td>
                <td>{row.current_value != null ? `${row.current_value} ${row.unit || ""}` : "—"}</td>
                <td>{row.baseline_value != null ? `${row.baseline_value} ${row.unit || ""}` : "—"}</td>
                <td>
                  {row.optimal_min != null || row.optimal_max != null
                    ? `${row.optimal_min ?? "—"}…${row.optimal_max ?? "—"} ${row.unit || ""}`
                    : "—"}
                </td>
                <td>{row.status || "unknown"}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <div className="grid-2" style={{ marginTop: 14 }}>
        <div className="card">
          <h3 className="card-title">Историческое сравнение</h3>
          {history?.metrics?.length ? (
            <ul style={{ margin: 0, paddingLeft: 18, display: "grid", gap: 6 }}>
              {history.metrics.map((row) => (
                <li key={row.metric}>
                  {metricDisplayName(row.metric)}: {row.absolute_delta != null ? row.absolute_delta : "—"} {row.unit || ""} ({row.trend || "unknown"})
                </li>
              ))}
            </ul>
          ) : (
            <div className="placeholder">Сравнение периодов пока недоступно</div>
          )}
        </div>
        <div className="card">
          <h3 className="card-title">Сравнение зон</h3>
          {zoneComparison?.metrics?.length ? (
            <ul style={{ margin: 0, paddingLeft: 18, display: "grid", gap: 6 }}>
              {zoneComparison.metrics.map((row) => (
                <li key={row.metric}>
                  {metricDisplayName(row.metric)}: ранг {row.rank ?? "—"} из {row.peer_count ?? 0}
                </li>
              ))}
            </ul>
          ) : (
            <div className="placeholder">Сравнение с соседними зонами пока недоступно</div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ─── CP-10 Recommendations ───────────────────────────────── */
function normalizeRecommendationSeverity(raw) {
  const value = String(raw || "").toLowerCase();
  if (value === "critical" || value === "cri") return "critical";
  if (value === "warning" || value === "warn") return "warning";
  return "info";
}

function normalizeRecommendationStatus(raw, applied, dismissed) {
  const value = String(raw || "").toLowerCase();
  if (value === "applied" || applied) return "applied";
  if (value === "dismissed" || dismissed) return "dismissed";
  return "active";
}

function recommendationSeverityLabel(severity) {
  if (severity === "critical") return "Critical";
  if (severity === "warning") return "Warning";
  return "Info";
}

// [TASK_START:T008]
function RecommendationSeverityBadge({ severity }) {
  return (
    <span className={"reco-severity-badge reco-severity-badge--" + severity}>
      {recommendationSeverityLabel(severity)}
    </span>
  );
}
// [TASK_COMPLETE:T008]

function recommendationSeverityClass(severity) {
  if (severity === "critical") return "cri";
  if (severity === "warning") return "warn";
  return "info";
}

function recommendationSourceLabel(source) {
  const value = String(source || "").toLowerCase();
  if (value === "sensor_analysis") return "Sensor analysis";
  if (value === "alert_correlation") return "Alert correlation";
  if (value === "scheduled_inspection") return "Scheduled inspection";
  if (value === "agronomist") return "Agronomist";
  return "System";
}

function recommendationStatusLabel(status) {
  if (status === "applied") return "Applied";
  if (status === "dismissed") return "Dismissed";
  return "Active";
}

function recommendationActionIcon(severity) {
  if (severity === "critical") return "ri-alarm-warning-line";
  if (severity === "warning") return "ri-error-warning-line";
  return "ri-lightbulb-line";
}

function toRecoViewModel(raw) {
  const severity = normalizeRecommendationSeverity(raw?.severity || raw?.kind);
  const status = normalizeRecommendationStatus(raw?.status, raw?.applied, raw?.dismissed);
  // [TASK_START:T012]
  const expiresAt = raw?.expires_at || raw?.expiresAt || null;
  const expiresAtMs = expiresAt ? Date.parse(expiresAt) : Number.NaN;
  const isExpired = Number.isFinite(expiresAtMs) && expiresAtMs <= Date.now();
  // [TASK_COMPLETE:T012]

  return {
    id: raw?.id || `rec-${Math.random().toString(16).slice(2, 8)}`,
    tenant_id: raw?.tenant_id || null,
    greenhouse_id: raw?.greenhouse_id || null,
    zone_id: raw?.zone_id || null,
    severity,
    source: raw?.source || "sensor_analysis",
    status,
    title: raw?.title || "Recommendation",
    detail: raw?.detail || raw?.desc || "",
    created_at: raw?.created_at || raw?.createdAt || null,
    expires_at: expiresAt,
    is_expired: isExpired,
    icon: raw?.icon || recommendationActionIcon(severity),
    applied: Boolean(raw?.applied || status === "applied"),
    dismissed: Boolean(raw?.dismissed || status === "dismissed"),
  };
}

function formatRecommendationTime(ts) {
  if (!ts) return "—";
  const value = Date.parse(ts);
  if (!Number.isFinite(value)) return String(ts);
  return new Date(value).toLocaleString();
}

function Recommendations({ onNav }) {
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState("");
  const [recommendations, setRecommendations] = React.useState([]);
  const [statusFilter, setStatusFilter] = React.useState("active");
  const [severityFilter, setSeverityFilter] = React.useState("all");
  const [pendingAction, setPendingAction] = React.useState(null); // { id, type, text, submitting, error }

  React.useEffect(() => {
    let cancelled = false;
    async function loadRecommendations() {
      setLoading(true);
      setError("");
      try {
        // [TASK_START:T006]
        const rows = await Keeper.api.recommendations();
        const list = Array.isArray(rows) ? rows.map(toRecoViewModel) : [];
        // [TASK_COMPLETE:T006]
        if (!cancelled) setRecommendations(list);
      } catch (_) {
        if (!cancelled) {
          // [TASK_START:T013]
          setError("Рекомендации временно недоступны");
          const fallback = Array.isArray(DATA?.recommendations) ? DATA.recommendations.map(toRecoViewModel) : [];
          setRecommendations(fallback);
          // [TASK_COMPLETE:T013]
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    }
    loadRecommendations();
    return () => {
      cancelled = true;
    };
  }, []);

  const statusCounts = React.useMemo(() => {
    const counts = { active: 0, applied: 0, dismissed: 0 };
    recommendations.forEach((rec) => {
      const value = rec.status;
      if (value === "applied") counts.applied += 1;
      else if (value === "dismissed") counts.dismissed += 1;
      else counts.active += 1;
    });
    return counts;
  }, [recommendations]);

  // [TASK_START:T009]
  const visibleRecommendations = React.useMemo(() => {
    return recommendations.filter((rec) => {
      if (statusFilter !== "all" && rec.status !== statusFilter) return false;
      if (severityFilter !== "all" && rec.severity !== severityFilter) return false;
      return true;
    });
  }, [recommendations, statusFilter, severityFilter]);
  // [TASK_COMPLETE:T009]

  function beginAction(recId, type) {
    setPendingAction({ id: recId, type, text: "", submitting: false, error: "" });
  }

  function cancelAction() {
    setPendingAction(null);
  }

  async function confirmAction() {
    if (!pendingAction) return;
    const prev = recommendations;
    const { id, type, text } = pendingAction;

    // [TASK_START:T007]
    if (type === "apply") {
      setRecommendations((rows) =>
        rows.map((row) =>
          row.id === id
            ? { ...row, status: "applied", applied: true, dismissed: false }
            : row
        )
      );
    } else {
      setRecommendations((rows) =>
        rows.map((row) =>
          row.id === id
            ? { ...row, status: "dismissed", dismissed: true, applied: false }
            : row
        )
      );
    }
    // [TASK_COMPLETE:T007]

    setPendingAction((state) => ({ ...state, submitting: true, error: "" }));
    try {
      if (type === "apply") {
        await Keeper.api.applyRecommendation(id, text ? { note: text } : undefined);
      } else {
        await Keeper.api.dismissRecommendation(id, text ? { reason: text } : undefined);
      }
      setPendingAction(null);
    } catch (_) {
      setRecommendations(prev);
      setPendingAction((state) => ({
        ...state,
        submitting: false,
        error: type === "apply" ? "Не удалось применить рекомендацию" : "Не удалось отклонить рекомендацию",
      }));
    }
  }

  return (
    <div className="page" data-screen-label="10 Рекомендации">
      <div className="page-head">
        <div>
          <div className="page-title">Рекомендации</div>
          <div className="page-sub">Приоритизированные действия по зонам и теплицам</div>
        </div>
        <div className="actions">
          <div className="segmented">
            <button className={statusFilter === "active" ? "on" : ""} onClick={() => setStatusFilter("active")}>
              Active · {statusCounts.active}
            </button>
            <button className={statusFilter === "applied" ? "on" : ""} onClick={() => setStatusFilter("applied")}>
              Applied · {statusCounts.applied}
            </button>
            <button className={statusFilter === "dismissed" ? "on" : ""} onClick={() => setStatusFilter("dismissed")}>
              Dismissed · {statusCounts.dismissed}
            </button>
          </div>
        </div>
      </div>

      <div style={{display:"flex",gap:8,flexWrap:"wrap"}}>
        <button className={"btn btn--sm " + (severityFilter === "all" ? "btn--dark" : "btn--secondary")} onClick={() => setSeverityFilter("all")}>All</button>
        <button className={"btn btn--sm " + (severityFilter === "warning" ? "btn--dark" : "btn--secondary")} onClick={() => setSeverityFilter("warning")}>Warning</button>
        <button className={"btn btn--sm " + (severityFilter === "critical" ? "btn--dark" : "btn--secondary")} onClick={() => setSeverityFilter("critical")}>Critical</button>
        <button className={"btn btn--sm " + (severityFilter === "info" ? "btn--dark" : "btn--secondary")} onClick={() => setSeverityFilter("info")}>Info</button>
      </div>

      {/* [TASK_START:T013] */}
      {error && <div className="reco-banner-error">{error}</div>}
      {/* [TASK_COMPLETE:T013] */}

      <div className="stack">
        {loading && <div className="placeholder">Загрузка рекомендаций…</div>}

        {!loading && recommendations.length === 0 && (
          <div className="reco-empty-state">
            <h4>Рекомендаций пока нет</h4>
            <p>Когда аналитика сформирует новые рекомендации, они появятся здесь.</p>
          </div>
        )}

        {!loading && recommendations.length > 0 && visibleRecommendations.length === 0 && (
          <div className="placeholder">Нет рекомендаций для выбранных фильтров</div>
        )}

        {visibleRecommendations.map((r)=>(
          <div key={r.id} className={"reco-card " + recommendationSeverityClass(r.severity)}>
            <div className="ic"><i className={r.icon}></i></div>
            <div className="body">
              <div className="reco-meta">
                <RecommendationSeverityBadge severity={r.severity} />
                <span className={"reco-status-badge reco-status-badge--" + r.status}>{recommendationStatusLabel(r.status)}</span>
                {/* [TASK_START:T012] */}
                {r.is_expired && <span className="reco-expired-badge">Expired</span>}
                {/* [TASK_COMPLETE:T012] */}
              </div>
              <h4>{r.title}</h4>
              <div className="desc">{r.detail}</div>
              <div className="reco-meta reco-meta--subtle">
                <span>{recommendationSourceLabel(r.source)}</span>
                <span>{r.zone_id || "—"}</span>
                <span>Expires: {formatRecommendationTime(r.expires_at)}</span>
              </div>
              <div style={{display:"flex",gap:14,alignItems:"center",flexWrap:"wrap"}}>
                <span style={{fontSize:11,color:"var(--fg-muted)",fontFamily:"ui-monospace,Menlo,monospace"}}>{r.id}</span>
                <span style={{fontSize:11,color:"var(--fg-muted)",fontFamily:"ui-monospace,Menlo,monospace"}}>Created: {formatRecommendationTime(r.created_at)}</span>
              </div>

              {pendingAction && pendingAction.id === r.id && (
                <>
                {/* [TASK_START:T010] */}
                <div className="reco-inline-form">
                  <label>{pendingAction.type === "apply" ? "Комментарий (опционально)" : "Причина (опционально)"}</label>
                  <textarea
                    value={pendingAction.text}
                    onChange={(e) => setPendingAction((state) => ({ ...state, text: e.target.value }))}
                    placeholder={pendingAction.type === "apply" ? "Что было выполнено?" : "Почему рекомендация отклонена?"}
                  />
                  {pendingAction.error && <div className="reco-inline-form__error">{pendingAction.error}</div>}
                  <div className="reco-inline-form__actions">
                    <button className="btn btn--secondary btn--sm" onClick={cancelAction} disabled={pendingAction.submitting}>Отмена</button>
                    <button className="btn btn--primary btn--sm" onClick={confirmAction} disabled={pendingAction.submitting}>
                      {pendingAction.submitting ? "Сохранение..." : "Подтвердить"}
                    </button>
                  </div>
                </div>
                {/* [TASK_COMPLETE:T010] */}
                </>
              )}
            </div>
            <div style={{display:"flex",flexDirection:"column",gap:6,minWidth:170}}>
              <button
                className="btn btn--primary btn--sm"
                onClick={() => beginAction(r.id, "apply")}
                disabled={r.status !== "active" || r.is_expired}
              >
                {/* [TASK_START:T010] */}
                <i className="ri-check-line"></i>Apply
                {/* [TASK_COMPLETE:T010] */}
              </button>
              <button className="btn btn--secondary btn--sm" onClick={()=>onNav("tasks")}><i className="ri-task-add-line"></i>Create task</button>
              <button
                className="btn btn--ghost btn--sm"
                onClick={() => beginAction(r.id, "dismiss")}
                disabled={r.status !== "active" || r.is_expired}
              >
                {/* [TASK_START:T011] */}
                Dismiss
                {/* [TASK_COMPLETE:T011] */}
              </button>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// [TASK_START:T014]
function RecommendationsPanel({ greenhouseId, zoneId, maxItems = 4 }) {
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState("");
  const [recommendations, setRecommendations] = React.useState([]);

  React.useEffect(() => {
    let cancelled = false;
    async function loadRecommendations() {
      setLoading(true);
      setError("");
      try {
        const rows = await Keeper.api.recommendations();
        const list = Array.isArray(rows) ? rows.map(toRecoViewModel) : [];
        if (!cancelled) setRecommendations(list);
      } catch (_) {
        if (!cancelled) {
          setError("Рекомендации временно недоступны");
          const fallback = Array.isArray(DATA?.recommendations) ? DATA.recommendations.map(toRecoViewModel) : [];
          setRecommendations(fallback);
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    }
    loadRecommendations();
    return () => {
      cancelled = true;
    };
  }, [greenhouseId, zoneId]);

  const visible = React.useMemo(() => {
    return recommendations
      .filter((item) => {
        if (item.status !== "active") return false;
        if (zoneId) return item.zone_id === zoneId;
        if (greenhouseId) return item.greenhouse_id === greenhouseId;
        return true;
      })
      .slice(0, maxItems);
  }, [greenhouseId, maxItems, recommendations, zoneId]);

  async function quickApply(id) {
    const prev = recommendations;
    setRecommendations((rows) =>
      rows.map((row) => (row.id === id ? { ...row, status: "applied", applied: true } : row))
    );
    try {
      await Keeper.api.applyRecommendation(id);
    } catch (_) {
      setRecommendations(prev);
    }
  }

  return (
    <section className="reco-panel">
      <div className="reco-panel__head">
        <h3>Recommendations</h3>
      </div>
      {error && <div className="reco-panel__note reco-panel__note--error">{error}</div>}
      {loading && <div className="reco-panel__note">Загрузка…</div>}
      {!loading && !error && visible.length === 0 && (
        <div className="reco-panel__note">Нет активных рекомендаций для текущего контекста</div>
      )}
      {!loading && !error && visible.map((item) => (
        <div key={item.id} className="reco-panel__item">
          <div className="reco-panel__meta">
            <RecommendationSeverityBadge severity={item.severity} />
            {item.is_expired && <span className="reco-expired-badge">Expired</span>}
          </div>
          <h4>{item.title}</h4>
          <p>{item.detail}</p>
          <div className="reco-panel__foot">
            <span>{item.zone_id || "—"}</span>
            <button
              className="btn btn--primary btn--sm"
              onClick={() => quickApply(item.id)}
              disabled={item.is_expired}
            >
              Quick apply
            </button>
          </div>
        </div>
      ))}
    </section>
  );
}
// [TASK_COMPLETE:T014]

/* ─── CP-11 Tasks Kanban ──────────────────────────────────── */
function TasksKanban() {
  const [view, setView] = React.useState("kanban");
  const [tasks, setTasks] = React.useState(DATA.tasks);
  const [drag, setDrag] = React.useState(null);

  const cols = [
    { id:"todo",   label:"К выполнению", count: tasks.filter(t=>t.col==="todo").length },
    { id:"doing",  label:"В работе",     count: tasks.filter(t=>t.col==="doing").length },
    { id:"review", label:"На проверке",  count: tasks.filter(t=>t.col==="review").length },
    { id:"done",   label:"Готово",       count: tasks.filter(t=>t.col==="done").length },
  ];

  function onDragStart(id) { setDrag(id); }
  function onDrop(colId) {
    if (!drag) return;
    setTasks(ts => ts.map(t => t.id===drag ? {...t, col: colId} : t));
    setDrag(null);
  }

  return (
    <div className="page" data-screen-label="11 Задачи">
      <div className="page-head">
        <div>
          <div className="page-title">Задачи</div>
          <div className="page-sub">{tasks.filter(t=>t.col!=="done").length} открытых · {tasks.filter(t=>t.col==="done").length} закрыто за неделю</div>
        </div>
        <div className="actions">
          <div className="segmented">
            <button className={view==="kanban"?"on":""} onClick={()=>setView("kanban")}>Канбан</button>
            <button className={view==="list"?"on":""} onClick={()=>setView("list")}>Список</button>
          </div>
          <button className="btn btn--secondary"><i className="ri-filter-line"></i>Фильтр</button>
          <button className="btn btn--primary"><i className="ri-add-line"></i>Новая задача</button>
        </div>
      </div>

      {view === "kanban" && (
        <div className="kanban">
          {cols.map(col => (
            <div key={col.id} className="kanban-col"
              onDragOver={(e)=>e.preventDefault()}
              onDrop={()=>onDrop(col.id)}
            >
              <div className="kanban-col-head">
                <b>{col.label}</b>
                <span className="count">{col.count}</span>
              </div>
              {tasks.filter(t=>t.col===col.id).map(t=>(
                <div key={t.id}
                  className={"kanban-card"+(drag===t.id?" drag":"")}
                  draggable
                  onDragStart={()=>onDragStart(t.id)}
                  onDragEnd={()=>setDrag(null)}
                >
                  <div className="meta">
                    <span><span className={"prio "+t.prio}></span>{t.id}</span>
                    <span>{t.site}</span>
                  </div>
                  <div className="title">{t.title}</div>
                  {t.linked && <div style={{fontSize:11,color:"var(--fg-muted)"}}><i className="ri-magic-line" style={{verticalAlign:-1}}></i> из рекомендации {t.linked}</div>}
                  <div className="foot">
                    <span><span className="av sm">{t.assignee}</span></span>
                    <span><i className="ri-calendar-line" style={{verticalAlign:-1}}></i> {t.due}</span>
                  </div>
                </div>
              ))}
            </div>
          ))}
        </div>
      )}

      {view === "list" && (
        <div className="card" style={{padding:0,overflow:"hidden"}}>
          <table>
            <thead><tr><th>ID</th><th>Задача</th><th>Приоритет</th><th>Объект</th><th>Исполнитель</th><th>Срок</th><th>Статус</th></tr></thead>
            <tbody>
              {tasks.map(t=>(
                <tr key={t.id}>
                  <td className="id-mono">{t.id}</td>
                  <td><b style={{font:"600 13px Inter",color:"var(--fg-strong)"}}>{t.title}</b>{t.linked && <span style={{fontSize:11,color:"var(--fg-muted)",marginLeft:8}}>· из {t.linked}</span>}</td>
                  <td><span className={"prio "+t.prio}></span>{t.prio==="high"?"Высокий":t.prio==="med"?"Средний":"Низкий"}</td>
                  <td>{t.site}</td>
                  <td><span className="av sm">{t.assignee}</span></td>
                  <td>{t.due}</td>
                  <td><span className={"status status--"+(t.col==="done"?"ok":t.col==="doing"?"warn":t.col==="review"?"info":"muted")}><span className="dot"></span>{t.col==="todo"?"К выполнению":t.col==="doing"?"В работе":t.col==="review"?"На проверке":"Готово"}</span></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );
}

/* ─── CP-12 Command center ────────────────────────────────── */
function Commands() {
  const [showConfirm, setShowConfirm] = React.useState(false);
  const [pending, setPending] = React.useState([
    { id:"CMD-882", at:"14:32", who:"ИП", action:"Закрыть задвижку", target:"AB1 · B-09", state:"running" },
    { id:"CMD-881", at:"14:18", who:"авто", action:"Открыть задвижки", target:"AB1, AB2 · 14 шт", state:"ok" },
  ]);

  function execute() {
    setShowConfirm(false);
    const cmd = { id:"CMD-883", at:"14:42", who:"ИП", action:"Закрыть задвижки", target:"AB2 · 6 шт", state:"queued" };
    setPending(p=>[cmd,...p]);
  }

  return (
    <div className="page" data-screen-label="12 Команды">
      <div className="page-head">
        <div>
          <div className="page-title">Команды</div>
          <div className="page-sub">Прямое управление устройствами с логом и подтверждением</div>
        </div>
      </div>

      <div className="cmd-grid">
        <div className="cmd-builder">
          <h3 className="card-title" style={{margin:0,marginBottom:14}}>Новая команда</h3>

          <div className="cmd-step">
            <div className="lbl">1 · Цель</div>
            <div className="cmd-target">
              <span className="chip on"><i className="ri-door-open-line"></i>Задвижки</span>
              <span className="chip">TDS</span>
              <span className="chip">Полив</span>
              <span className="chip">Камеры</span>
            </div>
          </div>

          <div className="cmd-step">
            <div className="lbl">2 · Зона</div>
            <div className="cmd-target">
              <span className="chip">AB1</span>
              <span className="chip on">AB2</span>
              <span className="chip">AB3</span>
              <span className="chip">все</span>
            </div>
          </div>

          <div className="cmd-step">
            <div className="lbl">3 · Действие</div>
            <div className="cmd-target">
              <span className="chip on"><i className="ri-lock-line"></i>Закрыть</span>
              <span className="chip"><i className="ri-lock-unlock-line"></i>Открыть</span>
              <span className="chip"><i className="ri-restart-line"></i>Перезагрузить</span>
              <span className="chip"><i className="ri-tools-line"></i>Калибровать</span>
            </div>
          </div>

          <div className="cmd-step" style={{borderStyle:"solid",background:"#1D1C1D",color:"#fff",fontFamily:"ui-monospace,Menlo,monospace",fontSize:12}}>
            <div className="lbl" style={{color:"#FFB300"}}>Превью команды</div>
            <code>{"POST /devices/zone:AB2/gates/close\nbody: { duration: \"4h\", reason: \"chem-treatment\" }\ntargets: 6 devices\nestimated: 12s"}</code>
          </div>

          <div style={{display:"flex",gap:8,marginTop:14}}>
            <button className="btn btn--secondary">Сохранить пресет</button>
            <button className="btn btn--primary" style={{flex:1,justifyContent:"center"}} onClick={()=>setShowConfirm(true)}>
              <i className="ri-shield-check-line"></i>Выполнить (с подтверждением)
            </button>
          </div>
        </div>

        <div className="card" style={{padding:0,overflow:"hidden"}}>
          <h3 className="card-title" style={{padding:"14px 16px",margin:0,borderBottom:"1px solid var(--border)"}}>Активные и недавние</h3>
          <table>
            <thead><tr><th>ID</th><th>Действие</th><th>Цель</th><th>Кем</th><th>Статус</th></tr></thead>
            <tbody>
              {pending.map(c=>(
                <tr key={c.id}>
                  <td className="id-mono">{c.id}<div style={{fontSize:11,color:"var(--fg-muted)"}}>{c.at}</div></td>
                  <td><b>{c.action}</b></td>
                  <td>{c.target}</td>
                  <td>{c.who}</td>
                  <td><span className={"status status--"+(c.state==="ok"?"ok":c.state==="queued"?"info":"warn")}><span className="dot"></span>{c.state==="queued"?"в очереди":c.state==="running"?"в работе":"выполнено"}</span></td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>

      {showConfirm && (
        <div className="modal-overlay" onClick={()=>setShowConfirm(false)}>
          <div className="modal" onClick={(e)=>e.stopPropagation()}>
            <div className="modal-head">
              <div style={{display:"flex",gap:12,alignItems:"flex-start"}}>
                <div style={{width:36,height:36,borderRadius:8,background:"#FCE2E0",color:"#9a1f18",display:"flex",alignItems:"center",justifyContent:"center",fontSize:18}}><i className="ri-error-warning-line"></i></div>
                <div>
                  <div style={{font:"700 16px Inter",color:"var(--fg-strong)"}}>Подтвердите команду</div>
                  <div style={{fontSize:13,color:"var(--fg-muted)",marginTop:2}}>Действие затронет 6 устройств и невозможно отменить</div>
                </div>
              </div>
              <button className="icon-btn" onClick={()=>setShowConfirm(false)}><i className="ri-close-line"></i></button>
            </div>
            <div className="modal-body">
              <dl style={{display:"grid",gridTemplateColumns:"100px 1fr",gap:"6px 12px",margin:0,fontSize:13}}>
                <dt style={{color:"var(--fg-muted)"}}>Действие</dt><dd style={{margin:0,fontWeight:600}}>Закрыть задвижки</dd>
                <dt style={{color:"var(--fg-muted)"}}>Цель</dt><dd style={{margin:0}}>AB2 · 6 ульев</dd>
                <dt style={{color:"var(--fg-muted)"}}>Длительность</dt><dd style={{margin:0}}>4 часа</dd>
                <dt style={{color:"var(--fg-muted)"}}>Причина</dt><dd style={{margin:0}}>Химобработка</dd>
              </dl>
              <div style={{marginTop:14,padding:"10px 12px",background:"#FFE7CF",border:"1px solid #FFB300",borderRadius:8,fontSize:12,color:"#a04200",display:"flex",gap:8}}>
                <i className="ri-information-line" style={{fontSize:14,marginTop:1}}></i>
                <span>Команда требует подтверждения второго пользователя при цели &gt; 5 устройств. Запрос отправлен М. Кузьминой.</span>
              </div>
            </div>
            <div className="modal-foot">
              <button className="btn btn--ghost" onClick={()=>setShowConfirm(false)}>Отменить</button>
              <button className="btn btn--danger" onClick={execute}><i className="ri-shield-check-line"></i>Выполнить</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ─── CP-13 OTA campaigns ─────────────────────────────────── */
function OTA() {
  return (
    <div className="page" data-screen-label="13 OTA">
      <div className="page-head">
        <div>
          <div className="page-title">OTA-обновления</div>
          <div className="page-sub">5 кампаний · 1 в работе · 1 неудача</div>
        </div>
        <div className="actions">
          <button className="btn btn--secondary"><i className="ri-history-line"></i>История</button>
          <button className="btn btn--primary"><i className="ri-add-line"></i>Новая кампания</button>
        </div>
      </div>

      <div className="card" style={{padding:0,overflow:"hidden"}}>
        <div className="ota-row" style={{background:"var(--n-50)",fontWeight:600,fontSize:11,textTransform:"uppercase",letterSpacing:".04em",color:"var(--fg-muted)"}}>
          <div>Кампания</div><div>Цель</div><div>Прогресс</div><div></div><div>Статус</div>
        </div>
        {DATA.ota.map(o=>(
          <div key={o.id} className="ota-row">
            <div>
              <b>{o.name}</b>
              <div className="meta">{o.id} · обновлено сегодня 14:38</div>
            </div>
            <div style={{fontSize:12,color:"var(--fg-muted)"}}>{o.target}</div>
            <div style={{fontVariantNumeric:"tabular-nums",fontSize:12}}>
              <b>{o.ok}</b><span style={{color:"var(--fg-muted)"}}>/{o.total}</span>
              {o.fail>0 && <span style={{color:"#9a1f18",marginLeft:6}}>· {o.fail} ошибки</span>}
            </div>
            <div className={"ota-bar "+(o.status==="failed"?"cri":o.status==="ok"?"ok":"")}><i style={{width:o.progress+"%"}}></i></div>
            <div>
              <span className={"status status--"+(o.status==="ok"?"ok":o.status==="failed"?"critical":o.status==="queued"?"muted":"warn")}>
                <span className="dot"></span>
                {o.status==="ok"?"Готово":o.status==="failed"?"Ошибка":o.status==="queued"?"В очереди":"В работе"}
              </span>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ─── CP-14 Reports ───────────────────────────────────────── */
const REPORT_FILTERS = [
  { id: "all", label: "All", icon: "ri-apps-line" },
  { id: "pollination", label: "Pollination", icon: "ri-bug-line" },
  { id: "microclimate", label: "Microclimate", icon: "ri-temp-hot-line" },
  { id: "irrigation", label: "Irrigation", icon: "ri-drop-line" },
  { id: "devices", label: "Devices", icon: "ri-router-line" },
  { id: "summary", label: "Summary", icon: "ri-bar-chart-line" },
];

function reportTemplateType(templateId) {
  const value = String(templateId || "").toLowerCase();
  if (value.includes("pollination")) return "pollination";
  if (value.includes("microclimate")) return "microclimate";
  if (value.includes("irrigation")) return "irrigation";
  if (value.includes("devices")) return "devices";
  if (value.includes("summary")) return "summary";
  return "other";
}

function reportScheduleLabel(schedule) {
  if (schedule === "daily") return "Ежедневно";
  if (schedule === "weekly") return "Еженедельно";
  if (schedule === "monthly") return "Ежемесячно";
  if (schedule === "on_demand") return "По запросу";
  return "—";
}

function formatReportDate(value) {
  if (!value) return "—";
  const ts = Date.parse(value);
  if (!Number.isFinite(ts)) return String(value);
  return new Date(ts).toLocaleString("ru-RU", {
    day: "2-digit",
    month: "short",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

function normalizeReportSnapshot(snapshot, template) {
  if (!snapshot || typeof snapshot !== "object") return null;
  const summary = snapshot.summary && typeof snapshot.summary === "object" ? snapshot.summary : {};
  const period = snapshot.period && typeof snapshot.period === "object" ? snapshot.period : {};

  return {
    id: snapshot.id || `snap-${Date.now()}`,
    template_id: snapshot.template_id || template?.id || "",
    title: snapshot.title || template?.title || "Отчёт",
    generated_at: snapshot.generated_at || null,
    date_from: snapshot.date_from || period.date_from || null,
    date_to: snapshot.date_to || period.date_to || null,
    greenhouses: Array.isArray(snapshot.greenhouses)
      ? snapshot.greenhouses
      : Array.isArray(summary.greenhouses)
        ? summary.greenhouses
        : [],
    hives: Array.isArray(snapshot.hives)
      ? snapshot.hives
      : Array.isArray(summary.hives)
        ? summary.hives
        : [],
    device_uptime: Array.isArray(snapshot.device_uptime)
      ? snapshot.device_uptime
      : Array.isArray(summary.device_uptime)
        ? summary.device_uptime
        : [],
    irrigation: Array.isArray(snapshot.irrigation)
      ? snapshot.irrigation
      : Array.isArray(summary.irrigation)
        ? summary.irrigation
        : [],
  };
}

function ReportDetail({ snapshot, onExport }) {
  if (!snapshot) return null;

  const hasGreenhouses = Array.isArray(snapshot.greenhouses) && snapshot.greenhouses.length > 0;
  const hasHives = Array.isArray(snapshot.hives) && snapshot.hives.length > 0;
  const hasUptime = Array.isArray(snapshot.device_uptime) && snapshot.device_uptime.length > 0;
  const hasIrrigation = Array.isArray(snapshot.irrigation) && snapshot.irrigation.length > 0;
  const hasAnySection = hasGreenhouses || hasHives || hasUptime || hasIrrigation;

  return (
    <div className="report-detail-panel">
      <div className="report-detail-head">
        <div>
          <div className="report-detail-title">{snapshot.title}</div>
          <div className="report-detail-period">
            Период: {snapshot.date_from || "—"} → {snapshot.date_to || "—"} · Сформирован: {formatReportDate(snapshot.generated_at)}
          </div>
        </div>
        <button className="btn btn--secondary btn--sm" onClick={() => onExport(snapshot)}>
          <i className="ri-download-line"></i>Экспорт
        </button>
      </div>

      {!hasAnySection && (
        <div className="report-empty-row">Сводные таблицы для этого шаблона пока недоступны</div>
      )}

      {hasGreenhouses && (
        <div className="report-detail-section">
          <h4>Сводка по теплицам</h4>
          <table className="report-detail-table">
            <thead>
              <tr>
                <th>Теплица</th>
                <th>Зоны</th>
                <th>Ульи</th>
                <th>Устройства</th>
                <th>T, °C</th>
                <th>RH, %</th>
              </tr>
            </thead>
            <tbody>
              {snapshot.greenhouses.map((row, index) => (
                <tr key={row.greenhouse_id || index}>
                  <td>{row.greenhouse_name || row.greenhouse_id || "—"}</td>
                  <td>{row.zone_count ?? "—"}</td>
                  <td>{row.hive_count ?? "—"}</td>
                  <td>{row.device_count ?? "—"}</td>
                  <td>{row.avg_temperature_c ?? "—"}</td>
                  <td>{row.avg_humidity_pct ?? "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {hasHives && (
        <div className="report-detail-section">
          <h4>Сводка по ульям</h4>
          <table className="report-detail-table">
            <thead>
              <tr>
                <th>Улей</th>
                <th>Теплица</th>
                <th>Зона</th>
                <th>Статус</th>
                <th>Активность/мин</th>
                <th>Пик</th>
              </tr>
            </thead>
            <tbody>
              {snapshot.hives.map((row, index) => (
                <tr key={row.hive_id || index}>
                  <td>{row.hive_name || row.hive_id || "—"}</td>
                  <td>{row.greenhouse_id || "—"}</td>
                  <td>{row.zone_id || "—"}</td>
                  <td>{row.status || "—"}</td>
                  <td>{row.avg_activity_per_min ?? "—"}</td>
                  <td>{row.peak_activity_per_min ?? "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {hasUptime && (
        <div className="report-detail-section">
          <h4>Стабильность устройств</h4>
          <table className="report-detail-table">
            <thead>
              <tr>
                <th>Устройство</th>
                <th>Зона</th>
                <th>Uptime</th>
                <th>Онлайн, мин</th>
                <th>Оффлайн, мин</th>
                <th>Батарея, %</th>
              </tr>
            </thead>
            <tbody>
              {snapshot.device_uptime.map((row, index) => (
                <tr key={row.device_id || index}>
                  <td>{row.device_name || row.device_id || "—"}</td>
                  <td>{row.zone_id || "—"}</td>
                  <td>{row.uptime_ratio != null ? `${Math.round(Number(row.uptime_ratio) * 100)}%` : "—"}</td>
                  <td>{row.uptime_minutes ?? "—"}</td>
                  <td>{row.downtime_minutes ?? "—"}</td>
                  <td>{row.avg_battery_pct ?? "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {hasIrrigation && (
        <div className="report-detail-section">
          <h4>Ирригация</h4>
          <table className="report-detail-table">
            <thead>
              <tr>
                <th>Теплица</th>
                <th>Влажность субстрата, %</th>
                <th>Подача, л</th>
                <th>Feed EC</th>
                <th>Drain EC</th>
              </tr>
            </thead>
            <tbody>
              {snapshot.irrigation.map((row, index) => (
                <tr key={row.greenhouse_id || index}>
                  <td>{row.greenhouse_name || row.greenhouse_id || "—"}</td>
                  <td>{row.avg_substrate_moisture_pct ?? "—"}</td>
                  <td>{row.total_drip_flow_l ?? "—"}</td>
                  <td>{row.avg_feed_ec ?? "—"}</td>
                  <td>{row.avg_drain_ec ?? "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );
}

function Reports() {
  const fallbackTemplates = Array.isArray(DATA?.reports) ? DATA.reports : [];
  const [templates, setTemplates] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [loadError, setLoadError] = React.useState("");
  const [demoMode, setDemoMode] = React.useState(false);
  const [typeFilter, setTypeFilter] = React.useState("all");
  const [detailSnapshot, setDetailSnapshot] = React.useState(null);
  const [exportBanner, setExportBanner] = React.useState("");
  const [modal, setModal] = React.useState({
    open: false,
    template: null,
    dateFrom: "",
    dateTo: "",
    submitting: false,
    progress: 0,
    error: "",
    apiError: "",
  });

  React.useEffect(() => {
    let cancelled = false;

    async function loadTemplates() {
      setLoading(true);
      setLoadError("");
      setDemoMode(false);

      // [TASK_START:T003]
      try {
        const rows = await Keeper.api.reports();
        if (cancelled) return;
        setTemplates(Array.isArray(rows) ? rows : []);
      // [TASK_COMPLETE:T003]
      } catch (error) {
        if (cancelled) return;
        const fallbackRows = Array.isArray(fallbackTemplates) ? fallbackTemplates : [];
        // [TASK_START:T010]
        const isApiError =
          window.Keeper &&
          window.Keeper.ApiError &&
          error instanceof window.Keeper.ApiError;
        if (isApiError) {
          setTemplates(fallbackRows);
          setDemoMode(true);
          setLoadError("Сервис отчётов недоступен · показаны demo-шаблоны");
          setLoading(false);
          return;
        }
        // [TASK_COMPLETE:T010]
        setTemplates(fallbackRows);
        setDemoMode(true);
        setLoadError("Показаны demo-шаблоны отчётов");
      } finally {
        if (!cancelled) setLoading(false);
      }
    }

    loadTemplates();
    return () => {
      cancelled = true;
    };
  }, [fallbackTemplates]);

  React.useEffect(() => {
    if (!modal.submitting) return undefined;
    const timer = setInterval(() => {
      setModal((state) => {
        if (!state.submitting) return state;
        return { ...state, progress: Math.min(92, state.progress + 7) };
      });
    }, 180);
    return () => clearInterval(timer);
  }, [modal.submitting]);

  React.useEffect(() => {
    if (!exportBanner) return undefined;
    const timer = setTimeout(() => setExportBanner(""), 2600);
    return () => clearTimeout(timer);
  }, [exportBanner]);

  const filteredTemplates = React.useMemo(() => {
    // [TASK_START:T004]
    if (typeFilter === "all") return templates;
    return templates.filter((template) => reportTemplateType(template.id) === typeFilter);
    // [TASK_COMPLETE:T004]
  }, [templates, typeFilter]);

  const scheduledCount = templates.filter((row) => row.schedule && row.schedule !== "on_demand").length;
  const lastRunAt = templates
    .map((row) => row.last_run_at)
    .filter(Boolean)
    .sort()
    .slice(-1)[0];

  function openGenerateModal(template) {
    setModal({
      open: true,
      template,
      dateFrom: "",
      dateTo: "",
      submitting: false,
      progress: 0,
      error: "",
      apiError: "",
    });
  }

  function closeGenerateModal() {
    if (modal.submitting) return;
    setModal({
      open: false,
      template: null,
      dateFrom: "",
      dateTo: "",
      submitting: false,
      progress: 0,
      error: "",
      apiError: "",
    });
  }

  async function confirmGenerateReport() {
    if (!modal.template || modal.submitting) return;
    // [TASK_START:T009]
    if (modal.dateFrom && modal.dateTo && modal.dateFrom > modal.dateTo) {
      setModal((state) => ({
        ...state,
        error: "Дата начала не может быть позже даты окончания",
      }));
      return;
    }
    // [TASK_COMPLETE:T009]

    // [TASK_START:T005]
    const payload = {};
    if (modal.dateFrom) payload.date_from = modal.dateFrom;
    if (modal.dateTo) payload.date_to = modal.dateTo;
    setModal((state) => ({
      ...state,
      submitting: true,
      progress: 6,
      error: "",
      apiError: "",
    }));
    try {
      const snapshot = await Keeper.api.runReport(modal.template.id, payload);
      setModal((state) => ({ ...state, progress: 100, submitting: false }));
      // [TASK_START:T007]
      const normalized = normalizeReportSnapshot(snapshot, modal.template);
      setDetailSnapshot(normalized);
      setModal({
        open: false,
        template: null,
        dateFrom: "",
        dateTo: "",
        submitting: false,
        progress: 0,
        error: "",
        apiError: "",
      });
      // [TASK_COMPLETE:T007]
    } catch (error) {
      setModal((state) => ({
        ...state,
        submitting: false,
        progress: 0,
        apiError:
          (error && error.backendMessage) ||
          "Не удалось сформировать отчёт. Повторите попытку.",
      }));
    }
    // [TASK_COMPLETE:T005]
  }

  function handleExportPlaceholder(snapshot) {
    setExportBanner(`Экспорт ${snapshot.title} будет доступен в следующем обновлении`);
  }

  return (
    <div className="page" data-screen-label="14 Отчёты">
      <div className="page-head">
        <div>
          <div className="page-title">Отчёты</div>
          <div className="page-sub">
            {templates.length} шаблонов · {scheduledCount} запланированы · последний {formatReportDate(lastRunAt)}
          </div>
        </div>
        <div className="actions">
          <button className="btn btn--secondary"><i className="ri-history-line"></i>Архив (37)</button>
          <button className="btn btn--primary"><i className="ri-add-line"></i>Новый шаблон</button>
        </div>
      </div>

      {demoMode && (
        <div className="report-demo-banner">
          <i className="ri-flask-line"></i> Demo mode: отчёты загружены из локальных данных
        </div>
      )}
      {loadError && <div className="report-load-error">{loadError}</div>}
      {exportBanner && <div className="report-export-banner"><i className="ri-download-line"></i> {exportBanner}</div>}

      <div className="report-filter-row">
        {REPORT_FILTERS.map((item) => (
          <button
            key={item.id}
            className={`chip ${typeFilter === item.id ? "on" : ""}`}
            onClick={() => setTypeFilter(item.id)}
          >
            <i className={item.icon}></i>
            {item.label}
          </button>
        ))}
      </div>

      {loading && (
        <div className="report-empty-state">
          <i className="ri-loader-4-line spin"></i>
          <div>Загрузка шаблонов…</div>
        </div>
      )}

      {!loading && templates.length === 0 && (
        // [TASK_START:T008]
        <div className="report-empty-state">
          <i className="ri-file-list-3-line"></i>
          <div>Шаблоны не загружены</div>
        </div>
        // [TASK_COMPLETE:T008]
      )}

      {!loading && templates.length > 0 && filteredTemplates.length === 0 && (
        <div className="report-empty-state">
          <i className="ri-filter-3-line"></i>
          <div>Нет шаблонов для выбранного фильтра</div>
        </div>
      )}

      {!loading && filteredTemplates.length > 0 && (
        <div className="report-grid">
          {filteredTemplates.map((report) => (
            <div key={report.id} className="report-tile">
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
                <div className="ic"><i className={report.icon}></i></div>
                <button className="icon-btn" style={{ width: 28, height: 28 }}><i className="ri-more-fill"></i></button>
              </div>
              <h4>{report.title}</h4>
              <div className="desc">{report.description || "—"}</div>
              <div className="foot">
                <span><i className="ri-time-line" style={{ verticalAlign: -1 }}></i> {reportScheduleLabel(report.schedule)}</span>
                <span>посл. {formatReportDate(report.last_run_at)}</span>
              </div>
              <div style={{ display: "flex", gap: 6 }}>
                <button className="btn btn--secondary btn--sm" style={{ flex: 1, justifyContent: "center" }}>
                  <i className="ri-edit-line"></i>Шаблон
                </button>
                <button
                  className="btn btn--primary btn--sm"
                  style={{ flex: 1, justifyContent: "center" }}
                  onClick={() => openGenerateModal(report)}
                >
                  <i className="ri-play-line"></i>Сгенерировать
                </button>
              </div>
            </div>
          ))}
        </div>
      )}

      {/* [TASK_START:T006] */}
      <ReportDetail snapshot={detailSnapshot} onExport={handleExportPlaceholder} />
      {/* [TASK_COMPLETE:T006] */}

      {modal.open && (
        <div className="modal-overlay" onClick={closeGenerateModal}>
          <div className="modal" onClick={(event) => event.stopPropagation()}>
            <div className="modal-head">
              <div>
                <div style={{ font: "700 16px Inter" }}>Генерация отчёта</div>
                <div style={{ fontSize: 12, color: "var(--fg-muted)", marginTop: 2 }}>{modal.template?.title}</div>
              </div>
            </div>
            <div className="modal-body">
              <div className="report-date-row">
                <label>
                  <span>Дата начала</span>
                  <input
                    className="report-date-input"
                    type="date"
                    value={modal.dateFrom}
                    onChange={(event) => setModal((state) => ({ ...state, dateFrom: event.target.value, error: "" }))}
                    disabled={modal.submitting}
                  />
                </label>
                <label>
                  <span>Дата окончания</span>
                  <input
                    className="report-date-input"
                    type="date"
                    value={modal.dateTo}
                    onChange={(event) => setModal((state) => ({ ...state, dateTo: event.target.value, error: "" }))}
                    disabled={modal.submitting}
                  />
                </label>
              </div>
              {modal.error && <div className="report-generate-error">{modal.error}</div>}
              {modal.apiError && <div className="report-generate-error">{modal.apiError}</div>}
              <div className="report-progress-wrap">
                <div className="report-progress-bar">
                  <div className="report-progress-fill" style={{ width: `${modal.progress}%` }}></div>
                </div>
                <div className="report-progress-text">
                  {modal.progress < 30
                    ? "Сбор данных за период…"
                    : modal.progress < 65
                      ? "Агрегация по теплицам…"
                      : modal.progress < 95
                        ? "Формирование сводки…"
                        : "Готово"}
                  {" · "}
                  {modal.progress} %
                </div>
              </div>
            </div>
            <div className="modal-foot">
              <button className="btn btn--secondary" onClick={closeGenerateModal} disabled={modal.submitting}>
                Отмена
              </button>
              <button className="btn btn--primary" onClick={confirmGenerateReport} disabled={modal.submitting}>
                <i className="ri-play-line"></i>Сформировать
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ─── CP-15 Settings ──────────────────────────────────────── */
function _asTrimmedString(value, fallback = "") {
  if (typeof value === "string") return value.trim();
  if (value === null || value === undefined) return fallback;
  return String(value).trim();
}

function _asBool(value, fallback = false) {
  if (typeof value === "boolean") return value;
  return fallback;
}

function _normalizeSettingsPayload(payload) {
  const root = payload && typeof payload === "object" ? payload : {};
  const profile = root.profile && typeof root.profile === "object" ? root.profile : {};
  const organization = root.organization && typeof root.organization === "object" ? root.organization : {};
  const notifications = root.notifications && typeof root.notifications === "object" ? root.notifications : {};
  const thresholds = root.thresholds && typeof root.thresholds === "object" ? root.thresholds : {};
  const deviceConfig = root.device_config && typeof root.device_config === "object" ? root.device_config : {};

  const language = _asTrimmedString(profile.language || organization.locale || "ru", "ru").toLowerCase();
  const locale = _asTrimmedString(organization.locale || language || "ru", "ru").toLowerCase();

  return {
    profile: {
      language: language || "ru",
      timezone: _asTrimmedString(profile.timezone || organization.timezone || "UTC", "UTC"),
    },
    organization: {
      tenant_id: _asTrimmedString(organization.tenant_id || DATA?.activeTenant?.id || "", ""),
      name: _asTrimmedString(organization.name || DATA?.activeTenant?.name || "Ahal Agro Park", "Ahal Agro Park"),
      country: _asTrimmedString(organization.country, ""),
      timezone: _asTrimmedString(organization.timezone || profile.timezone || "UTC", "UTC"),
      locale: locale || "ru",
    },
    notifications: {
      enabled: _asBool(notifications.enabled, true),
      alert_email: _asTrimmedString(notifications.alert_email || "", ""),
      digest_email: _asBool(notifications.digest_email, true),
      push: _asBool(notifications.push, true),
    },
    thresholds: thresholds,
    device_config: deviceConfig,
  };
}

function _orgDraftFromSettings(settings) {
  const source = settings && settings.organization ? settings.organization : {};
  const language = _asTrimmedString(settings?.profile?.language || source.locale || "ru", "ru").toLowerCase();
  return {
    name: _asTrimmedString(source.name || "Ahal Agro Park", "Ahal Agro Park"),
    timezone: _asTrimmedString(source.timezone || "UTC", "UTC"),
    language: language || "ru",
  };
}

function _notificationsDraftFromSettings(settings) {
  const source = settings && settings.notifications ? settings.notifications : {};
  return {
    enabled: _asBool(source.enabled, true),
    push: _asBool(source.push, true),
    digest_email: _asBool(source.digest_email, true),
    alert_email: _asTrimmedString(source.alert_email || "", ""),
  };
}

function _languageLocale(language) {
  const normalized = _asTrimmedString(language || "ru", "ru").toLowerCase();
  if (normalized === "tk") return "tk";
  if (normalized === "en") return "en";
  return "ru";
}

function _formatFieldError(error) {
  if (typeof Keeper?.extractApiFailure !== "function") return "";
  const failure = Keeper.extractApiFailure(error);
  if (!failure) return "";
  if (!Array.isArray(failure.details)) return "";
  const first = failure.details.find((item) => item && typeof item === "object");
  if (!first) return "";
  const field = _asTrimmedString(first.field || "", "");
  const reason = _asTrimmedString(first.reason || first.message || "", "");
  if (!field || !reason) return "";
  return `${field}: ${reason}`;
}

function Settings() {
  const [tab, setTab] = React.useState("org");
  const tabs = [
    { id:"org",      label:"Организация",  icon:"ri-building-line" },
    { id:"members",  label:"Пользователи", icon:"ri-team-line" },
    { id:"integ",    label:"Интеграции",   icon:"ri-plug-line" },
    { id:"notif",    label:"Уведомления",  icon:"ri-notification-3-line" },
    { id:"api",      label:"API-ключи",    icon:"ri-key-2-line" },
  ];
  const [loading, setLoading] = React.useState(true);
  const [loadError, setLoadError] = React.useState("");
  const [settings, setSettings] = React.useState(() => _normalizeSettingsPayload(null));
  const [orgDraft, setOrgDraft] = React.useState(() => _orgDraftFromSettings(_normalizeSettingsPayload(null)));
  const [notifDraft, setNotifDraft] = React.useState(() => _notificationsDraftFromSettings(_normalizeSettingsPayload(null)));
  const [orgErrors, setOrgErrors] = React.useState({ name: "", timezone: "" });
  const [notifErrors, setNotifErrors] = React.useState({ alert_email: "" });
  const [saveState, setSaveState] = React.useState({
    org: { submitting: false, message: "", error: "" },
    notif: { submitting: false, message: "", error: "" },
  });

  React.useEffect(() => {
    let cancelled = false;
    async function load() {
      setLoading(true);
      setLoadError("");
      try {
        const payload = await Keeper.api.settings();
        if (cancelled) return;
        const normalized = _normalizeSettingsPayload(payload);
        setSettings(normalized);
        setOrgDraft(_orgDraftFromSettings(normalized));
        setNotifDraft(_notificationsDraftFromSettings(normalized));
      } catch (error) {
        if (cancelled) return;
        const fallback = _normalizeSettingsPayload(null);
        setSettings(fallback);
        setOrgDraft(_orgDraftFromSettings(fallback));
        setNotifDraft(_notificationsDraftFromSettings(fallback));
        const formatted = typeof Keeper.formatApiFailure === "function"
          ? Keeper.formatApiFailure(error, { fallbackMessage: "Настройки временно недоступны" })
          : "Настройки временно недоступны";
        setLoadError(formatted);
      } finally {
        if (!cancelled) setLoading(false);
      }
    }
    load();
    return () => { cancelled = true; };
  }, []);

  const orgDirty = React.useMemo(() => {
    const baseline = _orgDraftFromSettings(settings);
    return baseline.name !== orgDraft.name
      || baseline.timezone !== orgDraft.timezone
      || baseline.language !== orgDraft.language;
  }, [settings, orgDraft]);

  const notifDirty = React.useMemo(() => {
    const baseline = _notificationsDraftFromSettings(settings);
    return baseline.enabled !== notifDraft.enabled
      || baseline.push !== notifDraft.push
      || baseline.digest_email !== notifDraft.digest_email
      || baseline.alert_email !== notifDraft.alert_email;
  }, [settings, notifDraft]);

  function validateOrgDraft(draft) {
    const normalized = {
      name: _asTrimmedString(draft.name, ""),
      timezone: _asTrimmedString(draft.timezone, ""),
      language: _languageLocale(draft.language),
    };
    const nextErrors = {
      name: normalized.name.length >= 2 ? "" : "Введите название (минимум 2 символа)",
      timezone: normalized.timezone ? "" : "Выберите часовой пояс",
    };
    setOrgErrors(nextErrors);
    return {
      normalized,
      valid: !nextErrors.name && !nextErrors.timezone,
    };
  }

  function validateNotificationsDraft(draft) {
    const normalized = {
      enabled: Boolean(draft.enabled),
      push: Boolean(draft.push),
      digest_email: Boolean(draft.digest_email),
      alert_email: _asTrimmedString(draft.alert_email || "", ""),
    };
    const hasAlertEmail = normalized.alert_email.length > 0;
    const emailOk = !hasAlertEmail || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalized.alert_email);
    const nextErrors = {
      alert_email: emailOk ? "" : "Введите корректный email для аварийных уведомлений",
    };
    setNotifErrors(nextErrors);
    return {
      normalized,
      valid: !nextErrors.alert_email,
    };
  }

  async function saveOrganization() {
    const checked = validateOrgDraft(orgDraft);
    if (!checked.valid) return;

    const previousSettings = settings;
    const previousOrgDraft = orgDraft;
    const locale = _languageLocale(checked.normalized.language);
    const optimistic = {
      ...settings,
      profile: {
        ...settings.profile,
        language: checked.normalized.language,
        timezone: checked.normalized.timezone,
      },
      organization: {
        ...settings.organization,
        name: checked.normalized.name,
        timezone: checked.normalized.timezone,
        locale,
      },
    };

    setSaveState((state) => ({
      ...state,
      org: { submitting: true, message: "Сохраняем…", error: "" },
    }));
    setSettings(optimistic);
    setOrgDraft(_orgDraftFromSettings(optimistic));

    try {
      const response = await Keeper.api.saveSettingsOrganization({
        name: checked.normalized.name,
        timezone: checked.normalized.timezone,
        locale,
      });
      const normalized = _normalizeSettingsPayload(response);
      setSettings(normalized);
      setOrgDraft(_orgDraftFromSettings(normalized));
      setSaveState((state) => ({
        ...state,
        org: { submitting: false, message: "Сохранено", error: "" },
      }));
    } catch (error) {
      setSettings(previousSettings);
      setOrgDraft(previousOrgDraft);
      const baseMessage = typeof Keeper.formatApiFailure === "function"
        ? Keeper.formatApiFailure(error, { fallbackMessage: "Не удалось сохранить настройки организации" })
        : "Не удалось сохранить настройки организации";
      const fieldMessage = _formatFieldError(error);
      setSaveState((state) => ({
        ...state,
        org: {
          submitting: false,
          message: "",
          error: fieldMessage ? `${baseMessage} · ${fieldMessage}` : baseMessage,
        },
      }));
    }
  }

  async function saveNotifications() {
    const checked = validateNotificationsDraft(notifDraft);
    if (!checked.valid) return;

    const previousSettings = settings;
    const previousNotifDraft = notifDraft;
    const optimistic = {
      ...settings,
      notifications: {
        ...settings.notifications,
        ...checked.normalized,
      },
    };

    setSaveState((state) => ({
      ...state,
      notif: { submitting: true, message: "Сохраняем…", error: "" },
    }));
    setSettings(optimistic);
    setNotifDraft(_notificationsDraftFromSettings(optimistic));

    try {
      const response = await Keeper.api.saveSettingsNotifications({
        enabled: checked.normalized.enabled,
        alert_email: checked.normalized.alert_email || null,
        digest_email: checked.normalized.digest_email,
        push: checked.normalized.push,
      });
      const normalized = _normalizeSettingsPayload(response);
      setSettings(normalized);
      setNotifDraft(_notificationsDraftFromSettings(normalized));
      setSaveState((state) => ({
        ...state,
        notif: { submitting: false, message: "Сохранено", error: "" },
      }));
    } catch (error) {
      setSettings(previousSettings);
      setNotifDraft(previousNotifDraft);
      const baseMessage = typeof Keeper.formatApiFailure === "function"
        ? Keeper.formatApiFailure(error, { fallbackMessage: "Не удалось сохранить настройки уведомлений" })
        : "Не удалось сохранить настройки уведомлений";
      const fieldMessage = _formatFieldError(error);
      setSaveState((state) => ({
        ...state,
        notif: {
          submitting: false,
          message: "",
          error: fieldMessage ? `${baseMessage} · ${fieldMessage}` : baseMessage,
        },
      }));
    }
  }

  const tzOptions = [
    { value: "Asia/Ashgabat", label: "UTC+05:00 Ашхабад" },
    { value: "Europe/Amsterdam", label: "UTC+01:00 Амстердам" },
    { value: "Europe/Madrid", label: "UTC+01:00 Мадрид" },
    { value: "UTC", label: "UTC+00:00" },
  ];

  const inputStyle = {
    height: 36,
    border: "1px solid var(--border)",
    borderRadius: 8,
    padding: "0 12px",
    font: "500 14px Inter",
    width: 300,
  };

  return (
    <div className="page" data-screen-label="15 Настройки">
      <div className="page-head">
        <div>
          <div className="page-title">Настройки</div>
          <div className="page-sub">Управление организацией и доступом</div>
        </div>
      </div>

      <div className="settings-shell">
        <div className="settings-nav">
          {tabs.map(t=>(
            <button key={t.id} className={tab===t.id?"on":""} onClick={()=>setTab(t.id)}>
              <i className={t.icon}></i>{t.label}
            </button>
          ))}
        </div>

        <div className="card settings-form">
          {loading && <div className="settings-banner">Загрузка настроек…</div>}
          {!loading && loadError && <div className="settings-banner settings-banner--error">{loadError}</div>}

          {tab==="org" && (<>
            <h3 className="card-title" style={{marginBottom:8}}>Организация</h3>
            <div className="settings-save-row">
              <div className="settings-save-note">Изменения применяются к активному tenant</div>
              <div className="settings-save-actions">
                {saveState.org.error && <span className="settings-inline-error">{saveState.org.error}</span>}
                {!saveState.org.error && saveState.org.message && (
                  <span className="settings-inline-ok">{saveState.org.message}</span>
                )}
                <button
                  className="btn btn--primary btn--sm"
                  onClick={saveOrganization}
                  disabled={loading || saveState.org.submitting || !orgDirty}
                >
                  {saveState.org.submitting ? "Сохранение..." : "Сохранить"}
                </button>
              </div>
            </div>
            <div className="row">
              <div><div className="l">Название</div><div className="desc">Видно всем участникам</div></div>
              <div>
                <input
                  value={orgDraft.name}
                  onChange={(event) => {
                    setOrgDraft((state) => ({ ...state, name: event.target.value }));
                    setOrgErrors((state) => ({ ...state, name: "" }));
                    setSaveState((state) => ({ ...state, org: { ...state.org, message: "", error: "" } }));
                  }}
                  style={{
                    ...inputStyle,
                    borderColor: orgErrors.name ? "#E5342B" : "var(--border)",
                  }}
                />
                {orgErrors.name && <div className="settings-field-error">{orgErrors.name}</div>}
              </div>
            </div>
            <div className="row">
              <div><div className="l">Часовой пояс</div><div className="desc">Используется для расписаний и отчётов</div></div>
              <div>
                <select
                  value={orgDraft.timezone}
                  onChange={(event) => {
                    setOrgDraft((state) => ({ ...state, timezone: event.target.value }));
                    setOrgErrors((state) => ({ ...state, timezone: "" }));
                    setSaveState((state) => ({ ...state, org: { ...state.org, message: "", error: "" } }));
                  }}
                  style={{
                    ...inputStyle,
                    borderColor: orgErrors.timezone ? "#E5342B" : "var(--border)",
                  }}
                >
                  {tzOptions.map((option) => (
                    <option key={option.value} value={option.value}>{option.label}</option>
                  ))}
                </select>
                {orgErrors.timezone && <div className="settings-field-error">{orgErrors.timezone}</div>}
              </div>
            </div>
            <div className="row">
              <div><div className="l">Язык интерфейса</div><div className="desc">По умолчанию для новых пользователей</div></div>
              <div>
                <div className="segmented">
                  <button
                    className={orgDraft.language === "ru" ? "on" : ""}
                    onClick={() => {
                      setOrgDraft((state) => ({ ...state, language: "ru" }));
                      setSaveState((state) => ({ ...state, org: { ...state.org, message: "", error: "" } }));
                    }}
                  >
                    Русский
                  </button>
                  <button
                    className={orgDraft.language === "en" ? "on" : ""}
                    onClick={() => {
                      setOrgDraft((state) => ({ ...state, language: "en" }));
                      setSaveState((state) => ({ ...state, org: { ...state.org, message: "", error: "" } }));
                    }}
                  >
                    English
                  </button>
                  <button
                    className={orgDraft.language === "tk" ? "on" : ""}
                    onClick={() => {
                      setOrgDraft((state) => ({ ...state, language: "tk" }));
                      setSaveState((state) => ({ ...state, org: { ...state.org, message: "", error: "" } }));
                    }}
                  >
                    Türkmen
                  </button>
                </div>
              </div>
            </div>
            <div className="row">
              <div><div className="l">Лого</div><div className="desc">Используется в шапке и отчётах</div></div>
              <div style={{display:"flex",gap:10,alignItems:"center"}}>
                <div style={{width:64,height:64,borderRadius:8,background:"#FFB300",display:"flex",alignItems:"center",justifyContent:"center",fontSize:32}}>🐝</div>
                <button className="btn btn--secondary btn--sm">Загрузить</button>
              </div>
            </div>
          </>)}

          {tab==="members" && (<>
            <h3 className="card-title">Пользователи <button className="btn btn--primary btn--sm"><i className="ri-add-line"></i>Пригласить</button></h3>
            <table style={{marginTop:8}}>
              <thead><tr><th>Имя</th><th>Email</th><th>Роль</th><th>Активность</th><th></th></tr></thead>
              <tbody>
                {DATA.members.map((m,i)=>(
                  <tr key={i}>
                    <td><div style={{display:"flex",gap:10,alignItems:"center"}}><span className="av sm">{m.init}</span><b style={{font:"600 13px Inter",color:"var(--fg-strong)"}}>{m.name}</b></div></td>
                    <td className="id-mono">{m.email}</td>
                    <td><span className={"status status--"+(m.role==="Администратор"?"warn":m.role==="Только просмотр"?"muted":"info")}><span className="dot"></span>{m.role}</span></td>
                    <td>{m.last}</td>
                    <td><button className="icon-btn" style={{width:28,height:28,border:"none",background:"transparent"}}><i className="ri-more-fill"></i></button></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </>)}

          {tab==="integ" && (<>
            <h3 className="card-title" style={{marginBottom:8}}>Интеграции</h3>
            {[
              { n:"Telegram", desc:"Уведомления и команды через бот", on:true, icon:"ri-telegram-line" },
              { n:"Email (SMTP)", desc:"Транзакционные письма через ваш SMTP", on:true, icon:"ri-mail-line" },
              { n:"1С: Предприятие", desc:"Выгрузка наряд-задач и расходников", on:false, icon:"ri-file-text-line" },
              { n:"S3 / MinIO", desc:"Архив отчётов и снимков с камер", on:true, icon:"ri-database-2-line" },
              { n:"Webhook", desc:"Push событий во внешние системы", on:false, icon:"ri-link" },
            ].map((it,i)=>(
              <div key={i} className="row">
                <div style={{display:"flex",gap:10,alignItems:"center"}}>
                  <div style={{width:32,height:32,background:"var(--n-100)",borderRadius:8,display:"flex",alignItems:"center",justifyContent:"center",color:"var(--fg)"}}><i className={it.icon}></i></div>
                  <div><div className="l">{it.n}</div><div className="desc">{it.desc}</div></div>
                </div>
                <div style={{display:"flex",gap:10,alignItems:"center",justifyContent:"flex-end"}}>
                  <button className="btn btn--secondary btn--sm">Настроить</button>
                  <div className={"switch"+(it.on?" on":"")}></div>
                </div>
              </div>
            ))}
          </>)}

          {tab==="notif" && (<>
            <h3 className="card-title" style={{marginBottom:8}}>Уведомления</h3>
            <div className="settings-save-row">
              <div className="settings-save-note">Выберите каналы и email для аварийных сообщений</div>
              <div className="settings-save-actions">
                {saveState.notif.error && <span className="settings-inline-error">{saveState.notif.error}</span>}
                {!saveState.notif.error && saveState.notif.message && (
                  <span className="settings-inline-ok">{saveState.notif.message}</span>
                )}
                <button
                  className="btn btn--primary btn--sm"
                  onClick={saveNotifications}
                  disabled={loading || saveState.notif.submitting || !notifDirty}
                >
                  {saveState.notif.submitting ? "Сохранение..." : "Сохранить"}
                </button>
              </div>
            </div>
            {[
              {key:"enabled", n:"Включить уведомления", desc:"Глобальный переключатель для всех каналов"},
              {key:"push", n:"Push в приложении", desc:"Мгновенные уведомления для операторов"},
              {key:"digest_email", n:"Email-дайджест", desc:"Ежедневная сводка по событиям и статусам"},
            ].map((it)=>(
              <div key={it.key} className="row">
                <div><div className="l">{it.n}</div><div className="desc">{it.desc}</div></div>
                <div style={{display:"flex",justifyContent:"flex-end"}}>
                  <button
                    className={"settings-toggle " + (notifDraft[it.key] ? "on" : "off")}
                    onClick={() => {
                      setNotifDraft((state) => ({ ...state, [it.key]: !state[it.key] }));
                      setSaveState((state) => ({ ...state, notif: { ...state.notif, message: "", error: "" } }));
                    }}
                  >
                    {notifDraft[it.key] ? "Вкл" : "Выкл"}
                  </button>
                </div>
              </div>
            ))}
            <div className="row">
              <div><div className="l">Email для аварий</div><div className="desc">Куда отправлять критические уведомления</div></div>
              <div>
                <input
                  value={notifDraft.alert_email}
                  onChange={(event) => {
                    setNotifDraft((state) => ({ ...state, alert_email: event.target.value }));
                    setNotifErrors((state) => ({ ...state, alert_email: "" }));
                    setSaveState((state) => ({ ...state, notif: { ...state.notif, message: "", error: "" } }));
                  }}
                  placeholder="ops@example.com"
                  style={{
                    ...inputStyle,
                    borderColor: notifErrors.alert_email ? "#E5342B" : "var(--border)",
                  }}
                />
                {notifErrors.alert_email && <div className="settings-field-error">{notifErrors.alert_email}</div>}
              </div>
            </div>
          </>)}

          {tab==="api" && (<>
            <h3 className="card-title">API-ключи <button className="btn btn--primary btn--sm"><i className="ri-add-line"></i>Создать</button></h3>
            <table style={{marginTop:8}}>
              <thead><tr><th>Имя</th><th>Префикс</th><th>Создан</th><th>Последний запрос</th><th></th></tr></thead>
              <tbody>
                <tr><td><b>Grafana readonly</b></td><td className="id-mono">kpr_8a2f...</td><td>14 янв 2026</td><td>5 мин назад</td><td><span className="status status--ok"><span className="dot"></span>Активен</span></td></tr>
                <tr><td><b>1С интеграция</b></td><td className="id-mono">kpr_771c...</td><td>2 ноя 2025</td><td>1 ч назад</td><td><span className="status status--ok"><span className="dot"></span>Активен</span></td></tr>
                <tr><td><b>Старый ключ Zabbix</b></td><td className="id-mono">kpr_4421...</td><td>17 авг 2025</td><td>—</td><td><span className="status status--muted"><span className="dot"></span>Отозван</span></td></tr>
              </tbody>
            </table>
          </>)}
        </div>
      </div>
    </div>
  );
}

window.Telemetry = Telemetry;
window.Microclimate = Microclimate;
window.Recommendations = Recommendations;
window.RecommendationsPanel = RecommendationsPanel;
window.TasksKanban = TasksKanban;
window.Commands = Commands;
window.OTA = OTA;
window.Reports = Reports;
window.Settings = Settings;
