// Reusable UI components const { useState, useEffect, useRef, useMemo } = React; // Generic avatar circle with initials function Avatar({ initials, color, size = 36, ring = false }) { return (
{initials}
); } function StatusBadge({ status }) { const map = { new: { label: "Новый", cls: "bg-[#4F8EF7]/15 text-[#7BA8F9] border-[#4F8EF7]/30" }, in_progress: { label: "В работе", cls: "bg-[#eab308]/15 text-[#eab308] border-[#eab308]/30" }, closed: { label: "Закрыт", cls: "bg-zinc-500/15 text-zinc-400 border-zinc-600/40" }, }; const cfg = map[status] || map.new; return ( {cfg.label} ); } function PlanBadge({ plan }) { const map = { Pro: "bg-gradient-to-r from-[#A855F7] to-[#4F8EF7] text-white", Basic: "bg-[#1f2a44] text-[#7BA8F9] border border-[#4F8EF7]/30", Trial: "bg-[#3d3320] text-[#eab308] border border-[#eab308]/30", }; return ( {plan} ); } function SubStatus({ status }) { const map = { active: { label: "Активна", color: "text-[#22c55e]", dot: "bg-[#22c55e]" }, expiring: { label: "Истекает", color: "text-[#eab308]", dot: "bg-[#eab308]" }, blocked: { label: "Заблокирована", color: "text-[#ef4444]", dot: "bg-[#ef4444]" }, }; const cfg = map[status] || map.active; return ( {cfg.label} ); } // Icons (inline SVG, stroke-based) function Icon({ name, className = "w-4 h-4", strokeWidth = 1.75 }) { const paths = { search: <>, bell: <>, menu: <>, send: <>, paperclip: <>, chevronDown: <>, chevronRight: <>, x: <>, check: <>, plus: <>, edit: <>, trash: <>, chat: <>, chart: <>, settings: <>, bellRing: <>, image: <>, sparkles: <>, user: <>, refresh: <>, key: <>, plus2: <>, arrowLeft: <>, arrowRight: <>, calendar: <>, operators: <>, book: <>, clock: <>, server: <>, }; return ( {paths[name]} ); } function Toast({ msg }) { if (!msg) return null; return (
{msg}
); } Object.assign(window, { Avatar, StatusBadge, PlanBadge, SubStatus, Icon, Toast });