const { Card, Icon, IconButton, Button, Badge, Modal, Select, Input, EmptyState, DropdownMenu, StatusPill } = window.ACRFlowDesignSystem_ba1e9c; const CHARTS = [ { value: "lista", label: "Lista de demandas", icon: "list" }, { value: "tabela", label: "Tabela detalhada", icon: "table-2" }, { value: "numeros", label: "Contadores", icon: "hash" }, { value: "barras", label: "Barras horizontais", icon: "bar-chart-2" }, { value: "colunas", label: "Colunas", icon: "bar-chart-3" }, { value: "linhas", label: "Linhas", icon: "line-chart" }, { value: "pizza", label: "Pizza", icon: "pie-chart" }, { value: "rosca", label: "Rosca", icon: "circle-dot" }, ]; const STATUS_TONE = { encerrada: "var(--success-solid)", andamento: "var(--accent-flow)", pendente: "var(--st-pendente)", aguardando_atendimento: "var(--st-aguardando)", aguardando_terceiros: "var(--st-aguardando)", aguardando_aprovacao: "var(--violet-500)", aberta: "var(--st-aberta)", triagem: "var(--st-triagem)", reaberta: "var(--danger-solid)" }; const SHORT = { aberta: "Aberta", triagem: "Triagem", aguardando_atendimento: "Aguard. atend.", andamento: "Em andam.", pendente: "Pendente", aguardando_terceiros: "Aguard. terc.", aguardando_aprovacao: "Aguard. aprov.", encerrada: "Encerrada", reaberta: "Reaberta" }; const sectorTone = (id) => "var(--setor-" + (id === "gestao-condominial" ? "condominial" : id === "departamento-pessoal" ? "pessoal" : "contabilidade") + ")"; function Bars({ groups }) { const max = Math.max(...groups.map((g) => g.value), 1); return (
{groups.map((g) => (
{g.short}
{g.value}
))}
); } function Columns({ groups }) { const max = Math.max(...groups.map((g) => g.value), 1); return (
{groups.map((g) => (
{g.value}
{g.short}
))}
); } function Lines({ groups }) { const max = Math.max(...groups.map((g) => g.value), 1); const w = 100, h = 46; const pts = groups.map((g, i) => [groups.length === 1 ? w / 2 : (i / (groups.length - 1)) * w, h - (g.value / max) * (h - 6)]); const path = pts.map((p, i) => (i ? "L" : "M") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" "); return (
{pts.map((p, i) => )}
{groups.map((g) => ( {g.short} · {g.value} ))}
); } function Pie({ groups, total, donut }) { let acc = 0; const stops = groups.map((g) => { const from = acc; acc += (g.value / total) * 100; return g.tone + " " + from + "% " + acc + "%"; }).join(", "); return (
{donut ? (
{total} demandas
) : null}
{groups.map((g) => ( {g.label} {g.value} ))}
); } /** Lista compacta: título, empresa, responsável e status de cada demanda. */ function DemandList({ list, onOpenDemand }) { return (
{list.map((d) => (
onOpenDemand && onOpenDemand(d.id)} style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 0", borderBottom: "1px solid var(--border-subtle)", cursor: onOpenDemand ? "pointer" : "default" }}> {d.id} {d.title} {(d.context || "").replace(/^Empresa: /, "")}{d.people && d.people[0] ? " · " + d.people[0].name : ""}
))}
); } /** Tabela detalhada da demanda: número, título, empresa, responsável, prioridade, prazo e status. */ function DemandTable({ list, onOpenDemand }) { const NSt = window.ACRFlowDesignSystem_ba1e9c; return ( onOpenDemand(r.id) : undefined} columns={[ { key: "id", label: "Nº", width: 112, render: (r) => {r.id} }, { key: "title", label: "Demanda", render: (r) => ( {r.title} {r.category}{r.people && r.people[0] ? " · " + r.people[0].name : ""} ) }, { key: "context", label: "Empresa", width: 168, render: (r) => {(r.context || "—").replace(/^Empresa: /, "")} }, { key: "status", label: "Status", width: 148, render: (r) => }, { key: "daysLeft", label: "Prazo", width: 122, render: (r) => r.status === "encerrada" ? Concluída : }, ]} /> ); } function Counters({ list, onNavigate }) { const enc = list.filter((d) => d.status === "encerrada").length; const pend = list.length - enc; const atraso = list.filter((d) => d.status !== "encerrada" && d.daysLeft <= 0).length; const pct = list.length ? Math.round((enc / list.length) * 100) : 0; return (
{[["Pendentes", pend, "var(--st-pendente)"], ["Concluídas", enc, "var(--success-solid)"], ["Atrasadas", atraso, "var(--danger-solid)"]].map(([l, v, tone]) => (
{l} {v}
))}
{pct}% concluídas · {list.length} demandas na categoria
); } /** Quadro personalizado: setor + categoria, com contadores ou gráfico. */ function CustomBoard({ config, demands, statuses, sectors, onNavigate, onOpenDemand, onChart, onRemove }) { const list = demands.filter((d) => d.sector === config.sector && (config.category === "todas" || d.category === config.category)); const groups = statuses .map((s) => ({ label: s.name, short: SHORT[s.id] || s.name, value: list.filter((d) => d.status === s.id).length, tone: STATUS_TONE[s.id] || "var(--accent-primary)" })) .filter((g) => g.value > 0); const sectorName = (sectors.find((s) => s.id === config.sector) || {}).name; const chart = config.chart || "lista"; return ( c.value === chart) || CHARTS[0]).icon} size={15} />} />} items={CHARTS.map((c) => ({ label: c.label, icon: , onSelect: () => onChart(config.id, c.value) }))} /> } onClick={() => onRemove(config.id)} />
}> {list.length ? ( <> {chart === "lista" ? : null} {chart === "tabela" ? : null} {chart === "numeros" ? : null} {chart === "barras" ? : null} {chart === "colunas" ? : null} {chart === "linhas" ? : null} {chart === "pizza" ? : null} {chart === "rosca" ? : null} {chart !== "numeros" && chart !== "tabela" ? (
{list.filter((d) => d.status !== "encerrada").length} pendentes {list.filter((d) => d.status === "encerrada").length} concluídas
) : null} ) : ( } title="Sem demandas nesta seleção." description="Assim que houver demandas no setor/categoria escolhidos, o quadro é preenchido." action={} /> )} ); } /** Modal de criação de quadro personalizado. */ function NewBoardModal({ sectors, onClose, onCreate }) { const [sector, setSector] = React.useState(sectors[0].id); const [category, setCategory] = React.useState("todas"); const [chart, setChartType] = React.useState("lista"); const setChart = (v) => { setChartType(v); if (v === "tabela") setSpan(12); }; const [title, setTitle] = React.useState(""); const [span, setSpan] = React.useState(4); const cats = (sectors.find((s) => s.id === sector) || { categories: [] }).categories; const suggested = category === "todas" ? (sectors.find((s) => s.id === sector) || {}).name : category; return ( }>
setCategory(e.target.value)} options={[{ value: "todas", label: "Todas as categorias" }, ...cats.map((c) => ({ value: c, label: c }))]} />
setTitle(e.target.value)} hint="Deixe vazio para usar o nome da categoria." />
Visualização
{CHARTS.map((c) => ( ))}