Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom actions confirmation #3455

Closed
soullivaneuh opened this issue Jul 2, 2020 · 13 comments
Closed

Custom actions confirmation #3455

soullivaneuh opened this issue Jul 2, 2020 · 13 comments

Comments

@soullivaneuh
Copy link
Contributor

soullivaneuh commented Jul 2, 2020

Short description of what this feature will allow to do:

Be able to quickly configure a confirmation page/popup about a custom action

Example of how to use this feature

Something like this:

public function configureActions(Actions $actions): Actions
{
    return parent::configureActions($actions)
        ->add(
            Crud::PAGE_DETAIL,
            Action::new('reset', 'Reset')
                ->linkToCrudAction('reset')
                ->askConfirmation('Are you very very sure? This will destroy the world!')
            ,
        )
    ;
}

I found an option on the twig files that looks like the feature I want:

<button type="submit" class="btn {{ action.css_class|default('btn-secondary') }} {% if action.ask_confirm %}ask-confirm-batch-button{% endif -%}" title="{{ action.title|default('') is empty ? '' : action.title|trans(_trans_parameters) }}" name="{{ form.name.vars.full_name }}" value="{{ action.name }}">

But I don't find anything on the documentation about how to use it.

@soullivaneuh
Copy link
Contributor Author

Also related to #1279 and #2978.

You apparently did some work about a feature like this, but it was too complicated.

I'm not sure to understand. How a confirmation message to display is too complicated? The proposed configuration way looks ok to me. 🤔

@soullivaneuh
Copy link
Contributor Author

It looks we already have enough resource to make the confirmation modal with ease thanks to the delete action implement:

image

@soullivaneuh
Copy link
Contributor Author

Another nice to have thing would be alternative actions to propose in addition to "Cancel". For example:

public function configureActions(Actions $actions): Actions
{
    return parent::configureActions($actions)
        ->add(
            Crud::PAGE_DETAIL,
            Action::new('reset', 'Reset')
                ->linkToCrudAction('reset')
                ->askConfirmation('Are you very very sure? This will destroy the world!', [
                    Action::new('index', 'Return to list')->linkToCrudAction('index'),
                ])
            ,
        )
    ;
}

What do you think?

@soullivaneuh
Copy link
Contributor Author

The askConfirmation may even take 3 parameters: Title, message (optional), alternativeActions (optional).

This would look like this:

Action::new('reset', 'Reset')
    ->linkToCrudAction('reset')
    ->askConfirmation(
        'Are you very very sure to reset the source of life?',
        'This will destroy the world!',
        [
            Action::new('index', 'Return to list')->linkToCrudAction('index'),
        ],
    )
;

@Quenway
Copy link

Quenway commented Jan 28, 2022

Hi @soullivaneuh,

I had the same problem here... A lot of custom action who should ask confirm before process... I found a solution right now, and hope it can help you...

I take my inspiration from the delete-action which already toggle a confirm modal :D

So, here we go :

  1. Create a template in directory templates/bundles/EasyAdminBundle/layout.html.twig
{% extends '@!EasyAdmin/layout.html.twig' %}

{%  block content_footer_wrapper %}
    <div id="modal-confirm" class="modal fade" tabindex="-1">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-body">
                    <h4>Êtes vous sûr ?</h4>
                    <p>Parce que ça va faire des trucs de fifou</p>
                </div>
                <div class="modal-footer">
                    <button type="button" data-bs-dismiss="modal" class="btn btn-secondary">
                        <span class="btn-label">{{ 'action.cancel'|trans([], 'EasyAdminBundle') }}</span>
                    </button>

                    <button type="button" data-bs-dismiss="modal" class="btn btn-success" id="modal-confirm-button">
                        <span class="btn-label">{{ 'action.confirm'|trans([], 'EasyAdminBundle') }}</span>
                    </button>
                </div>
            </div>
        </div>
    </div>
{% endblock %}
  1. Add HTML Attribute data-bs-toggle and data-bs-target to your custom action PLUS the CSS class confirm-action
        $validAction = Action::new('valid', 'Valider', 'fa fa-check')
            ->displayAsLink()
            ->linkToCrudAction('validate')
            ->addCssClass('confirm-action')
            ->setHtmlAttributes([
                'data-bs-toggle' => 'modal',
                'data-bs-target' => '#modal-confirm',
            ])
        ;
  1. Create a JS file in public/assets/js/, I named it confirm-modal.js (Globally, it prevent the redirection onClick, show the modal, and add a listener on confirm button in the modal in order to redirect to the href link.
document.addEventListener("DOMContentLoaded",(
    function() {
        document.querySelectorAll(".confirm-action").forEach((function(e){
            e.addEventListener("click",(function(t){
                t.preventDefault();
                document.querySelector("#modal-confirm-button").addEventListener("click",(function(){
                    location.replace(e.getAttribute("href"));
                }));
            }));
        }));
    }
));
  1. In the CRUD controller of your entity, create configureAssets function and call the JS file confirm-modal.js
public function configureAssets(Assets $assets): Assets
{
    $assets->addJsFile('assets/js/confirm-modal.js');

    return parent::configureAssets($assets);
}

It's my first shot, it's not perfectly integrated but it Works ! Now I just have to adjust some things to make this more generic.

I'm glad that I can share this with you, personally, this thing made my day 🥇

@DARDORKE
Copy link

Hello ! I tried this solution by updating 'edit/new actions' to get a confirmation modal but when I confirm into the modal window, the page page reload and nothing persist in the database.
Any solutions ?

ModuleCrudController (src/Controller/Admin/ModuleCrudController.php) :

public function configureActions(Actions $actions): Actions
    {
        return $actions
            ->update(Crud::PAGE_INDEX, Action::NEW,
            fn(Action $action) => $action
                ->setLabel('Ajouter un module'))
            ->update(Crud::PAGE_INDEX, Action::BATCH_DELETE,
            fn(Action$action) => $action
                ->setLabel('Supprimer'))
            ->update(Crud::PAGE_NEW, Action::SAVE_AND_ADD_ANOTHER,
            fn(Action $action) => $action
                ->setLabel('Créer et ajouter un nouveau module')
                ->displayAsLink()
                ->addCssClass('confirm-action')
                ->setHtmlAttributes([
                    'data-bs-toggle' => 'modal',
                    'data-bs-target' => '#modal-confirm',
                ]));

ModuleCrudController (src/Controller/Admin/ModuleCrudController.php) :

public function configureAssets(Assets $assets): Assets
    {
        $assets->addJsFile('assets/js/confirm-modal.js');

        return parent::configureAssets($assets);
    }

layout.html.twig (templates/bundles/EasyAdminBundle/layout.html.twig) :

{% extends '@!EasyAdmin/layout.html.twig' %}

{%  block content_footer_wrapper %}
    <div id="modal-confirm" class="modal fade" tabindex="-1">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-body">
                    <h4>Êtes-vous sûr ?</h4>
                    <p>Parce que ça va faire des trucs de fifou</p>
                </div>
                <div class="modal-footer">
                    <button type="button" data-bs-dismiss="modal" class="btn btn-secondary">
                        <span class="btn-label">{{ 'action.cancel'|trans([], 'EasyAdminBundle') }}</span>
                    </button>

                    <button type="button" data-bs-dismiss="modal" class="btn btn-success" id="modal-confirm-button">
                        <span class="btn-label">{{ 'action.confirm'|trans([], 'EasyAdminBundle') }}</span>
                    </button>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

confirm-modal.js (public/assets/js/confirm-modal.js) :

document.addEventListener("DOMContentLoaded",(
    function() {
        document.querySelectorAll(".confirm-action").forEach((function(e){
            e.addEventListener("click",(function(t){
                t.preventDefault();
                document.querySelector("#modal-confirm-button").addEventListener("click",(function(){
                    location.replace(e.getAttribute("href"));
                }));
            }));
        }));
    }
));

@elkouo
Copy link

elkouo commented Feb 24, 2023

Are you on the EDIT or LIST page ?

Because on EDIT page, the layout is not include. You can rewrite "crud/detail.html.twig" with the same {% block content_footer_wrapper %}

@lwillems
Copy link

Same thing with stimulus.js (symfony ux)

overrided ea layout.html.twig

{% extends '@!EasyAdmin/layout.html.twig' %}
{% block content %}
    <div data-controller="confirm-modal">
        {{ include('/admin/crud/includes/_action_modal.html.twig') }}
        {{ parent() }}
    </div>
{% endblock %}

_action_modal.html.twig

<div id="modal-confirm" class="modal fade" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-body">
                <h4>{{ 'action.modal.archive_title'|trans([], 'admin') }}</h4>
            </div>
            <div class="modal-footer">
                <button type="button" data-bs-dismiss="modal" class="btn btn-secondary">
                    <span class="btn-label">{{ 'action.cancel'|trans([], 'EasyAdminBundle') }}</span>
                </button>

                <button type="button" data-confirm-modal-target="confirm" data-bs-dismiss="modal" class="btn btn-danger" id="modal-confirm-button">
                    <span class="btn-label">{{ 'batch_action_modal.action'|trans(domain = 'EasyAdminBundle') }}</span>
                </button>
            </div>
        </div>
    </div>
</div>

In your controller custom action config

    $archiveBatch = Action::new('archive', 'action.archive', 'fa fa-archive')
        ->linkToCrudAction('archiveBatch')
        ->addCssClass('text-danger')
        ->setHtmlAttributes([
            'data-action' => 'click->confirm-modal#confirm',
            'data-bs-toggle' => 'modal',
            'data-bs-target' => '#modal-confirm',
        ]);

confirm_modal_controller.js

import { Controller } from '@hotwired/stimulus';

/*
* The following line makes this controller "lazy": it won't be downloaded until needed
* See https://github.com/symfony/stimulus-bridge#lazy-controllers
*/
/* stimulusFetch: 'lazy' */
export default class extends Controller {
    static targets = ['confirm']

    confirm(event) {
        event.preventDefault();
        this.confirmTarget.addEventListener('click', function() {
            location.replace(event.target.getAttribute('href'));
        });
    }
}

@astronati
Copy link

news about this?

@alex-mbp
Copy link

This would be a useful feature

@radziejewicz
Copy link

Bump this issue :D

@Seros
Copy link

Seros commented Nov 13, 2024

Just leaving this here in case someone is looking for a quick and easy solution
#2978 (comment)

@javiereguiluz
Copy link
Collaborator

Thanks for this proposal. I'm closing this issue in favor of #6674, which tries to solve this and other closely related issues. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants