Add Comment in Sidebar

Hi everyone, do you have any ideas on how to retain all the comments added in a form in the sidebar?

I’ve added a client script to allow comments in the sidebar, and it works. However, when I submit the form with an existing comment, that comment is visible. But when I add a new comment, it appears in the sidebar temporarily; upon reloading the website, only the first comment remains in the sidebar, while the new comment is only visible at the bottom of the form.

I want all comments to be consistently visible in the sidebar for easier user access.

This is the client script:

frappe.ui.form.on('Purchase Order', {
    refresh: function(frm) {
        // Function to get the first name from full name
        var getFirstName = function(fullName) {
            return fullName.split(' ')[0]; // Split by space and get the first part
        };

        // Ensure the custom comments section exists in the sidebar
        if ($('.custom-comments-section').length === 0) {
            $('<div class="custom-comments-section"><h4>Comments</h4></div>').appendTo('.form-sidebar');
        }

        // Function to add or update comment in custom sidebar section
        var add_or_update_comment_to_sidebar = function(comment_id, comment, user_fullname) {
            var first_name = getFirstName(user_fullname); // Get first name
            var $existing_comment = $(`.comment[data-comment-id="${comment_id}"]`);
            
            if ($existing_comment.length > 0) {
                // Update existing comment
                $existing_comment.find('.comment-text').text(comment);
                $existing_comment.find('.comment-by').text(first_name); // Update first name
            } else {
                // Add new comment
                var comment_html = `
                    <div class="comment" data-comment-id="${comment_id}">
                        <div class="comment-body">
                            <b class="comment-by">${first_name}</b>: <span class="comment-text">${comment}</span>
                        </div>
                        <div class="comment-actions">
                            <a href="#" class="edit-comment btn btn-link btn-xs">Edit</a>
                            <a href="#" class="delete-comment btn btn-link btn-xs">Delete</a>
                        </div>
                    </div>
                `;
                $('.custom-comments-section').append(comment_html);
            }
        };

        // Add a button to the sidebar to add a new comment
        var add_comment_button_html = `
            <div class="add-comment-button">
                <button class="btn btn-primary btn-sm btn-add-comment">
                    <i class="octicon octicon-plus"></i> Add Comment
                </button>
            </div>
        `;
        $(add_comment_button_html).insertBefore('.custom-comments-section');

        // Event handler for adding a new comment
        $(document).on('click', '.btn-add-comment', function() {
            frappe.prompt(
                [
                    {
                        fieldtype: 'Small Text',
                        fieldname: 'comment',
                        label: 'Comment',
                        reqd: 1
                    }
                ],
                function(values) {
                    // Manually add or update the comment in the sidebar
                    frappe.call({
                        method: "frappe.desk.form.utils.add_comment",
                        args: {
                            reference_doctype: frm.doc.doctype,
                            reference_name: frm.doc.name,
                            content: values.comment,
                            comment_email: frappe.session.user,
                            comment_by: frappe.session.user_fullname
                        },
                        callback: function(r) {
                            if (!r.exc) {
                                // Update the comments section in the sidebar
                                add_or_update_comment_to_sidebar(r.message.name, values.comment, frappe.session.user_fullname);
                                frappe.show_alert({
                                    message: __('Comment added successfully'),
                                    indicator: 'green'
                                });
                            }
                        }
                    });
                },
                __('Add a Comment'),
                __('Submit')
            );
        });

        // Load existing comments into the custom sidebar section
        frappe.call({
            method: "frappe.client.get_list",
            args: {
                doctype: "Comment",
                fields: ["name", "content", "comment_by"],
                filters: {
                    reference_doctype: frm.doc.doctype,
                    reference_name: frm.doc.name
                },
                order_by: "creation asc"
            },
            callback: function(r) {
                if (r.message) {
                    r.message.forEach(function(comment) {
                        add_or_update_comment_to_sidebar(comment.name, comment.content, comment.comment_by);
                    });
                }
            }
        });

        // Event handler for editing a comment
        $(document).on('click', '.edit-comment', function(e) {
            e.preventDefault();
            var $comment_div = $(this).closest('.comment');
            var comment_id = $comment_div.data('comment-id');
            var comment_text = $comment_div.find('.comment-text').text();

            frappe.prompt(
                [
                    {
                        fieldtype: 'Small Text',
                        fieldname: 'comment',
                        label: 'Comment',
                        default: comment_text,
                        reqd: 1
                    }
                ],
                function(values) {
                    frappe.call({
                        method: "frappe.client.set_value",
                        args: {
                            doctype: "Comment",
                            name: comment_id,
                            fieldname: "content",
                            value: values.comment
                        },
                        callback: function(r) {
                            if (!r.exc) {
                                $comment_div.find('.comment-text').text(values.comment);
                                frappe.show_alert({
                                    message: __('Comment updated successfully'),
                                    indicator: 'green'
                                });
                            }
                        }
                    });
                },
                __('Edit Comment'),
                __('Submit')
            );
        });

        // Event handler for deleting a comment
        $(document).on('click', '.delete-comment', function(e) {
            e.preventDefault();
            var $comment_div = $(this).closest('.comment');
            var comment_id = $comment_div.data('comment-id');

            frappe.confirm(__('Are you sure you want to delete this comment?'), function() {
                frappe.call({
                    method: "frappe.client.delete",
                    args: {
                        doctype: "Comment",
                        name: comment_id
                    },
                    callback: function(r) {
                        if (!r.exc) {
                            $comment_div.remove();
                            frappe.show_alert({
                                message: __('Comment deleted successfully'),
                                indicator: 'red'
                            });
                        }
                    }
                });
            });
        });
    }
});

Hi @4nimzz,

Please apply the code:

frappe.ui.form.on('Purchase Order', {
    refresh: function(frm) {
        // Function to get the first name from full name
        const getFirstName = fullName => fullName.split(' ')[0];

        // Ensure the custom comments section exists in the sidebar
        if (!$('.custom-comments-section').length) {
            $('<div class="custom-comments-section"></div>').appendTo('.form-sidebar');
        }

        // Remove duplicate add comment buttons
        $('.add-comment-button').remove();

        // Add a button to the sidebar to add a new comment
        const add_comment_button_html = `
            <div class="add-comment-button">
                <button class="btn btn-primary btn-sm btn-add-comment">
                    <i class="octicon octicon-plus"></i> Add Comment
                </button>
            </div><br>
        `;
        $(add_comment_button_html).insertBefore('.custom-comments-section');

        // Function to add or update comment in custom sidebar section
        const addOrUpdateComment = (comment_id, comment, user_fullname) => {
            const first_name = getFirstName(user_fullname);
            let $comment = $(`.comment[data-comment-id="${comment_id}"]`);
            
            if ($comment.length) {
                $comment.find('.comment-text').html(comment); // Preserve HTML formatting
                $comment.find('.comment-by').text(first_name);
            } else {
                const comment_html = `
                    <div class="comment" data-comment-id="${comment_id}">
                        <div class="comment-body">
                            <b class="comment-by">${first_name}</b>: <span class="comment-text">${comment}</span>
                        </div>
                        <div class="comment-actions">
                            <a href="#" class="edit-comment btn btn-link btn-xs"><i class="fa fa-edit"></i></a>
                            <a href="#" class="delete-comment btn btn-link btn-xs"><i class="fa fa-trash-o"></i></a>
                        </div>
                    </div>
                `;
                $('.custom-comments-section').append(comment_html);
            }
        };

        // Event handler for adding a new comment
        $(document).off('click', '.btn-add-comment').on('click', '.btn-add-comment', function() {
            frappe.prompt(
                [{ fieldtype: 'Text Editor', fieldname: 'comment', label: 'Comment', reqd: 1 }],
                values => {
                    frappe.call({
                        method: "frappe.desk.form.utils.add_comment",
                        args: {
                            reference_doctype: frm.doc.doctype,
                            reference_name: frm.doc.name,
                            content: values.comment,
                            comment_email: frappe.session.user,
                            comment_by: frappe.session.user_fullname
                        },
                        callback: r => {
                            if (!r.exc) {
                                addOrUpdateComment(r.message.name, values.comment, frappe.session.user_fullname);
                                frappe.show_alert({ message: __('Comment added successfully'), indicator: 'green' });
                                frm.reload_doc(); // Refresh the form after adding a comment
                            }
                        }
                    });
                },
                __('Add a Comment'),
                __('Submit')
            );
        });

        // Load existing comments into the custom sidebar section
        const loadComments = () => {
            $('.custom-comments-section').empty(); // Clear existing comments
            frappe.call({
                method: "frappe.client.get_list",
                args: {
                    doctype: "Comment",
                    fields: ["name", "content", "comment_by"],
                    filters: {
                        reference_doctype: frm.doc.doctype,
                        reference_name: frm.doc.name
                    },
                    order_by: "creation asc"
                },
                callback: r => {
                    if (r.message) {
                        r.message.forEach(comment => {
                            addOrUpdateComment(comment.name, comment.content, comment.comment_by);
                        });
                    }
                }
            });
        };

        // Initial load of comments
        loadComments();

        // Event handler for editing a comment
        $(document).off('click', '.edit-comment').on('click', '.edit-comment', function(e) {
            e.preventDefault();
            const $comment_div = $(this).closest('.comment');
            const comment_id = $comment_div.data('comment-id');
            const comment_text = $comment_div.find('.comment-text').html(); // Get HTML content

            frappe.prompt(
                [{ fieldtype: 'Text Editor', fieldname: 'comment', label: 'Comment', default: comment_text, reqd: 1 }],
                values => {
                    frappe.call({
                        method: "frappe.client.set_value",
                        args: {
                            doctype: "Comment",
                            name: comment_id,
                            fieldname: "content",
                            value: values.comment
                        },
                        callback: r => {
                            if (!r.exc) {
                                $comment_div.find('.comment-text').html(values.comment); // Preserve HTML formatting
                                frappe.show_alert({ message: __('Comment updated successfully'), indicator: 'green' });
                            }
                        }
                    });
                },
                __('Edit Comment'),
                __('Submit')
            );
        });

        // Event handler for deleting a comment
        $(document).off('click', '.delete-comment').on('click', '.delete-comment', function(e) {
            e.preventDefault();
            const $comment_div = $(this).closest('.comment');
            const comment_id = $comment_div.data('comment-id');

            frappe.confirm(__('Are you sure you want to delete this comment?'), () => {
                frappe.call({
                    method: "frappe.client.delete",
                    args: { doctype: "Comment", name: comment_id },
                    callback: r => {
                        if (!r.exc) {
                            $comment_div.remove();
                            frappe.show_alert({ message: __('Comment deleted successfully'), indicator: 'red' });
                        }
                    }
                });
            });
        });
    }
});

Output:

5 Likes

Thank you so much, @NCP!