Serenity: Action buttons in TypeScript - Purpose: Batch Approve or Deny a Row and Clone data to secondary table

Created on 11 Jul 2016  Â·  5Comments  Â·  Source: serenity-is/Serenity

Has anyone put together a sample for inline action buttons in TypeScript? The guide has yet to be updated and I'm wanting to add a "Approve" and "Deny" action buttons.

I've yet to get too acquainted with TypeScript so I figured I'd reach out first before getting too deep into this. I was thinking about creating a DataTransferHelper much like the ExcelExportHelper, that would just deal with transferring data between tables in batch. (I'm looking into the Bulk Action sample as I write this)

I'm attempting to create a Approval flow that has 2 tables that are exactly the same except that one table is only accessible to the admin and requires the admin to approve a entry on another table, for an external user to be able to enter data into the Admin table.

The Admin goes to the StandardUser table and "Approves" an entry by clicking the inline button and thus the data is cloned from the current row to a new row in the Admin table. It then uses IsActiveDelete to hide the row in the StandardUser Table.

If the Admin "Denies" the entry, the entry has a field "Status" that gets changed to "Denied" in the StandardUser Table and then, using conditional formatting, we identify it with a differentiated color until the item is edited and approved.

Most helpful comment

@sayuga
I have added the inline buttons for approved and reject functionality but i have did it using Saltaralle as i did not like the typescript ( I am trying to lean typescript as serenity needs it but still i prefer to use Saltaralle instead of typescript :-)).
Don't know how much it will be helpful for you but here is some insight for your reference -
Screen Shot
inlineapproval

Code -

protected override List<SlickColumn> GetColumns()
        {
            var columns = base.GetColumns();
            //Only add Approve and Reject buttons if has Approval Permissions
            if (Authorization.HasPermission(VoucherApprovalPermissionKey))
            {
                columns.Add(new SlickColumn
                {
                    Field = "ApproveVoucher",
                    Title = "",
                    Format = ctx =>
                    {
                        var item = (VouchersRow)ctx.Item;
                        if (item.Status.Value == ApprovalStatus.Submitted && item.VoucherDebitAmount.Value == item.VoucherCreditAmount.Value)
                            return "<a class='slickRowbtn green ApproveVoucher' href='#'><span>✓</span>Approve</a>";
                        return item.StatusByName;
                    },
                    Width = 102,
                    MinWidth = 102,
                    MaxWidth = 102
                });
                columns.Add(new SlickColumn
                {
                    Field = "RejectVOucher",
                    Title = "",
                    Format = ctx =>
                    {
                        var item = (VouchersRow)ctx.Item;
                        if (item.Status.Value == ApprovalStatus.Submitted && item.VoucherDebitAmount.Value == item.VoucherCreditAmount.Value)
                            return "<a class='slickRowbtn red RejectVoucher' href='#'><span>X</span>Reject</a>";
                        return "";

                    },
                    Width = 87,
                    MinWidth = 87,
                    MaxWidth = 87
                });
            }
            //columns.Insert(0, GridRowSelectionMixin.CreateSelectColumn(() => rowSelection));
            return columns;
        }
        protected override void OnClick(jQueryEvent e, int row, int cell)
        {
            base.OnClick(e, row, cell);

            if (e.IsDefaultPrevented())
                return;

            var currentRow = Items[row];
            // *** check if the clicked "button" is of type fa (because the buttons are of font awesome type)
            if (J(e.Target).HasClass("slickRowbtn"))
            {
                e.PreventDefault();
                var EntityId = currentRow.Id.Value;
                var status = ApprovalStatus.None;
                if (J(e.Target).HasClass("ApproveVoucher"))
                    status = ApprovalStatus.Approved;
                if (J(e.Target).HasClass("RejectVoucher"))
                    status = ApprovalStatus.Rejected;
                if (status != ApprovalStatus.None)
                {
                    var SR2 = new SaveRequest<VouchersRow>
                    {
                        Entity = new VouchersRow
                        {
                            Id = currentRow.Id,
                            Status = status
                        }
                    };
                    VouchersService.Update(SR2, onSuccess: (saveresponse) => { Refresh(); });
                }
            }
        }

All 5 comments

@sayuga
I have added the inline buttons for approved and reject functionality but i have did it using Saltaralle as i did not like the typescript ( I am trying to lean typescript as serenity needs it but still i prefer to use Saltaralle instead of typescript :-)).
Don't know how much it will be helpful for you but here is some insight for your reference -
Screen Shot
inlineapproval

Code -

protected override List<SlickColumn> GetColumns()
        {
            var columns = base.GetColumns();
            //Only add Approve and Reject buttons if has Approval Permissions
            if (Authorization.HasPermission(VoucherApprovalPermissionKey))
            {
                columns.Add(new SlickColumn
                {
                    Field = "ApproveVoucher",
                    Title = "",
                    Format = ctx =>
                    {
                        var item = (VouchersRow)ctx.Item;
                        if (item.Status.Value == ApprovalStatus.Submitted && item.VoucherDebitAmount.Value == item.VoucherCreditAmount.Value)
                            return "<a class='slickRowbtn green ApproveVoucher' href='#'><span>✓</span>Approve</a>";
                        return item.StatusByName;
                    },
                    Width = 102,
                    MinWidth = 102,
                    MaxWidth = 102
                });
                columns.Add(new SlickColumn
                {
                    Field = "RejectVOucher",
                    Title = "",
                    Format = ctx =>
                    {
                        var item = (VouchersRow)ctx.Item;
                        if (item.Status.Value == ApprovalStatus.Submitted && item.VoucherDebitAmount.Value == item.VoucherCreditAmount.Value)
                            return "<a class='slickRowbtn red RejectVoucher' href='#'><span>X</span>Reject</a>";
                        return "";

                    },
                    Width = 87,
                    MinWidth = 87,
                    MaxWidth = 87
                });
            }
            //columns.Insert(0, GridRowSelectionMixin.CreateSelectColumn(() => rowSelection));
            return columns;
        }
        protected override void OnClick(jQueryEvent e, int row, int cell)
        {
            base.OnClick(e, row, cell);

            if (e.IsDefaultPrevented())
                return;

            var currentRow = Items[row];
            // *** check if the clicked "button" is of type fa (because the buttons are of font awesome type)
            if (J(e.Target).HasClass("slickRowbtn"))
            {
                e.PreventDefault();
                var EntityId = currentRow.Id.Value;
                var status = ApprovalStatus.None;
                if (J(e.Target).HasClass("ApproveVoucher"))
                    status = ApprovalStatus.Approved;
                if (J(e.Target).HasClass("RejectVoucher"))
                    status = ApprovalStatus.Rejected;
                if (status != ApprovalStatus.None)
                {
                    var SR2 = new SaveRequest<VouchersRow>
                    {
                        Entity = new VouchersRow
                        {
                            Id = currentRow.Id,
                            Status = status
                        }
                    };
                    VouchersService.Update(SR2, onSuccess: (saveresponse) => { Refresh(); });
                }
            }
        }

@Ramveer

No worries. I have most of my coding in TypeScript because of various functionalities I'm leveraging but this give me a well placed starting point. Thank you. I'll play around with it and adapt. That is part of the fun after all. ^_^

I'm at a roadblock as to how to best call for the data selected on one table and then adding it as a new entry on another table without needing to call on the dialog for the other table. I've been attempting a number of things but getting nowhere.

Though I appreciate the help from @Ramveer, I was unable to successfully do what I want with in-line buttons thus I opted for the CancellableBulkAction sample. I managed to get the check boxes and the toolbar buttons just how I want them but I cant get the table transfer from one to the other.

I want to do this:

Select using check boxes then click button to affect each selected row:

-Approve Button:

  1. clones fields from TempMatrix (Table1) to MasterMatrix(Table2) for each checked row.
  2. Sets Field.Status on TempMatrix to 4 (Enumerated field 4 = Approved)
  3. Sets Field.IsActive on TempMatrix to 0.
  4. Deny Button:
  5. Sets Field.Status on TempMatrix to 5 (Enumerated field 5 = Denied)

So far I've done the following:

I took the CancellableBulkAction sample and recreated the files such that I could have two batch services (one for each Button), using the best of my understanding on how the code was meant to work. Here is how it looks so far:

I create a BatchFile.cs with:

namespace FMCDashboard.Matrix.Endpoints
{
using Serenity.Data;
using Serenity.Services;
using System.Collections.Generic;
using System.Web.Mvc;

public class ApprovalBulkActionRequest : ServiceRequest
{
    public List<int> TempMatrixIDs { get; set; }
}

[ServiceAuthorize, RoutePrefix("Services/Matrix/TempMatrix"), Route("{action}")]
[ConnectionKey("Default")]
public class TempMatrixBatchApprovalController : ServiceEndpoint
{

    public ServiceResponse ApprovalBulkAction(IUnitOfWork uow, ApprovalBulkActionRequest request)
    {
        request.CheckNotNull();

        foreach (var x in request.TempMatrixIDs)    //Do Something//
        {
        //Attempt 2 - Failure
            //using myRow = Entities.MasterMatrixRow;
            //using myOtherRow = Entities.TempMatrixRow;
            //...//

            //var fldone = myRow.Fields;
            //var fldtwo = myOtherRow.Fields;

            //fldone.FmTagNum = fldtwo.FmTagNum;
            //fldtwo.Status = Filters.MatrixStatus.Approved;
            //fldtwo.IsActive = 0;                                             


            //Attempt 1 - Failure

            //    var tempD = request.TempMatrixIDs[x];
            //    var nwRow = new Forms.MasterMatrixForm();

            //    nwRow.FmTagNum= tempD.FmTagNum;
            //    nwRow.Status = Filters.MatrixStatus.Added;
            //    tempD.Status = Filters.MatrixStatus.Approved;
            //    tempD.IsActive = 0;            
            //
        };            return new ServiceResponse();
    }
}

}

In a BatchFile.ts I had the following:

namespace FMCDashboard.Matrix {

export interface ApprovalBulkActionRequest extends Serenity.ServiceRequest {
    TempMatrixIDs?: number[]
}
export interface DenyBulkActionRequest extends Serenity.ServiceRequest {
    TempMatrixIDs?: number[]
}

export namespace BulkActionService {
    export const baseUrl = '/Matrix/TempMatrix';

    export declare function ApprovalBulkAction(request: ApprovalBulkActionRequest, onSuccess?: (response: Serenity.ServiceResponse) => void, opt?: Q.ServiceOptions<any>): JQueryXHR;
    export declare function DenyBulkAction(request: DenyBulkActionRequest, onSuccess?: (response: Serenity.ServiceResponse) => void, opt?: Q.ServiceOptions<any>): JQueryXHR;


    export namespace Methods {        
        export declare const ApprovalBulkAction: string;
        export declare const DenyBulkAction: string;

    }

    ['ApprovalBulkAction', 'DenyBulkAction'].forEach(x => {
        (<any>BulkActionService)[x] = function (r, s, o) { return Q.serviceRequest(baseUrl + '/' + x, r, s, o); };
        (<any>Methods)[x] = baseUrl + '/' + x;
    });
}    

}

With that in place I went ahead and added the following to the TempMatrixGrid.ts

//.......//

buttons.push(
{
title: 'Approve Items',
cssClass: 'approve-button',
onClick: () =>
{
if (!this.onViewSubmit()) { return; }

                    var action = new ApprovalBulkAction();
                    action.done = () => this.rowSelection.resetCheckedAndRefresh();
                    action.execute(this.rowSelection.getSelectedKeys());                        
                }
            });
        buttons.push(
            {
                title: 'Deny Items',
                cssClass: 'reject',
                onClick: () =>
                {
                    if (!this.onViewSubmit()) { return; }

                    var action = new DenyBulkAction();
                    action.done = () => this.rowSelection.resetCheckedAndRefresh();
                    action.execute(this.rowSelection.getSelectedKeys());


                }
            });

Any help on this is appreciated. Thank you.

For approve, reject, don't need bulk action, just write a method in your endpoint (MyEndpoint) and call it with MyService.Approve(), MyService.Reject()

Hello i want to add the option of approve and reject in order form how can do please help

Was this page helpful?
0 / 5 - 0 ratings

Related issues

moostafaa picture moostafaa  Â·  3Comments

dudeman972 picture dudeman972  Â·  3Comments

Pinellus picture Pinellus  Â·  3Comments

stepankurdylo picture stepankurdylo  Â·  3Comments

ga5tan picture ga5tan  Â·  3Comments