const { Card, DataTable, Button, Icon, IconButton, Tabs, SearchField, Select, StatusPill, PriorityBadge, SectorTag, DeadlineIndicator, ProgressBar, AvatarGroup, Badge, EmptyState } = window.ACRFlowDesignSystem_ba1e9c;
const PRESETS = [
{ value: "todos", label: "Todo o histórico" },
{ value: "3", label: "Últimos 3 dias" },
{ value: "7", label: "Últimos 7 dias" },
{ value: "30", label: "Últimos 30 dias" },
{ value: "90", label: "Últimos 3 meses" },
{ value: "custom", label: "Escolher no calendário" },
];
const MONTHS = ["janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro"];
const WD = ["D", "S", "T", "Q", "Q", "S", "S"];
const TODAY = new Date(2026, 8, 1);
const parseBR = (s) => { const [d, m, y] = (s || "").split("/"); return y ? new Date(+y, +m - 1, +d) : null; };
const fmtBR = (d) => d ? String(d.getDate()).padStart(2, "0") + "/" + String(d.getMonth() + 1).padStart(2, "0") + "/" + d.getFullYear() : "";
const sameDay = (a, b) => a && b && a.toDateString() === b.toDateString();
/** Mini-calendário de seleção de intervalo: 1º clique define o início, 2º o fim. */
function RangeCalendar({ from, to, onPick }) {
const [view, setView] = React.useState(new Date((to || TODAY).getFullYear(), (to || TODAY).getMonth(), 1));
const first = new Date(view.getFullYear(), view.getMonth(), 1);
const days = new Date(view.getFullYear(), view.getMonth() + 1, 0).getDate();
const cells = [...Array(first.getDay()).fill(null), ...Array.from({ length: days }, (_, i) => new Date(view.getFullYear(), view.getMonth(), i + 1))];
const inRange = (d) => from && to && d > from && d < to;
const edge = (d) => sameDay(d, from) || sameDay(d, to);
return (
} onClick={() => setView(new Date(view.getFullYear(), view.getMonth() - 1, 1))} />
{MONTHS[view.getMonth()]} {view.getFullYear()}
} onClick={() => setView(new Date(view.getFullYear(), view.getMonth() + 1, 1))} />
{WD.map((w, i) => {w} )}
{cells.map((d, i) => d ? (
onPick(d)}
style={{ height: 30, border: "none", cursor: "pointer", borderRadius: edge(d) ? "var(--radius-sm)" : inRange(d) ? 0 : "var(--radius-sm)",
background: edge(d) ? "var(--accent-primary)" : inRange(d) ? "var(--accent-primary-soft)" : "transparent",
color: edge(d) ? "#fff" : inRange(d) ? "var(--text-brand)" : "var(--text-body)",
font: sameDay(d, TODAY) ? "var(--type-body-strong)" : "var(--type-body)",
outline: sameDay(d, TODAY) && !edge(d) ? "1px solid var(--border-focus)" : "none" }}>{d.getDate()}
) : )}
{from ? fmtBR(from) + (to ? " – " + fmtBR(to) : " – ...") : "Selecione a data inicial"}
onPick(null)}>Limpar
);
}
function DemandsListScreen({ onOpenDemand, onNavigate }) {
const D = window.ACRData;
const [q, setQ] = React.useState("");
const [sector, setSector] = React.useState("todos");
const [status, setStatus] = React.useState("todos");
const [priority, setPriority] = React.useState("todas");
const [preset, setPreset] = React.useState("todos");
const [from, setFrom] = React.useState(null);
const [to, setTo] = React.useState(null);
const [calOpen, setCalOpen] = React.useState(false);
const [advanced, setAdvanced] = React.useState(false);
const all = D.demands;
const sectors = D.sectorsCfg;
const pick = (d) => {
if (!d) { setFrom(null); setTo(null); return; }
if (!from || (from && to)) { setFrom(d); setTo(null); return; }
if (d < from) { setTo(from); setFrom(d); } else setTo(d);
};
const inPeriod = (dem) => {
const ref = parseBR(dem.opened) || parseBR(dem.due);
if (!ref) return true;
if (preset === "custom") {
if (from && ref < from) return false;
if (to && ref > to) return false;
return true;
}
if (preset === "todos") return true;
const limit = new Date(TODAY); limit.setDate(limit.getDate() - Number(preset));
return ref >= limit;
};
const rows = all.filter((d) =>
(sector === "todos" || d.sector === sector) &&
(status === "todos" || d.status === status) &&
(priority === "todas" || d.priority === priority) &&
inPeriod(d) &&
(d.title + " " + d.id + " " + (d.context || "") + " " + (d.category || "") + " " + (d.people || []).map((p) => p.name).join(" ") + " " + (d.tags || []).join(" ")).toLowerCase().includes(q.toLowerCase()));
const periodLabel = preset === "custom"
? (from ? fmtBR(from) + (to ? " – " + fmtBR(to) : " – ...") : "Escolher no calendário")
: (PRESETS.find((p) => p.value === preset) || {}).label;
const active = [
sector !== "todos" && { k: "sector", label: (sectors.find((s) => s.id === sector) || {}).name, clear: () => setSector("todos") },
status !== "todos" && { k: "status", label: (D.statuses.find((s) => s.id === status) || {}).name, clear: () => setStatus("todos") },
priority !== "todas" && { k: "prio", label: (D.priorities.find((p) => p.id === priority) || {}).name, clear: () => setPriority("todas") },
preset !== "todos" && { k: "per", label: periodLabel, clear: () => { setPreset("todos"); setFrom(null); setTo(null); } },
q && { k: "q", label: '"' + q + '"', clear: () => setQ("") },
].filter(Boolean);
const clearAll = () => { setQ(""); setSector("todos"); setStatus("todos"); setPriority("todas"); setPreset("todos"); setFrom(null); setTo(null); };
return (
Todas as Demandas
Busque em todo o histórico — demandas encerradas há meses ou anos mantêm timeline, evidências e ciclo completo.
}>Exportar
} onClick={() => onNavigate("nova")}>Nova demanda
setQ(e.target.value)} onClear={() => setQ("")} width="100%" shortcut={null}
placeholder="Nº da demanda, título, empresa, responsável ou tag..." />
setSector(e.target.value)}
options={[{ value: "todos", label: "Todos os setores" }, ...sectors.map((s) => ({ value: s.id, label: s.name }))]} />
setStatus(e.target.value)}
options={[{ value: "todos", label: "Todos os status" }, ...D.statuses.map((s) => ({ value: s.id, label: s.name }))]} />
Período de abertura
{ setPreset(e.target.value); setCalOpen(e.target.value === "custom"); }} options={PRESETS} />
}
onClick={() => { setPreset("custom"); setCalOpen((v) => !v); }} />
{preset === "custom" ?
{from ? fmtBR(from) + (to ? " – " + fmtBR(to) : " – selecione o fim") : "Clique no calendário para definir o intervalo"} : null}
{advanced ? (
setPriority(e.target.value)}
options={[{ value: "todas", label: "Todas as prioridades" }, ...D.priorities.map((p) => ({ value: p.id, label: p.name }))]} />
) : null}
{advanced ? ({ value: t.id, label: t.name }))]} /> : null}
{advanced ? ({ value: c.id, label: c.name }))]} /> : null}
{calOpen ? (
{from ? fmtBR(from) + (to ? " – " + fmtBR(to) : " – selecione a data final") : "Selecione a data inicial"}
O primeiro clique define o início do período e o segundo o fim. Sem data final, a busca vai da data escolhida até hoje.
setCalOpen(false)}>Fechar calendário
) : null}
{active.length ? active.map((f) => (
{f.label}
)) : Nenhum filtro aplicado — mostrando todo o histórico. }
{active.length ? Limpar tudo : null}
} onClick={() => setAdvanced((v) => !v)}>
{advanced ? "Menos filtros" : "Mais filtros"}
d.status === "aguardando_atendimento").length },
{ id: "andamento", label: "Em andamento", count: all.filter((d) => d.status === "andamento").length },
{ id: "pendente", label: "Pendentes", count: all.filter((d) => d.status === "pendente").length },
{ id: "aguardando_aprovacao", label: "Aguardando aprovação", count: all.filter((d) => d.status === "aguardando_aprovacao").length },
{ id: "encerrada", label: "Encerradas", count: all.filter((d) => d.status === "encerrada").length },
]} />
{rows.length ? (
{r.id} },
{ key: "title", label: "Demanda", render: (r) => (
{r.title}
{r.context}{r.category ? " · " + r.category : ""}
) },
{ key: "sector", label: "Setor", width: 190, render: (r) => },
{ key: "status", label: "Status", width: 190, render: (r) => },
{ key: "priority", label: "Prioridade", width: 108, render: (r) => },
{ key: "people", label: "Responsáveis", width: 118, render: (r) => },
{ key: "daysLeft", label: "Prazo", width: 170, render: (r) => r.status === "encerrada" ? Entregue em {r.due} : },
{ key: "progress", label: "Conclusão", width: 128, render: (r) => },
]}
rows={rows} onRowClick={(r) => onOpenDemand(r.id)} dense />
) : (
} title="Nenhuma demanda encontrada." description="Amplie o período ou remova filtros — o histórico completo continua disponível." action={Limpar filtros } />
)}
);
}
Object.assign(window, { DemandsListScreen });