/* Demandas de contrato · a tela do módulo.

   O trabalho que a BNI pede a um fornecedor dentro de um contrato, do pedido à
   aceitação. Especificação em docs/demandas/demandas.md; as regras de transição
   são validadas no servidor (src/demandas.js), e esta tela apenas esconde o que
   não cabe no momento.

   Três telas:
     #/demandas                  · página principal, todas as demandas
     #/demandas/contrato/<id>    · ficha do contrato, só as dele
     #/demandas/demanda/<id>     · a demanda

   Os dois lados usam as mesmas telas, com a perspectiva dada pelo papel de quem
   entrou: a administração da BNI cria, aprova e analisa; o fornecedor estima,
   executa e envia. Os rótulos de status mudam conforme quem olha (PD_STATUS).

   Usa o escopo global compartilhado com app.jsx (componentes do design system,
   Icone, dataBr, chamarApi, hooks do React). Nada é redeclarado aqui.

   O prefixo Pd/pd- dos nomes internos e das classes vem da origem do arquivo,
   que nasceu como protótipo navegável. É só um espaço de nomes local. */

/* Cada status tem:
     extenso     · nome neutro, usado nas colunas do Kanban
     admin       · como a administração lê
     fornecedor  · como o fornecedor lê
     ator        · de quem é a ação ('admin', 'fornecedor', 'ambos' ou null)
     variant     · peso visual da etiqueta

   A regra do texto é a mesma nas duas perspectivas: quando a ação é de quem
   está olhando, o rótulo diz "sua"; quando é da outra parte, nomeia a outra
   parte. A cor segue a mesma lógica: vermelho quando a ação é de quem olha,
   cinza quando está com o outro lado ou a demanda encerrou. Assim cor e texto
   contam a mesma história em qualquer perspectiva. O variant é escolhido para
   que os seis status fiquem distinguíveis dentro de cada perspectiva. */
const PD_STATUS = {
  'Aguardando estimativa': {
    extenso: 'Aguardando estimativa do fornecedor',
    admin: 'Aguardando estimativa do fornecedor',
    fornecedor: 'Aguardando sua estimativa',
    ator: 'fornecedor', variant: 'solid',
  },
  'Aguardando confirmação': {
    extenso: 'Aguardando confirmação do administrador',
    admin: 'Aguardando sua confirmação',
    fornecedor: 'Aguardando confirmação do administrador',
    ator: 'admin', variant: 'solid',
  },
  'Em negociação': {
    extenso: 'Em negociação',
    admin: 'Em negociação',
    fornecedor: 'Em negociação',
    ator: 'ambos', variant: 'soft',
  },
  // Rótulo único nas três formas: a etapa não espera ninguém, o trabalho está
  // sendo feito, e a própria etapa já diz que é o fornecedor quem executa.
  'Em andamento': {
    extenso: 'Em andamento',
    admin: 'Em andamento',
    fornecedor: 'Em andamento',
    ator: 'fornecedor', variant: 'outline',
  },
  'Em análise': {
    extenso: 'Em análise pelo administrador',
    admin: 'Aguardando sua análise',
    fornecedor: 'Em análise pelo administrador',
    ator: 'admin', variant: 'outline',
  },
  'Finalizada': {
    extenso: 'Finalizada',
    admin: 'Finalizada',
    fornecedor: 'Finalizada',
    ator: null, variant: 'soft',
  },
};

/* Vermelho quando a ação é de quem está olhando. */
function pdEhMinhaVez(status, perspectiva) {
  const ator = PD_STATUS[status]?.ator;
  return ator === 'ambos' || (!!ator && ator === perspectiva);
}

const PD_ORDEM_STATUS = Object.keys(PD_STATUS);

/* Os dados vêm do sistema: fornecedores, contratos e demandas são lidos da API.
   O contexto carrega as coleções de apoio uma vez e as entrega a quem precisa
   resolver um vínculo, em vez de cada tela buscar por conta própria. */
const ContextoDados = createContext({ fornecedores: [], contratos: [] });
const usarDados = () => useContext(ContextoDados);

const acharFornecedor = (dados, id) => dados.fornecedores.find((f) => String(f.id) === String(id));
const acharContrato = (dados, id) => dados.contratos.find((c) => String(c.id) === String(id));
const fornecedorDoContrato = (dados, contratoId) => {
  const contrato = acharContrato(dados, contratoId);
  return contrato ? acharFornecedor(dados, contrato.fornecedorId) : undefined;
};
const contratosDe = (dados, fornecedorId) => dados.contratos.filter((c) => String(c.fornecedorId) === String(fornecedorId));

/* Nome legível de um registro que pode não estar ao alcance de quem olha: um
   contrato excluído, ou fora do recorte. Melhor dizer isso do que mostrar um id
   cru ou um espaço em branco. */
const nomeDoContrato = (dados, id) => (acharContrato(dados, id) || {}).objeto || 'Contrato não encontrado';
const nomeDoFornecedor = (dados, contratoId) => (fornecedorDoContrato(dados, contratoId) || {}).nome || 'Fornecedor não encontrado';

/* De onde vem a demanda, como se escreve para quem está olhando. Na visão do
   fornecedor o nome dele é a mesma resposta em toda linha, então sobra só o
   contrato, que é o que distingue uma demanda da outra. */
function pdOrigem(dados, contratoId, perspectiva) {
  if (perspectiva === 'fornecedor') return nomeDoContrato(dados, contratoId);
  return `${nomeDoFornecedor(dados, contratoId)} · ${nomeDoContrato(dados, contratoId)}`;
}

/* Tradução dos registros da API para o formato que as telas usam. Conversa,
   anexos e trilha ainda não existem no sistema: ficam vazios, e as telas dizem
   isso em vez de inventar. */
const paraFornecedor = (r) => ({ id: String(r.id), nome: r.dados.fornecedor || `Registro ${r.id}` });
const paraContrato = (r) => ({
  id: String(r.id),
  fornecedorId: String(r.dados.fornecedor || ''),
  objeto: r.dados.objeto || `Contrato ${r.id}`,
  dataContrato: r.dados.dataContrato || '',
  valor: r.dados.valor || '',
  responsavel: r.dados.responsavel || '',
  resumo: r.dados.resumo || '',
});
const paraDemanda = (r, anexos = []) => ({
  id: String(r.id),
  contratoId: String(r.dados.contrato || ''),
  nome: r.dados.nome || `Demanda ${r.id}`,
  descricao: r.dados.descricao || '',
  status: r.dados.status || 'Aguardando estimativa',
  // O campo guarda minutos; vazio vira nulo, para a tela mostrar "·" e não "0h".
  minutos: r.dados.minutos === '' || r.dados.minutos == null ? null : Number(r.dados.minutos),
  prazo: r.dados.prazo || '',
  criadaEm: (r.criadoEm || '').slice(0, 10),
  anexos,
});

/* O anexo do sistema para a linha que a tela desenha. O tamanho vem em bytes e
   a data no carimbo de criação; o protótipo mostra os dois já formatados. */
const paraAnexo = (a) => ({
  nome: a.nome,
  categoria: a.categoria,
  tamanho: tamanhoLegivel(a.tamanho),
  em: (a.criadoEm || '').slice(0, 10),
});

const PD_CATEGORIAS = ['Evidência de trabalho', 'Comprovação final'];
const PD_LINHAS_MAX = 10;

/* Duração em minutos para leitura: 480 -> "8h", 7230 -> "120h30". */
function pdHoras(minutos) {
  if (minutos == null || minutos === '') return '';
  const h = Math.floor(minutos / 60);
  const m = minutos % 60;
  return m === 0 ? `${h}h` : `${h}h${String(m).padStart(2, '0')}`;
}

/* Máscara de duração HH:MM, sem máximo de horas: os dois últimos dígitos são os
   minutos e todo o resto são horas ("12030" -> "120:30"). */
function pdMascaraHoras(texto) {
  const digitos = String(texto).replace(/\D/g, '').slice(0, 6);
  if (digitos.length <= 2) return digitos;
  return `${digitos.slice(0, -2)}:${digitos.slice(-2)}`;
}

function pdMinutosDeHHMM(texto) {
  const partes = String(texto).split(':');
  if (partes.length !== 2) return null;
  const h = Number(partes[0]);
  const m = Number(partes[1]);
  if (!Number.isInteger(h) || !Number.isInteger(m) || m > 59) return null;
  return h * 60 + m;
}

/* "2026-08-24 14:03" -> "24/08/2026 14:03". */
function pdDataHora(carimbo) {
  const [dia, hora] = String(carimbo || '').split(' ');
  return dia ? `${dataBr(dia)}${hora ? ` ${hora}` : ''}` : '';
}


/* Etiqueta de status. O rótulo depende de quem olha; a cor, só do status. */
function PdEtiqueta({ status, perspectiva }) {
  const def = PD_STATUS[status] || PD_STATUS['Aguardando estimativa'];
  const tone = pdEhMinhaVez(status, perspectiva) ? 'red' : 'grey';
  return <Badge tone={tone} variant={def.variant}>{def[perspectiva] || def.extenso}</Badge>;
}

/* Altura mínima vem do CSS (.pd-textarea); o efeito define a altura explícita,
   então um atributo rows seria ignorado. */
function PdTextarea({ label, value, onChange, placeholder }) {
  const ref = useRef(null);
  // Cresce com o conteúdo até PD_LINHAS_MAX linhas; passando disso, rola.
  useEffect(() => {
    const campo = ref.current;
    if (!campo) return;
    const estilo = window.getComputedStyle(campo);
    const alturaLinha = parseFloat(estilo.lineHeight) || parseFloat(estilo.fontSize) * 1.5;
    const molduras = parseFloat(estilo.paddingTop) + parseFloat(estilo.paddingBottom)
      + parseFloat(estilo.borderTopWidth) + parseFloat(estilo.borderBottomWidth);
    const teto = alturaLinha * PD_LINHAS_MAX + molduras;
    campo.style.height = 'auto';
    const desejada = campo.scrollHeight;
    campo.style.height = `${Math.min(desejada, teto)}px`;
    campo.style.overflowY = desejada > teto ? 'auto' : 'hidden';
  }, [value]);
  return (
    <label className="campo-cheio">
      <span className="campo-rotulo">{label}</span>
      <textarea ref={ref} className="campo-textarea pd-textarea"
        placeholder={placeholder} value={value} onChange={onChange} />
    </label>
  );
}

/* Filtro em cascata: primeiro o fornecedor, e só então um contrato dele. */
function PdFiltros({ fornecedorId, contratoId, aoMudar, travadoEm }) {
  const dados = usarDados();
  const contratos = fornecedorId ? contratosDe(dados, fornecedorId) : [];
  return (
    <div className="pd-filtros">
      {travadoEm ? (
        // O fornecedor logado só vê as próprias demandas: em vez de um select
        // desabilitado com o nome dele, o recorte é afirmado em texto. A célula
        // repete a estrutura do Select (rótulo em cima, valor embaixo) para o
        // texto alinhar com o campo ao lado sem ajuste manual.
        <div className="pd-filtros-fixo">
          <span className="pd-filtros-fixo-rotulo">Exibindo</span>
          <span className="pd-filtros-fixo-valor">Suas demandas</span>
        </div>
      ) : (
        <Select label="Fornecedor" value={fornecedorId}
          onChange={(e) => aoMudar({ fornecedorId: e.target.value, contratoId: '' })}
          options={[{ value: '', label: 'Todos os fornecedores' }]
            .concat(dados.fornecedores.map((f) => ({ value: f.id, label: f.nome })))} />
      )}
      <Select label="Contrato" value={contratoId}
        disabled={!fornecedorId}
        helper={!fornecedorId ? 'Escolha um fornecedor primeiro' : undefined}
        onChange={(e) => aoMudar({ fornecedorId, contratoId: e.target.value })}
        options={[{ value: '', label: fornecedorId ? 'Todos os contratos do fornecedor' : '' }]
          .concat(contratos.map((c) => ({ value: c.id, label: c.objeto })))} />
      {(fornecedorId || contratoId) && !travadoEm && (
        <span className="pd-filtros-acao">
          <Button variant="ghost" size="sm" onClick={() => aoMudar({ fornecedorId: '', contratoId: '' })}>
            Limpar filtros
          </Button>
        </span>
      )}
    </div>
  );
}

/* Lista de demandas: linha inteira clicável, sem coluna de ações. As colunas de
   horas, prazo e etiqueta têm largura fixa para ficarem alinhadas entre linhas. */
function PdLista({ demandas, perspectiva, mostrarOrigem, aoAbrir }) {
  const dados = usarDados();
  if (demandas.length === 0) {
    return (
      <p className="toolbar-contagem" style={{ margin: 0 }}>
        Nenhuma demanda para o filtro escolhido.
      </p>
    );
  }
  return (
    <div className="lista-vinculos">
      {demandas.map((d) => (
        <a key={d.id} className="linha-vinculo pd-linha-demanda"
          href={`#/demandas/demanda/${d.id}`}
          onClick={(e) => { e.preventDefault(); aoAbrir(d.id); }}>
          <Icone nome="prancheta" tamanho={15} />
          <span className="pd-demanda-nome">
            <span className="link-detalhe">{d.nome}</span>
            {mostrarOrigem && (
              <span className="linha-secundaria">{pdOrigem(dados, d.contratoId, perspectiva)}</span>
            )}
          </span>
          <span className="pd-demanda-horas">{pdHoras(d.minutos) || '·'}</span>
          <span className="pd-demanda-prazo">{dataBr(d.prazo) || '·'}</span>
          <span className="pd-demanda-etiqueta">
            <PdEtiqueta status={d.status} perspectiva={perspectiva} />
          </span>
        </a>
      ))}
    </div>
  );
}

/* Kanban: uma coluna por etapa, sempre com o nome extenso. A demanda anda de
   coluna conforme o status muda. */
function PdKanban({ demandas, perspectiva, aoAbrir }) {
  const dados = usarDados();
  return (
    <div className="pd-kanban">
      {PD_ORDEM_STATUS.map((status) => {
        const daColuna = demandas.filter((d) => d.status === status);
        const def = PD_STATUS[status];
        return (
          // O topo da coluna usa a mesma cor dos cartões dela: vermelho quando
          // a vez daquela etapa é de quem está olhando.
          <div key={status} className="pd-coluna">
            <div className={'pd-coluna-topo' + (pdEhMinhaVez(status, perspectiva) ? ' pd-coluna-minha-vez' : '')}>
              <span className="pd-coluna-titulo">{def.extenso}</span>
              <span className="pd-coluna-contagem">{daColuna.length}</span>
            </div>
            <div className="pd-coluna-corpo">
              {daColuna.length === 0 && <p className="pd-coluna-vazia">Nenhuma demanda</p>}
              {daColuna.map((d) => (
                // A coluna já nomeia a etapa, então o cartão não repete o
                // status como etiqueta: a cor da borda diz de quem é a vez.
                <a key={d.id}
                  className={'pd-cartao' + (pdEhMinhaVez(d.status, perspectiva) ? ' pd-cartao-minha-vez' : '')}
                  href={`#/demandas/demanda/${d.id}`}
                  onClick={(e) => { e.preventDefault(); aoAbrir(d.id); }}>
                  <span className="pd-cartao-nome">{d.nome}</span>
                  <span className="pd-cartao-origem">{pdOrigem(dados, d.contratoId, perspectiva)}</span>
                  <span className="pd-cartao-dados">
                    {pdHoras(d.minutos) || 'sem estimativa'}
                    {d.prazo ? ` · ${dataBr(d.prazo)}` : ''}
                  </span>
                </a>
              ))}
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* Tela 1: página principal com todas as demandas, alternando lista e Kanban. */
function PdPaginaPrincipal({ demandas, perspectiva, fornecedorLogado, podeCriar,
  exibicao, aoTrocarExibicao, filtro, aoFiltrar, aoAbrir, aoCriar }) {
  const dados = usarDados();
  // Na visão do fornecedor o recorte é dele e o filtro de fornecedor fica travado.
  const travadoEm = perspectiva === 'fornecedor' ? fornecedorLogado : null;
  const efetivo = travadoEm ? { fornecedorId: travadoEm, contratoId: filtro.contratoId } : filtro;

  /* Universo do usuário, antes de qualquer filtro: a administração vê todas as
     demandas do sistema, o fornecedor só as dos contratos dele. O filtro atua
     dentro desse universo e o contador compara com ele, e não com o total do
     sistema, que para o fornecedor incluiria demandas que ele nem pode abrir. */
  // Um contrato pode não estar na coleção carregada (excluído, ou fora do
  // recorte de quem olha). Sem o vínculo não dá para afirmar de quem é a
  // demanda, então ela fica de fora do recorte em vez de derrubar a tela.
  const doFornecedor = (d, fornecedorId) => {
    const contrato = acharContrato(dados, d.contratoId);
    return !!contrato && String(contrato.fornecedorId) === String(fornecedorId);
  };

  const acessiveis = travadoEm ? demandas.filter((d) => doFornecedor(d, travadoEm)) : demandas;

  const visiveis = acessiveis.filter((d) => {
    if (efetivo.contratoId) return d.contratoId === efetivo.contratoId;
    if (efetivo.fornecedorId) return doFornecedor(d, efetivo.fornecedorId);
    return true;
  });

  return (
    <div>
      <SectionHeading className="titulo-secao" overline="Gestão de contratos" title="Demandas" />
      <div className="pd-barra">
        <Tabs value={exibicao} onChange={aoTrocarExibicao}
          tabs={[{ value: 'lista', label: 'Lista' }, { value: 'kanban', label: 'Kanban' }]} />
        <span className="toolbar-contagem pd-barra-contagem">
          {visiveis.length} de {acessiveis.length} demanda{acessiveis.length === 1 ? '' : 's'}
        </span>
        {podeCriar && (
          <Button variant="secondary" size="sm" leadingIcon={<Icone nome="mais" />} onClick={aoCriar}>
            Nova demanda
          </Button>
        )}
      </div>

      <div className="detalhe-secao">
        <Card>
          <PdFiltros fornecedorId={efetivo.fornecedorId} contratoId={efetivo.contratoId}
            aoMudar={aoFiltrar} travadoEm={travadoEm} />
        </Card>
      </div>

      <div className="detalhe-secao">
        {exibicao === 'lista' ? (
          <Card>
            <PdLista demandas={visiveis} perspectiva={perspectiva} mostrarOrigem aoAbrir={aoAbrir} />
          </Card>
        ) : (
          <PdKanban demandas={visiveis} perspectiva={perspectiva} aoAbrir={aoAbrir} />
        )}
      </div>
    </div>
  );
}

/* Tela 2: a ficha do contrato, com o card de Demandas só daquele contrato. Aqui
   existe apenas a lista, e a criação já nasce presa a este contrato. */
function PdTelaContrato({ contrato, demandas, perspectiva, podeCriar, aoAbrir, aoCriar }) {
  const dados = usarDados();
  const fornecedor = acharFornecedor(dados, contrato.fornecedorId);
  const doContrato = demandas.filter((d) => d.contratoId === contrato.id);

  /* Os documentos do contrato são os anexos de verdade do registro. Ficam numa
     carga própria porque são do contrato aberto, e não da coleção inteira. */
  const [anexos, setAnexos] = useState(null);
  useEffect(() => {
    let vivo = true;
    chamarApi(`/api/contratos/${contrato.id}/anexos`)
      .then((corpo) => { if (vivo) setAnexos(corpo.anexos); })
      .catch(() => { if (vivo) setAnexos([]); });
    return () => { vivo = false; };
  }, [contrato.id]);
  return (
    <div>
      <a className="voltar" href="#/contratos"><Icone nome="voltar" tamanho={14} /> Contratos</a>
      <div className="pd-titulo">
        <SectionHeading className="titulo-secao" overline="Gestão de contratos" title={contrato.objeto} />
      </div>
      <div className="detalhe-secao">
        <Card accent>
          <div className="ficha-grade">
            <div className="ficha-item">
              <div className="ficha-rotulo">Objeto</div>
              <div className="ficha-valor">{contrato.objeto}</div>
            </div>
            <div className="ficha-item">
              <div className="ficha-rotulo">Data do contrato</div>
              <div className="ficha-valor">{dataBr(contrato.dataContrato)}</div>
            </div>
            <div className="ficha-item">
              <div className="ficha-rotulo">Valor (R$)</div>
              <div className="ficha-valor">{contrato.valor}</div>
            </div>
            <div className="ficha-item">
              <div className="ficha-rotulo">Responsável</div>
              <div className="ficha-valor">{contrato.responsavel}</div>
            </div>
            <div className="ficha-item">
              <div className="ficha-rotulo">Fornecedor</div>
              <div className="ficha-valor">{fornecedor ? fornecedor.nome : 'Fornecedor não encontrado'}</div>
            </div>
          </div>
          <div className="ficha-item-longa">
            <div className="ficha-rotulo">Resumo</div>
            <div className="detalhe-texto pd-texto-longo">{contrato.resumo}</div>
          </div>
        </Card>
      </div>

      <div className="detalhe-secao">
        <div className="grupo-menu-rotulo">Demandas</div>
        <Card>
          {doContrato.length === 0 ? (
            <p className="toolbar-contagem" style={{ margin: 0 }}>
              Nenhuma demanda enviada ao fornecedor ainda.
            </p>
          ) : (
            <PdLista demandas={doContrato} perspectiva={perspectiva} aoAbrir={aoAbrir} />
          )}
          {podeCriar && (
            <div className="vinculos-acao">
              <Button variant="secondary" size="sm" leadingIcon={<Icone nome="mais" />} onClick={aoCriar}>
                Nova demanda
              </Button>
            </div>
          )}
        </Card>
      </div>

      <div className="detalhe-secao">
        <div className="grupo-menu-rotulo">Contratos, propostas e documentos</div>
        <Card>
          {anexos === null && <p className="toolbar-contagem" style={{ margin: 0 }}>Carregando…</p>}
          {anexos !== null && anexos.length === 0 && (
            <p className="toolbar-contagem" style={{ margin: 0 }}>
              Nenhum documento anexado a este contrato.
            </p>
          )}
          {anexos !== null && anexos.length > 0 && (
            <div className="lista-vinculos">
              {anexos.map((a) => (
                <div key={a.id} className="linha-vinculo pd-linha-anexo">
                  <Icone nome="clipe" tamanho={15} />
                  <span className="pd-anexo-nome">{a.nome}</span>
                  <Badge tone="grey" variant="soft">{a.categoria}</Badge>
                  <span className="pd-anexo-meta">
                    {tamanhoLegivel(a.tamanho)} · {dataBr(a.criadoEm)}
                  </span>
                </div>
              ))}
            </div>
          )}
        </Card>
      </div>
    </div>
  );
}

/* Thread de conversa: colapsada por padrão, abre com o clique. */
function PdConversa({ mensagens, naoLidas, podeEscrever, aoEnviar, aoAbrir }) {
  const [aberta, setAberta] = useState(false);
  const [texto, setTexto] = useState('');

  function alternar() {
    setAberta((a) => {
      if (!a) aoAbrir();
      return !a;
    });
  }

  return (
    <div className="detalhe-secao">
      <div className="grupo-menu-rotulo">Conversa da demanda</div>
      <Card>
        <button type="button" className="pd-conversa-cabecalho" onClick={alternar} aria-expanded={aberta}>
          <Icone nome={aberta ? 'menos' : 'mais'} tamanho={15} />
          <span className="pd-conversa-titulo">
            {mensagens.length === 0 ? 'Nenhuma mensagem' : `${mensagens.length} mensagem${mensagens.length > 1 ? 's' : ''}`}
          </span>
          {naoLidas > 0 && (
            <span className="pd-conversa-novas">
              <Badge tone="red" variant="solid">{naoLidas} nova{naoLidas > 1 ? 's' : ''}</Badge>
            </span>
          )}
          <span className="pd-conversa-dica">{aberta ? 'Clique para recolher' : 'Clique para abrir'}</span>
        </button>

        {aberta && (
          <div className="pd-conversa-corpo">
            {mensagens.length === 0 && (
              <p className="toolbar-contagem" style={{ margin: 0 }}>
                A conversa se abre quando a administração contesta uma estimativa ou devolve
                uma entrega, e fica registrada como prova do acordo.
              </p>
            )}
            {mensagens.map((m, i) => (
              <div key={i} className={'pd-mensagem' + (m.papel === 'Administração' ? ' pd-mensagem-bni' : '')}>
                <div className="pd-mensagem-topo">
                  <span className="pd-mensagem-autor">{m.autor}</span>
                  <span className="pd-mensagem-papel">{m.papel}</span>
                  <span className="pd-mensagem-data">{pdDataHora(m.em)}</span>
                </div>
                <div className="pd-mensagem-texto">{m.texto}</div>
              </div>
            ))}

            {podeEscrever && (
              <div className="pd-conversa-escrever">
                <PdTextarea label="Escrever na conversa"
                  placeholder="Mensagem para a outra parte"
                  value={texto} onChange={(e) => setTexto(e.target.value)} />
                <Button variant="secondary" size="sm" disabled={!texto.trim()}
                  onClick={() => { aoEnviar(texto.trim()); setTexto(''); }}>
                  Enviar mensagem
                </Button>
              </div>
            )}
          </div>
        )}
      </Card>
    </div>
  );
}

function PdTrilha({ trilha }) {
  return (
    <div className="detalhe-secao">
      <div className="grupo-menu-rotulo">Trilha de auditoria</div>
      <Card>
        {/* Demanda recém-criada ainda não teve transição: a trilha começa no
            primeiro passo do fluxo. Dizer isso vale mais do que um card vazio. */}
        {trilha.length === 0 && (
          <p className="toolbar-contagem" style={{ margin: 0 }}>
            Nada registrado ainda. Cada mudança de status entra aqui, com quem fez e quando.
          </p>
        )}
        <div className="pd-trilha">
          {trilha.map((e, i) => (
            <div key={i} className="pd-trilha-item">
              <span className="pd-trilha-data">{pdDataHora(e.em)}</span>
              <span className="pd-trilha-quem">{e.autor} <span className="pd-trilha-papel">{e.papel}</span></span>
              <span className="pd-trilha-mudanca">
                {e.de ? <>{e.de} <Icone nome="setaDireita" tamanho={13} /> {e.para}</> : <>Demanda criada em {e.para}</>}
              </span>
            </div>
          ))}
        </div>
      </Card>
    </div>
  );
}

/* Os status em que anexo pode existir. Antes da execução não há o que anexar,
   então a seção some em vez de ficar vazia ocupando espaço: ela pertence a uma
   etapa que ainda não chegou. A condição inclui "tem anexo" por segurança, para
   nunca esconder arquivo que exista. */
const PD_STATUS_COM_ANEXO = ['Em andamento', 'Em análise', 'Finalizada'];

function PdAnexos({ anexos, status, perspectiva }) {
  if (!PD_STATUS_COM_ANEXO.includes(status) && anexos.length === 0) return null;
  const temFinal = anexos.some((a) => a.categoria === 'Comprovação final');
  return (
    <div className="detalhe-secao">
      <div className="grupo-menu-rotulo">Comprovações e evidências</div>
      <Card>
        {anexos.length === 0 ? (
          <p className="toolbar-contagem" style={{ margin: 0 }}>
            {perspectiva === 'fornecedor'
              ? 'Nenhum anexo ainda. Anexe as evidências durante a execução; ao menos uma de "Comprovação final" é obrigatória para enviar à análise.'
              : 'Nenhum anexo ainda. O fornecedor anexa durante a execução, e ao menos um de "Comprovação final" é obrigatório para enviar a demanda à análise.'}
          </p>
        ) : (
          <div className="lista-vinculos">
            {anexos.map((a, i) => (
              <div key={i} className="linha-vinculo pd-linha-anexo">
                <Icone nome="clipe" tamanho={15} />
                <span className="pd-anexo-nome">{a.nome}</span>
                <Badge tone={a.categoria === 'Comprovação final' ? 'red' : 'grey'} variant="soft">
                  {a.categoria}
                </Badge>
                <span className="pd-anexo-meta">{a.tamanho} · {dataBr(a.em)}</span>
              </div>
            ))}
          </div>
        )}
        {status === 'Em análise' && !temFinal && (
          <div className="pd-nota">Sem anexo de "Comprovação final": o envio não deveria ter sido permitido.</div>
        )}
      </Card>
    </div>
  );
}

function PdDialogoMensagem({ titulo, rotulo, textoBotao, aoFechar, aoConfirmar }) {
  const [texto, setTexto] = useState('');
  return (
    <Dialog open title={titulo} accent onClose={aoFechar}
      footer={<>
        <Button variant="ghost" onClick={aoFechar}>Cancelar</Button>
        <Button variant="primary" disabled={!texto.trim()} onClick={() => aoConfirmar(texto.trim())}>
          {textoBotao}
        </Button>
      </>}>
      <PdTextarea label={`${rotulo} *`}
        placeholder="O fornecedor vai ler esta mensagem na conversa da demanda"
        value={texto} onChange={(e) => setTexto(e.target.value)} />
    </Dialog>
  );
}

/* Criação. Com contratoFixo, a demanda nasce presa àquele contrato e não há como
   trocar; sem ele, o usuário escolhe fornecedor e então um contrato dele. */
function PdDialogoNovaDemanda({ contratoFixo, aoFechar, aoCriar }) {
  const dados = usarDados();
  const [fornecedorId, setFornecedorId] = useState('');
  const [contratoId, setContratoId] = useState('');
  const [nome, setNome] = useState('');
  const [descricao, setDescricao] = useState('');

  const alvo = contratoFixo || contratoId;
  const valido = !!alvo && nome.trim() && descricao.trim();
  const contratos = fornecedorId ? contratosDe(dados, fornecedorId) : [];

  return (
    <Dialog open title="Nova demanda" accent onClose={aoFechar}
      footer={<>
        <Button variant="ghost" onClick={aoFechar}>Cancelar</Button>
        <Button variant="primary" disabled={!valido}
          onClick={() => aoCriar(alvo, nome.trim(), descricao.trim())}>
          Criar demanda
        </Button>
      </>}>
      {contratoFixo ? (
        <div className="pd-contrato-fixo">
          <div className="ficha-rotulo">Contrato</div>
          <div className="ficha-valor">{nomeDoContrato(dados, contratoFixo)}</div>
          <div className="pd-nota" style={{ marginTop: 0 }}>
            Aberta a partir da ficha deste contrato: a demanda nasce vinculada a ele,
            e o vínculo não pode ser trocado aqui.
          </div>
        </div>
      ) : (
        <div className="form-grade pd-grade-espacada">
          <Select label="Fornecedor *" value={fornecedorId}
            onChange={(e) => { setFornecedorId(e.target.value); setContratoId(''); }}
            options={[{ value: '', label: '' }]
              .concat(dados.fornecedores.map((f) => ({ value: f.id, label: f.nome })))} />
          <Select label="Contrato *" value={contratoId} disabled={!fornecedorId}
            helper={!fornecedorId ? 'Escolha um fornecedor primeiro' : undefined}
            onChange={(e) => setContratoId(e.target.value)}
            options={[{ value: '', label: '' }]
              .concat(contratos.map((c) => ({ value: c.id, label: c.objeto })))} />
        </div>
      )}
      <div className="form-grade">
        <Input label="Nome da demanda *" value={nome} onChange={(e) => setNome(e.target.value)} />
        <PdTextarea label="Descrição da tarefa *"
          value={descricao} onChange={(e) => setDescricao(e.target.value)} />
      </div>
      <p className="pd-nota">
        A demanda nasce em "Aguardando estimativa" e o fornecedor é notificado por
        e-mail. Horas e prazo são preenchidos por ele.
      </p>
    </Dialog>
  );
}

/* O que o fornecedor faz na página da demanda: informar a estimativa, anexar as
   comprovações e enviar para análise. Cada bloco aparece só no status em que a
   ação existe; nos outros a bola está com a BNI e não há o que fazer aqui. */
function PdAcoesFornecedor({ demanda, aoEstimar, aoAnexar, aoEnviarAnalise }) {
  const [horas, setHoras] = useState('');
  const [prazo, setPrazo] = useState('');
  const [categoria, setCategoria] = useState(PD_CATEGORIAS[0]);
  const [enviando, setEnviando] = useState(false);
  const arquivoRef = useRef(null);

  async function escolher(evento) {
    const arquivo = evento.target.files[0];
    evento.target.value = '';
    if (!arquivo) return;
    setEnviando(true);
    try {
      await aoAnexar(arquivo, categoria);
    } finally {
      setEnviando(false);
    }
  }

  const pedeEstimativa = demanda.status === 'Aguardando estimativa' || demanda.status === 'Em negociação';
  const pedeEnvio = demanda.status === 'Em andamento';
  if (!pedeEstimativa && !pedeEnvio) return null;

  const minutos = pdMinutosDeHHMM(horas);
  const podeEstimar = minutos !== null && !!prazo;
  const temComprovacaoFinal = demanda.anexos.some((a) => a.categoria === 'Comprovação final');
  const revisao = demanda.status === 'Em negociação';

  return (
    <div className="detalhe-secao">
      <div className="grupo-menu-rotulo">
        {pedeEstimativa ? (revisao ? 'Revisar a estimativa' : 'Informar a estimativa') : 'Enviar o trabalho'}
      </div>
      <Card accent>
        {pedeEstimativa && (
          <>
            <p className="pd-nota pd-nota-topo">
              {revisao
                ? 'A administração contestou a estimativa anterior. Leia o motivo na conversa e reenvie horas e prazo: a demanda volta para confirmação.'
                : 'Informe quanto tempo levará e para quando entrega. Os dois campos são obrigatórios para a demanda avançar.'}
            </p>
            <div className="form-grade">
              <Input label="Horas estimadas para conclusão da demanda *" placeholder="120:30"
                value={horas} onChange={(e) => setHoras(pdMascaraHoras(e.target.value))}
                helper="Duração no formato HH:MM, sem limite máximo de horas." />
              <Input type="date" label="Data estimada de conclusão da demanda *"
                value={prazo} onChange={(e) => setPrazo(e.target.value)} />
            </div>
            <div className="vinculos-acao">
              <Button variant="primary" size="sm" disabled={!podeEstimar}
                leadingIcon={<Icone nome="confirmar" />}
                onClick={() => { aoEstimar(minutos, prazo); setHoras(''); setPrazo(''); }}>
                {revisao ? 'Reenviar estimativa' : 'Enviar estimativa'}
              </Button>
              {horas && minutos !== null && (
                <span className="pd-simulacao-eco">Vai aparecer como {pdHoras(minutos)}</span>
              )}
            </div>
          </>
        )}
        {pedeEnvio && (
          <>
            <p className="pd-nota pd-nota-topo">
              Anexe as evidências do que foi feito. Para enviar à análise é preciso ao
              menos um anexo de "Comprovação final", que é a prova de conclusão.
            </p>
            {/* Duas categorias apenas, e a escolha precisa ficar à vista ao lado do
                botão que envia. Um select esticaria na célula e deixaria o botão
                vizinho parecendo solto, então a escolha vira dois botões: o ativo
                em primário preenchido, o outro em secundário. Nada de estilo novo,
                só as variantes que o design system já tem. */}
            <div className="pd-anexar">
              <div className="pd-anexar-campo">
                <span className="pd-rotulo-campo">Categoria do anexo</span>
                <div className="pd-escolha" role="group" aria-label="Categoria do anexo">
                  {PD_CATEGORIAS.map((c) => (
                    <Button key={c} size="sm" aria-pressed={categoria === c}
                      variant={categoria === c ? 'primary' : 'secondary'}
                      onClick={() => setCategoria(c)}>
                      {c}
                    </Button>
                  ))}
                </div>
              </div>
              <div className="pd-anexar-campo">
                <span className="pd-rotulo-campo">Arquivo</span>
                <input ref={arquivoRef} type="file" hidden onChange={escolher} />
                <Button variant="secondary" size="sm" disabled={enviando}
                  leadingIcon={<Icone nome="clipe" />}
                  onClick={() => arquivoRef.current && arquivoRef.current.click()}>
                  {enviando ? 'Enviando…' : 'Anexar arquivo'}
                </Button>
              </div>
            </div>
            <div className="vinculos-acao">
              <Button variant="primary" size="sm" disabled={!temComprovacaoFinal}
                leadingIcon={<Icone nome="confirmar" />} onClick={aoEnviarAnalise}>
                Enviar para análise
              </Button>
            </div>
            {!temComprovacaoFinal && (
              <p className="pd-nota">
                Envio bloqueado: ainda não há anexo de "Comprovação final".
              </p>
            )}
          </>
        )}
      </Card>
    </div>
  );
}

/* Tela 3: a página da demanda. É onde ficam as ações de aprovar e contestar. */
function PdTelaDemanda({ demanda, mensagens, trilha, naoLidas, perspectiva, aoVoltar,
  aoAgir, aoMensagem, aoMarcarLidas, aoAnexar }) {
  const [dialogo, setDialogo] = useState(null); // 'contestar' | 'devolver'
  const dados = usarDados();
  const terminal = demanda.status === 'Finalizada';
  const contrato = acharContrato(dados, demanda.contratoId);
  const ehAdmin = perspectiva === 'admin';

  const acoes = () => {
    if (!ehAdmin) return null;
    if (demanda.status === 'Aguardando confirmação') {
      return (
        <>
          <Button variant="primary" leadingIcon={<Icone nome="confirmar" />}
            onClick={() => aoAgir('aprovar-estimativa')}>
            Aprovar estimativa
          </Button>
          <Button variant="ghost" leadingIcon={<Icone nome="negar" />} onClick={() => setDialogo('contestar')}>
            Contestar
          </Button>
        </>
      );
    }
    if (demanda.status === 'Em análise') {
      return (
        <>
          <Button variant="primary" leadingIcon={<Icone nome="confirmar" />}
            onClick={() => aoAgir('aceitar-entrega')}>
            Aceitar entrega
          </Button>
          <Button variant="ghost" leadingIcon={<Icone nome="negar" />} onClick={() => setDialogo('devolver')}>
            Devolver
          </Button>
        </>
      );
    }
    return null;
  };

  /* Aviso de espera: só aparece quando quem está olhando não tem o que fazer
     nesta etapa. Do lado do fornecedor, nos status em que ele age o bloco de
     ações já diz o que fazer, e repetir seria ruído. */
  const espera = (ehAdmin ? {
    'Aguardando estimativa': 'Aguardando o fornecedor informar horas estimadas e prazo de entrega.',
    'Em negociação': 'Negociação aberta na conversa. Quando o fornecedor reenviar a estimativa, a demanda volta para confirmação.',
    'Em andamento': `Fornecedor executando. Prazo de entrega em ${dataBr(demanda.prazo)}.`,
    'Finalizada': 'Demanda finalizada e aceita. O registro é imutável: nada de anexos, mensagens ou transições.',
  } : {
    'Aguardando confirmação': 'A administração está avaliando as horas e o prazo que você informou. Pode aprovar ou contestar.',
    'Em análise': 'A administração está conferindo os anexos enviados. Pode aceitar a entrega ou devolver com o motivo na conversa.',
    'Finalizada': 'Demanda finalizada e aceita pela administração. O registro é imutável: nada de anexos, mensagens ou transições.',
  })[demanda.status];

  return (
    <div>
      <a className="voltar" href="#/demandas"
        onClick={(e) => { e.preventDefault(); aoVoltar(); }}>
        <Icone nome="voltar" tamanho={14} /> Demandas
      </a>
      <div className="pd-titulo">
        <SectionHeading className="titulo-secao"
          overline={`Demanda do contrato · ${nomeDoFornecedor(dados, demanda.contratoId)}`}
          title={demanda.nome} />
      </div>
      <div className="detalhe-acoes">
        <span className="toolbar-contagem detalhe-atualizado">Criada em {dataBr(demanda.criadaEm)}</span>
        {acoes()}
      </div>

      {espera && <div className="pd-espera"><Alert tone="info" title={PD_STATUS[demanda.status][perspectiva]}>{espera}</Alert></div>}

      <div className="detalhe-secao">
        <Card accent>
          <div className="ficha-grade">
            <div className="ficha-item">
              <div className="ficha-rotulo">Status atual</div>
              <div className="ficha-valor"><PdEtiqueta status={demanda.status} perspectiva={perspectiva} /></div>
            </div>
            <div className="ficha-item">
              <div className="ficha-rotulo">Horas estimadas para conclusão da demanda</div>
              <div className="ficha-valor">{pdHoras(demanda.minutos) || '·'}</div>
            </div>
            <div className="ficha-item">
              <div className="ficha-rotulo">Data estimada de conclusão da demanda</div>
              <div className="ficha-valor">{dataBr(demanda.prazo) || '·'}</div>
            </div>
            <div className="ficha-item">
              <div className="ficha-rotulo">Fornecedor</div>
              <div className="ficha-valor">{nomeDoFornecedor(dados, demanda.contratoId)}</div>
            </div>
            <div className="ficha-item">
              <div className="ficha-rotulo">Contrato</div>
              {/* Sem o contrato ao alcance não há ficha para abrir: o nome vira
                  texto, e o aviso de não encontrado aparece no lugar do link. */}
              <div className="ficha-valor">
                {contrato ? (
                  <a className="link-detalhe" href={`#/demandas/contrato/${contrato.id}`}>
                    {contrato.objeto}
                  </a>
                ) : nomeDoContrato(dados, demanda.contratoId)}
              </div>
            </div>
          </div>
          <div className="ficha-item-longa">
            <div className="ficha-rotulo">Descrição da tarefa</div>
            <div className="detalhe-texto pd-texto-longo">{demanda.descricao}</div>
          </div>
        </Card>
      </div>

      {!ehAdmin && (
        <PdAcoesFornecedor demanda={demanda}
          aoEstimar={(minutos, prazo) => aoAgir('enviar-estimativa', { minutos, prazo })}
          aoAnexar={aoAnexar}
          aoEnviarAnalise={() => aoAgir('enviar-para-analise')} />
      )}

      <PdAnexos anexos={demanda.anexos} status={demanda.status} perspectiva={perspectiva} />

      <PdConversa mensagens={mensagens} naoLidas={naoLidas}
        podeEscrever={!terminal} aoEnviar={(texto) => aoMensagem(texto)}
        aoAbrir={aoMarcarLidas} />

      <PdTrilha trilha={trilha} />

      {dialogo === 'contestar' && (
        <PdDialogoMensagem titulo="Contestar a estimativa" textoBotao="Contestar"
          rotulo="Motivo da contestação" aoFechar={() => setDialogo(null)}
          aoConfirmar={(texto) => {
            setDialogo(null);
            aoAgir('contestar-estimativa', { texto });
          }} />
      )}
      {dialogo === 'devolver' && (
        <PdDialogoMensagem titulo="Devolver a entrega" textoBotao="Devolver"
          rotulo="O que precisa mudar" aoFechar={() => setDialogo(null)}
          aoConfirmar={(texto) => {
            setDialogo(null);
            aoAgir('devolver-entrega', { texto });
          }} />
      )}
    </div>
  );
}

/* Raiz do módulo: carrega os dados, executa as ações contra a API e escolhe a
   tela pela sub-rota. `rota` é o caminho depois de "demandas":
     []                      -> página principal
     ['contrato', id]        -> ficha do contrato
     ['demanda', id]         -> página da demanda */
function PainelDemandas({ avisar, rota = [] }) {
  const usuario = usarUsuario();
  const externo = !!usuario?.externo;
  /* A perspectiva é o papel de quem entrou, e não uma escolha: a BNI vê a visão
     dela, o fornecedor a dele. Os rótulos de status seguem daí (PD_STATUS). */
  const perspectiva = externo ? 'fornecedor' : 'admin';
  // Quem cria demanda é a BNI, e só quem escreve: o papel de leitura não cria.
  const podeCriar = !externo && !usuario?.somenteLeitura;

  const [dados, setDados] = useState(null);
  const [demandas, setDemandas] = useState(null);
  const [detalhe, setDetalhe] = useState(null);
  const [erro, setErro] = useState('');
  const [criando, setCriando] = useState(null); // { contratoFixo } | null
  const [exibicao, setExibicao] = useState('lista');
  const [filtro, setFiltro] = useState({ fornecedorId: '', contratoId: '' });

  const [onde, id] = rota;

  const carregar = useCallback(async () => {
    /* O usuário externo não alcança /api/fornecedores: a área não é dele, e
       pedir devolveria 403 e derrubaria a carga inteira. No lugar da lista
       entra a própria empresa, o único fornecedor que ele veria de qualquer
       forma, nomeada do ponto de vista de quem olha. */
    const [respContratos, respDemandas, respFornecedores] = await Promise.all([
      chamarApi('/api/contratos'),
      chamarApi('/api/demandas'),
      externo ? Promise.resolve(null) : chamarApi('/api/fornecedores'),
    ]);
    const fornecedores = respFornecedores
      ? respFornecedores.registros.map(paraFornecedor)
      : [{ id: String(usuario?.fornecedorId || ''), nome: 'Sua empresa' }];
    setDados({ fornecedores, contratos: respContratos.registros.map(paraContrato) });
    setDemandas(respDemandas.registros.map((r) => paraDemanda(r)));
  }, [externo, usuario?.fornecedorId]);

  /* A página da demanda precisa de conversa, trilha e anexos, que não vêm na
     listagem: uma chamada só traz tudo, mais a contagem de não lidas de quem
     está olhando. */
  const carregarDetalhe = useCallback(async (demandaId) => {
    const corpo = await chamarApi(`/api/demandas/${demandaId}/detalhe`);
    setDetalhe({
      demanda: paraDemanda(corpo.registro, corpo.anexos.map(paraAnexo)),
      mensagens: corpo.mensagens,
      trilha: corpo.trilha,
      naoLidas: corpo.naoLidas,
    });
  }, []);

  useEffect(() => {
    carregar().catch((excecao) => setErro(excecao.message));
  }, [carregar]);

  useEffect(() => {
    if (onde !== 'demanda' || !id) { setDetalhe(null); return; }
    setDetalhe(null);
    carregarDetalhe(id).catch((excecao) => setErro(excecao.message));
  }, [onde, id, carregarDetalhe]);

  const abrir = (demandaId) => { window.location.hash = `/demandas/demanda/${demandaId}`; };
  const voltar = () => { window.location.hash = '/demandas'; };

  // Toda escrita recarrega o que mudou: o servidor é quem decide o resultado,
  // e refazer a conta na tela seria adivinhar.
  const recarregar = async (demandaId) => {
    await carregar();
    if (demandaId) await carregarDetalhe(demandaId);
  };

  async function criar(contratoId, nome, descricao) {
    try {
      const { registro } = await chamarApi('/api/demandas', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ dados: { nome, descricao, contrato: contratoId } }),
      });
      setCriando(null);
      await carregar();
      avisar('success', `Demanda criada para ${nomeDoFornecedor(dados, contratoId)}.`);
      abrir(registro.id);
    } catch (excecao) {
      avisar('warning', excecao.message);
    }
  }

  /* Uma ação do fluxo. O corpo leva o que a ação exigir: horas e prazo na
     estimativa, o texto na contestação e na devolução. */
  async function agir(demandaId, acao, corpo = {}) {
    try {
      const { rotulo, registro } = await chamarApi(`/api/demandas/${demandaId}/acoes`, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ acao, ...corpo }),
      });
      await recarregar(demandaId);
      avisar('success', `${rotulo}. Status agora é "${registro.dados.status}".`);
    } catch (excecao) {
      avisar('warning', excecao.message);
    }
  }

  async function mensagem(demandaId, texto) {
    try {
      await chamarApi(`/api/demandas/${demandaId}/mensagens`, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ texto }),
      });
      await carregarDetalhe(demandaId);
    } catch (excecao) {
      avisar('warning', excecao.message);
    }
  }

  async function marcarLidas(demandaId) {
    try {
      await chamarApi(`/api/demandas/${demandaId}/lida`, { method: 'POST' });
      await carregarDetalhe(demandaId);
    } catch { /* a conversa abre mesmo se a marca falhar */ }
  }

  async function anexar(demandaId, arquivo, categoria) {
    try {
      await chamarApi(`/api/demandas/${demandaId}/anexos`, {
        method: 'POST',
        headers: {
          'content-type': arquivo.type || 'application/octet-stream',
          'x-nome-arquivo': encodeURIComponent(arquivo.name),
          'x-categoria-anexo': encodeURIComponent(categoria),
        },
        body: arquivo,
      });
      await carregarDetalhe(demandaId);
      avisar('success', `Anexo de "${categoria}" enviado.`);
    } catch (excecao) {
      avisar('warning', excecao.message);
    }
  }

  const titulo = (
    <SectionHeading className="titulo-secao" overline="Gestão de contratos" title="Demandas" />
  );

  if (erro) {
    return <div>{titulo}<Alert tone="warning" title="Não foi possível carregar as demandas">{erro}</Alert></div>;
  }
  if (!dados || !demandas) {
    return <div>{titulo}<p className="toolbar-contagem">Carregando…</p></div>;
  }

  if (onde === 'contrato') {
    const contrato = acharContrato(dados, id);
    if (!contrato) {
      return (
        <div>
          <a className="voltar" href="#/demandas"><Icone nome="voltar" tamanho={14} /> Demandas</a>
          <Alert tone="warning" title="Contrato não encontrado">Volte para a lista de demandas.</Alert>
        </div>
      );
    }
    return (
      <ContextoDados.Provider value={dados}>
        <PdTelaContrato contrato={contrato} demandas={demandas} perspectiva={perspectiva}
          podeCriar={podeCriar}
          aoAbrir={abrir} aoCriar={() => setCriando({ contratoFixo: contrato.id })} />
        {criando && (
          <PdDialogoNovaDemanda contratoFixo={criando.contratoFixo}
            aoFechar={() => setCriando(null)} aoCriar={criar} />
        )}
      </ContextoDados.Provider>
    );
  }

  if (onde === 'demanda') {
    if (!detalhe) {
      return (
        <div>
          <a className="voltar" href="#/demandas"><Icone nome="voltar" tamanho={14} /> Demandas</a>
          <p className="toolbar-contagem">Carregando…</p>
        </div>
      );
    }
    return (
      <ContextoDados.Provider value={dados}>
        <PdTelaDemanda demanda={detalhe.demanda} mensagens={detalhe.mensagens}
          trilha={detalhe.trilha} naoLidas={detalhe.naoLidas}
          perspectiva={perspectiva} aoVoltar={voltar}
          aoAgir={(acao, corpo) => agir(detalhe.demanda.id, acao, corpo)}
          aoMensagem={(texto) => mensagem(detalhe.demanda.id, texto)}
          aoMarcarLidas={() => marcarLidas(detalhe.demanda.id)}
          aoAnexar={(arquivo, categoria) => anexar(detalhe.demanda.id, arquivo, categoria)} />
      </ContextoDados.Provider>
    );
  }

  return (
    <ContextoDados.Provider value={dados}>
      <PdPaginaPrincipal demandas={demandas} perspectiva={perspectiva}
        fornecedorLogado={String(usuario?.fornecedorId || '')}
        exibicao={exibicao} aoTrocarExibicao={setExibicao}
        filtro={filtro} aoFiltrar={setFiltro}
        podeCriar={podeCriar}
        aoAbrir={abrir} aoCriar={() => setCriando({})} />
      {criando && (
        <PdDialogoNovaDemanda contratoFixo={criando.contratoFixo}
          aoFechar={() => setCriando(null)} aoCriar={criar} />
      )}
    </ContextoDados.Provider>
  );
}

/* A lista de demandas para quem não é a tela de demandas: a ficha genérica do
   contrato (#/contratos/<id>) lista os registros que apontam para ele, e a linha
   de vínculo do CRUD só sabe mostrar o nome. A demanda tem horas, prazo e
   status, que é o que se quer ver ali, e o clique vai direto para ela.

   Recebe os registros crus da API, como vieram do CRUD genérico, e reusa a
   mesma lista da tela — o formato fica igual nos dois lugares por construção,
   e não por duas implementações que combinam hoje. */
function ListaDemandasVinculadas({ registros, perspectiva, aoAbrir }) {
  const demandas = (registros || []).map((r) => paraDemanda(r));
  return <PdLista demandas={demandas} perspectiva={perspectiva} aoAbrir={aoAbrir} />;
}

window.PainelDemandas = PainelDemandas;
window.ListaDemandasVinculadas = ListaDemandasVinculadas;
