DepartamentoProdutoList editar arquivo

0:00 / 0:00

Arquivo MAIS EDITADO da sequência DELETE. Ganha a coluna da lixeirinha na tabela, a inner class DeleteFromList que trata o ciclo de dois cliques (confirmar + executar), e uma constante nova CONFIRM_LIST.

CÓDIGO COMPLETO
package br.xt.app.departamento.produto;

import br.jasap.core.Effect;
import br.jasap.effect.Response;
import br.jasap.gui.JasapPage;
import br.jasap.gui.ListColumn;
import br.jasap.gui.ListLine;
import br.jasap.gui.ListView;
import br.jasap.gui.Table;
import br.jasap.gui.Toast;
import br.jasap.util.JasapFunctions;
import br.jasap.util.Js;
import br.jasap.util.exceptions.SQLConstraintException;
import br.xt.acore.view.IconButton;
import br.xt.acore.view.XtPage;

public class DepartamentoProdutoList extends DepartamentoProdutoAction {

    @Override
    public Effect execute() throws Exception {
        render();
        return new Response();
    }

    public void render() throws Exception {
        XtPage page = new XtPage(getManager());
        if (isAjaxCall()) {
            update(JasapPage.DIV_WINDOW, page.content(window().toHtml()));
        } else {
            page.getTable()
                    .setBorder(4)
                    .rowC("100%")
                    .setContent(page.content(window().toHtml()))
                    .setStyle(ui().stretchBorder());
            page.setWinTitle("Produtos");
            getOutput().write(this, page);
        }
    }

    public Table window() throws Exception {
        Table w = new Table(getManager()).setSize("100%", "100%");
        w.rowC("99%", JasapPage.DIV_WSPACE, lView());
        w.rowC("1%",  null, ui().line());
        return w;
    }

    private ListView lv = null;
    public ListView lView() throws Exception {
        if (lv == null) {
            lv = ui().lView();

            lv.setOrderBy(DepartamentoProdutoBean.NOME_PRODUTO);
            lv.setPageSize(9999);

            lv.setFiltro(getFiltro());
            getFactory().departamento().proModel().daoList(lv.getData());

            ListColumn col_nome = lv.newColumn("Nome").setWidth(220).setPadding(";padding:10 8 10 8;");
            ListColumn col_vl   = lv.newColumn("Valor").setWidth(100).setPadding(";padding:10 8 10 8;").alignCenter();
            ListColumn col_qtd  = lv.newColumn("Qtd").setWidth(80).setPadding(";padding:10 8 10 8;").alignCenter();
            ListColumn col_obs  = lv.newColumn("Observação").setPadding(";padding:10 8 10 8;");
            ListColumn col_del  = lv.newColumn("").setWidth(50).setPadding(";padding:6 4 6 4;").alignCenter();

            while (lv.hasNext()) {
                DepartamentoProdutoBean bean = (DepartamentoProdutoBean) lv.next();
                ListLine line = lv.createLine();
                col_nome.setContent(bean.getNome_produto());
                col_vl.setContent(bean.getVl_produto());
                col_qtd.setContent(bean.getQtd_produto());
                col_obs.setContent(bean.getObs_produto());
                col_del.setHtmlData(new IconButton("trash")
                        .setColor("#d9534f")
                        .setTitle("Excluir")
                        .setOnclick(link(DeleteFromList.class).putInteger(DepartamentoProdutoBean.ID_PRODUTO, bean.getId_produto()).ajax())
                        .toHtml());
                lv.addLine(line);
            }
        }
        return lv;
    }

    public DepartamentoProdutoWBean getFiltro() throws Exception {
        DepartamentoProdutoWBean filtro = (DepartamentoProdutoWBean) getSession().getObject(FILTRO);
        if (filtro == null) {
            filtro = proWBean();
            getSession().addObj(FILTRO, filtro);
        }
        return filtro;
    }

    public static class DeleteFromList extends DepartamentoProdutoList {
        @Override
        public Effect execute() throws Exception {
            try {
                Integer id = getInput().getInteger(DepartamentoProdutoBean.ID_PRODUTO);
                if (JasapFunctions.equals(getInput().getInteger(CONFIRM_LIST), 1)) {
                    DepartamentoProdutoBean bean = new DepartamentoProdutoBean();
                    bean.setId_produto(id);
                    getFactory().departamento().proModel().daoDelete(bean);
                    update(lView().getDIV_BODY(), lView().getBody());
                    update(lView().getDIV_NAVIGATE(), lView().getNavForm());
                } else {
                    eval(Js.swalConfirm("Confirma a exclusão desse registro?",
                            link(DeleteFromList.class)
                                    .putInteger(DepartamentoProdutoBean.ID_PRODUTO, id)
                                    .putInteger(CONFIRM_LIST, 1).ajax(), ""));
                }
                return new Response();
            } catch (SQLConstraintException e) {
                eval(new Toast("Falha ao excluir: esse registro está sendo usado em outra tabela.").fail());
                return new Response();
            }
        }
    }

    public static final String LIST         = ROOT.concat("__LIST/");
    public static final String FILTRO       = LIST.concat("__FILTRO");
    public static final String CONFIRM_LIST = LIST.concat("__CONFIRM_LIST");

}

Mudanças nesta sequência: 5 imports novos, 1 coluna nova (col_del), IconButton dentro do while, 1 inner class inteira (DeleteFromList), 1 constante (CONFIRM_LIST).

IconButton — componente de ícone do projeto

IconButton é um componente fluent de br.xt.acore.view que gera um botão de ícone pronto com event.stopPropagation() embutido. Ele encapsula o HTML do botão que antes era montado na mão:

new IconButton("trash")       // ícone de lixeira (glyphicon)
    .setColor("#d9534f")       // vermelho Bootstrap (danger)
    .setTitle("Excluir")       // tooltip ao passar o mouse
    .setOnclick(link(...).ajax())  // ação Ajax ao clicar
    .toHtml()                  // gera o HTML final

O stopPropagation já vem embutido — quando a linha da tabela tiver setOnclick (na sequência UPDATE), clicar no ícone NÃO abre o formulário de edição.

Usa setHtmlData() (não setContent()) porque o resultado é HTML bruto que o browser precisa interpretar como botão real.

O ciclo de dois cliques — DeleteFromList

A inner class DeleteFromList trata DOIS cenários no mesmo método:

CliqueFlag CONFIRM_LISTO que o agente faz
1º (na lixeirinha)Ausente / não é 1Entra no else. Escreve ordem Js.swalConfirm pro browser abrir popup de confirmação. O botão SIM do popup chama a PRÓPRIA DeleteFromList de novo — com CONFIRM_LIST=1.
2º (SIM no popup)1Entra no if. Cria ficha em branco com só o ID, chama daoDelete, e atualiza parcialmente DIV_BODY + DIV_NAVIGATE da ListView.

Importante: cada clique é um request NOVO, com agente e cartão novos. O encadeamento ("o segundo clique precisa vir depois do primeiro") vive no CÓDIGO (a flag no input), não na memória do agente — cada agente chega zero-memória.

Por que capturar SQLConstraintException separadamente?

Tentar excluir um produto que tem FK apontando pra ele em outra tabela faz o banco REJEITAR o delete, lançando SQLConstraintException. Esse erro tem uma CAUSA CLARA que dá pra explicar pro usuário.

Sem o catch, a exceção subiria pro ErrorFilter global que mostra "Ocorreu um erro" genérico. Com o catch específico, mostramos Toast com mensagem útil: "Falha ao excluir: esse registro está sendo usado em outra tabela."

Outros erros (conexão, query inválida) NÃO são capturados aqui — sobem pro ErrorFilter, que é a rede de proteção padrão.

Update parcial pós-delete

Depois do daoDelete, em vez de recarregar a página inteira, atualizamos só o corpo da lista:

update(lView().getDIV_BODY(), lView().getBody());
update(lView().getDIV_NAVIGATE(), lView().getNavForm());

Ao chamar lView() depois do delete, ele é reexecutado — busca a lista nova no banco (sem a linha excluída) e remonta a tabela. O framework manda os dois updates Ajax pro browser, que cola os novos conteúdos por cima dos antigos. Usuário vê a lista sem a linha, tudo sem reload.