// Telegram slide-over, toast system, and shared status helpers for the orders register. const DEMO_TODAY = { d: 31, m: 7, y: 2025 }; // "сегодня" для демо const STATUS_META = { draft: { label: "Черновик", cls: "muted", icon: I.Edit, dot: "var(--ink-400)" }, sent: { label: "Отправлено", cls: "tg", icon: I.Plane, dot: "#2682c0" }, accepted: { label: "Принято", cls: "accepted", icon: I.CheckCircle, dot: "#5546b0" }, in_progress: { label: "В работе", cls: "warn", icon: I.Hourglass, dot: "var(--warn)" }, overdue: { label: "Просрочено", cls: "err", icon: I.Alert, dot: "var(--err)" }, done: { label: "Выполнено", cls: "ok", icon: I.CheckCircle, dot: "var(--ok)" }, cancelled: { label: "Отменено", cls: "muted", icon: I.X, dot: "var(--ink-300)" }, }; const DEPT_COLORS = { "Юридический отдел": "#3a5f9e", "Бухгалтерия": "#1f7a4c", "Отдел кадров": "#a8612e", "Канцелярия": "#7a5aa6", "Отдел контроля": "#b13b46", "Руководство": "#36465e", }; function parseRuDate(s) { if (!s) return null; const m = String(s).match(/(\d{1,2})\.(\d{1,2})\.(\d{4})/); if (!m) return null; return { d: +m[1], m: +m[2], y: +m[3] }; } function dateValue(dt) { return dt ? dt.y * 10000 + dt.m * 100 + dt.d : 0; } function daysBetween(a, b) { const ms = Date.UTC(a.y, a.m - 1, a.d) - Date.UTC(b.y, b.m - 1, b.d); return Math.round(ms / 86400000); } // "Today" comes from the server (window.SERVER_TODAY) when available, else the demo date. function effToday() { return window.SERVER_TODAY || DEMO_TODAY; } // Effective status — promotes stale open tasks to "overdue" function effStatus(task) { if (task.status === "done" || task.status === "draft" || task.status === "cancelled") return task.status; const ord = parseRuDate(task.dateOrder) || parseRuDate(task.dateStart); if (ord && daysBetween(effToday(), ord) > 25) return "overdue"; return task.status; } function StatusPill({ status }) { const meta = STATUS_META[status] || STATUS_META.draft; const Ic = meta.icon; return ( {meta.label} ); } // ---------- Toasts ---------- function Toasts({ items }) { return (
{items.map(t => (
{t.icon} {t.text}
))}
); } // ---------- Telegram panel ---------- // Real task-communication panel: shows the live note/question thread (executor // questions from Telegram + site replies) and lets the controller send a message // to the executor (saved as a note and pushed to their Telegram chat). function TelegramPanel({ open, person, task, notes, onClose, onAccept, onDone, onSendNote }) { const bodyRef = React.useRef(null); const [draft, setDraft] = React.useState(""); const list = notes || []; React.useEffect(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [list.length, open]); const st = task ? task.status : null; const eff = task ? effStatus(task) : "draft"; const canAccept = st === "sent"; const canDone = st === "sent" || st === "accepted" || st === "in_progress"; const due = task ? (task.dateEnd || task.dateStart || "—") : "—"; const send = () => { if (draft.trim() && onSendNote) { onSendNote(draft.trim()); setDraft(""); } }; return ( <>
); } Object.assign(window, { STATUS_META, DEPT_COLORS, DEMO_TODAY, parseRuDate, dateValue, effStatus, StatusPill, Toasts, TelegramPanel });