Multiple Buttons in the List JS

image

Hey Team,
Is this feature available in frappe 14 ?

we added this by making changes in the backend files

can you please guide me

Do you have knowlege in the backend

yes i do

What is your requriments and what is the purpose of these buttons

The button actions should change values of select field

Can you explain with an example

okay suppose i want to change status of Lead. button dropdown should contain options such as draft, converted etc. and selecting the option should result in changing status.

Ok let me create the logic for this one i will update you once i complete
currently i m using to create chat

thank you . looking forward for it

sorry for being late i have been busy in my project so couldn’t manage the time to update you

We are working on a new project with same senario once this is compeleted i will share the step by step guide for you thank you for your patience

@Ashique Hey
thanks for the patience here is our custom script and final results

Hey, thank you.
can you share the script

@Ashique here is the complete script to add button in list view and perform workflow actions hope this helps :blush:

i suggest you look at the script carefully and adjust it as per your requirment
as this is based on our requirment
what this script will do
it will show the status button in the list view currrently we have 3 status pending lvl 1 approve and submit so on click it will goto lvl 1 approved and then again on click it will goto submit you can also add multiple buttons in the dropdown to perform same actions
this is the final outcome

// List view settings for Shift Request
frappe.listview_settings['Shift Request'] = {
    hide_name_column : true,
    button: {
        // Always show custom action buttons
        show(doc) {
            return true;
        },

        // Define buttons per row
        get_label(doc) {
            // Status button changes based on docstatus
            const status_button = (() => {
                if (doc.docstatus === 1) {
                    return `<button class="btn btn-xs btn-default" title="Approved">
                        <i class="fa fa-check-circle text-success"></i> Approved
                    </button>`;
                } else if (doc.docstatus === 2) {
                    return `<button class="btn btn-xs btn-default" title="Cancelled">
                        <i class="fa fa-times-circle text-danger"></i> Cancelled
                    </button>`;
                }
                // Show action buttons based on role and workflow state
                if (frappe.user.has_role("Shift Request LV-1") && doc.workflow_state === "Pending") {
                    return `<button onClick="approveFunction(event, this, '${doc.name}')" class="btn btn-xs btn-default" title="Approve">
                                <i class="fa fa-hourglass-half text-warning"></i> Approve
                            </button>`;
                } else if (frappe.user.has_role("Shift Request Approver") && doc.workflow_state === "LV-1 Approved") {
                    return `<button onClick="submitFunction(event, this, '${doc.name}')" class="btn btn-xs btn-default" title="Submit">
                                <i class="fa fa-hourglass-half text-warning"></i> Submit
                            </button>`;
                }
            })();

            // Combine all buttons (status + view/edit/delete)
            const buttons = [
                status_button,
                `<button onClick="viewFunction(event, this, '${doc.name}')" class="btn btn-xs btn-default" title="View">
                    <i class="fa fa-eye"></i>
                </button>`,
                `<button onClick="editFunction(event, this, '${doc.name}')" class="btn btn-xs btn-default" title="Edit">
                    <i class="fa fa-edit"></i>
                </button>`,
                `<button onClick="deleteFunction(event, this, '${doc.name}')" class="btn btn-xs btn-default" title="Delete">
                    <i class="fa fa-trash"></i>
                </button>`
            ];
            
            return `
                <span class="btn-group list-actions-extended" style="display: flex; gap: 4px;">
                    ${buttons.join('\n')}
                </span>
            `;
        },

        get_description() {
            return '';
        }
    },

    onload(listview) {
        // Store listview globally for refresh use
        window.current_shift_listview = listview;

        // Add custom status filter
        listview.page.add_field({
            fieldname: 'docstatus',
            label: 'Status',
            fieldtype: 'Select',
            options: [0, 1, 2],
            default: null,
            input_class: 'input-xs',
            placeholder: 'Status',
            is_filter: 1,
            condition: '=',
            onchange: () => listview.refresh()
        }, listview.page.page_form.find('.standard-filter-section'));

        // Replace filter option labels
        const doc_filter = document.querySelector("select[data-fieldname='docstatus']");
        if (doc_filter) {
            doc_filter.innerHTML = '';
            doc_filter.add(new Option('', ''));
            doc_filter.add(new Option('Pending Approval', '0'));
            doc_filter.add(new Option('Approved', '1'));
            doc_filter.add(new Option('Cancelled', '2'));
        }

        // Remove Frappe's default action buttons
        new MutationObserver(() => {
            $('.list-row').find('.list-actions .btn-action').remove();
        }).observe(document.body, { childList: true, subtree: true });
    }
};

// --- Utility Functions ---

// Prevent event bubbling and default behavior
function stopEvent(event) {
    event.preventDefault();
    event.stopPropagation();
}

// Delete document with confirmation
window.deleteFunction = function (event, el, doc_name) {
    stopEvent(event);
    frappe.confirm('Are you sure you want to delete this record?', () => {
        frappe.call({
            method: 'frappe.client.delete',
            args: { doctype: 'Shift Request', name: doc_name },
            callback: () => {
                frappe.show_alert('Deleted');
                window.current_shift_listview.refresh();
            }
        });
    });
};

// Navigate to form view
window.viewFunction = function (event, el, doc_name) {
    stopEvent(event);
    frappe.set_route('Form', 'Shift Request', doc_name);
};

// Show editable dialog and update document
window.editFunction = function (event, el, doc_name) {
    stopEvent(event);

    frappe.call({
        method: 'frappe.client.get',
        args: {
            doctype: 'Shift Request',
            name: doc_name
        },
        callback({ message: doc }) {
            const d = new frappe.ui.Dialog({
                title: `Edit Shift Request: ${doc.name}`,
                fields: [
                    { fieldtype: 'Section Break', label: 'Shift Details' },
                    {
                        label: 'Shift Type',
                        fieldname: 'shift_type',
                        fieldtype: 'Link',
                        options: 'Shift Type',
                        default: doc.shift_type,
                        reqd: 1,
                        get_query: () => ({
                            filters: [
                                ['name', 'in', [
                                    'add your shift type if needed'
                                ]]
                            ]
                        })
                    },
                    { fieldtype: 'Column Break' },
                    { fieldtype: 'Section Break' },
                    {
                        label: 'From Date',
                        fieldname: 'from_date',
                        fieldtype: 'Date',
                        default: doc.from_date,
                        reqd: 1
                    },
                    { fieldtype: 'Column Break' },
                    {
                        label: 'From Time',
                        fieldname: 'from_time',
                        fieldtype: 'Time',
                        default: doc.from_time,
                        reqd: 1
                    },
                    { fieldtype: 'Section Break' },
                    {
                        label: 'To Date',
                        fieldname: 'to_date',
                        fieldtype: 'Date',
                        default: doc.to_date,
                        reqd: 1
                    },
                    { fieldtype: 'Column Break' },
                    {
                        label: 'To Time',
                        fieldname: 'to_time',
                        fieldtype: 'Time',
                        default: doc.to_time,
                        reqd: 1
                    },
                ],
                primary_action_label: 'Update',
                primary_action(values) {
                    Object.assign(doc, values);
                    frappe.call({
                        method: 'frappe.client.save',
                        args: { doc },
                        callback: () => {
                            frappe.show_alert({ message: 'Updated successfully', indicator: 'green' });
                            d.hide();
                            window.current_shift_listview.refresh();
                        }
                    });
                }
            });

            d.show();
        }
    });
};

// Approve document
window.approveFunction = function (event, el, doc_name) {
    stopEvent(event);

    frappe.confirm(`Approve ${doc_name}?`, () => {
        // Fetch document before applying workflow
        frappe.call({
            method: 'frappe.client.get',
            args: { doctype: 'Shift Request', name: doc_name },
            callback: ({ message: doc }) => {
                if (!doc) return frappe.msgprint('Document not found');

                // Apply workflow action
                frappe.xcall('frappe.model.workflow.apply_workflow', {
                    doc: doc,
                    action: 'Approve' // Can be made dynamic based on state
                }).then(() => {
                    frappe.show_alert({ message: 'Approved Successfully', indicator: 'green' });
                    window.current_shift_listview.refresh();
                });
            }
        });
    });
};


// Submit (approve) document
window.submitFunction = function (event, el, doc_name) {
    stopEvent(event);
    frappe.confirm(`Approve ${doc_name}?`, () => {
        frappe.call({
            method: 'frappe.client.get',
            args: { doctype: 'Shift Request', name: doc_name },
            callback: ({ message: doc }) => {
                frappe.call({
                    method: 'frappe.client.submit',
                    args: { doc },
                    callback: () => {
                        frappe.show_alert({
                            message: 'Approved',
                            indicator: 'green'
                        });
                        window.current_shift_listview.refresh();
                    }
                });
            }
        });
    });
};
1 Like