Velodata Logo

VELODATA

FIND US

Address
Level 19,  50 Cavill Ave
Surfers Paradise,  Qld. Aus

Hours
Monday-Friday:  9AM – 5:30PM
Saturday:  11AM – 3PM

 

Building a Business Application in WordPress

WordPress is a terrific ecosystem for website development.  If you know how to do it,  you can use WordPress to build an incredibly powerful business application.  Let’s take a look at some demonstrations,  and then we’ll show you how it’s done.  Please Note:  Additional information regarding this tutorial can be found at the bottom of this screen.  Ideally,  your screen width should be at least 1024 pixels.

Cellular One New Connections
Contact Centre

Sorry,  but a Wide Screen is required to look at this tutorial. There are lots of screenshots showing programming code.

UNDERSTANDING THE BASICS

In the examples shown above,  we’re using DataTables.js,  Backbone.js and React.js to produce the “client end” of a super fast business database system.  And it’s not a small database either.  There are millions of records in our demonstration database,  and it’s real data too,  from a company valued at $1.3BN back in the day. For example, the Customer Care module has 179,000 customers in it.  Each customer has a Handset History,  a Payment History,  a Network Event History,  and an Internal Notes History.

More and more businesses are using WordPress as their company website framework because it can do so much more than merely writing personal blog articles.  When users of your software can seamlessly move between WordPress and your business applications the possibilites are endless,  limited only by your imagination.  

Isn’t that neat?  With just one click you’re able to move from WordPress into a “software modal” which contains a powerful Customer User Interface.  And after you close the modal,  here you are back at a WordPress Page written in Elementor Pro.  Indeed,  you might not have noticed it yet,  but all of this is happening in a true Single Page Application.  

HOW THIS TUTORIAL IS LAID OUT

Like most software applications which work over the internet,  this software demonstration uses PHP as the primary data extraction service,  and it uses Javascript Libraries in the client front end.  The following chapters explain it all.

Building a Business Application in WordPress

Here in Step Two of the Tutorial we’re going to introduce you to our demo Customer Interface and how to build it.  We’re also going to look at the Javascript functions which (a) extract the relevant JSON data and (b) which convert raw HTML into powerful Javascript Objects.  

SO…  LET’S MOVE ON TO THE TUTORIAL

Step Two  –  Building A Customer Interface.

Before we continue,  there’s something worth remembering about computer programming,  and it’s this   –  when it comes to designing professional business applications,  sooner or later the software actually has to DO what a business requires,  rather than giving priority to appearance over function.  

So why is this important?  Well,  having worked as a Systems Analyst for many decades,  I can testify first hand to a certain syndrome.  All too often,  during the design phase I see computer programmers spending way too much time on making software “look pretty” instead of making software “look fast and functional”.  

You might appreciate now why I’m using 24 year old data in these software examples  –  albeit huge amounts of data from a real company which was valued at $1.3 Billion AUD back in the day.  I’m doing this to demonstrate function does indeed take priority over appearance.  After all,  the data itself hasn’t changed.  In reality,  what HAS changed over the last 24 years is the way we present data to users  –  in particular,  over the internet.

Step Two  –  Building a Customer Interface.

In the Introduction we provided some demonstrations which contained a mixture of Data Tables and drill down Search Functions which ultimately lead to individual business interfaces.  During the next few parts of this tutorial we’re going to focus on one single Customer User Interface,  and how to build it.  Please Note:  I’m not saying that you have to make YOUR future Business Applications look (or function) the same way.  

In regards to THIS demonstration,  the software is divided into a true client server paradigm.  100% of the formatting and appearance takes place in the browser  (aka the client).  The client software is server agnostic.  The client software doesn’t care what brand of SQL engine you’re using,  or what brand of webserver you’re using.  All the client software cares about is “please give me the data in JSON format…”

This is probably a good time to familiarise yourself with our demo Client User Interface. You can do that by clicking here.  The data is real by the way – the customer became delinquent with their bill payments and they were sent to a Credit Collections Agency.  Poke around inside the User Interface  –  in particular,  try clicking on the various drill down functions.

Let’s have a look at the Javascript function which gets called when you click on the “href” element in the previous paragraph.  The programming code is shown below.  The URL which calls for the data can be seen at Line 12.  And yes,  all of this programming code is currently loaded in your browser and it’s active as you read this.  

In the following code snippet,  you’ll note of a function at Line 74 called F0_Build_Payment_History_Table.  In the next section we’re going to inspect how the data which belongs to that sub function is created,  and then after the JSON date is returned to your browser,  how it then gets converted into a great looking Javascript Object.

				
					    function F0_Load_Single_Account(lnCustno) {
        $("#iosLoading").click();
        object_ARCUST_Model = Backbone.Model.extend({
            initialize: function () {
                console.log("Backbone::Creating object_ARCUST");
            }
        });
        object_ARCUST = new object_ARCUST_Model({
            urlRoot: "model/Update_object_ARCUST.php"
        });
        $.ajax({
            url: "/model/Retrieve_Customer_Data.php",
            type: "POST",
            data: {lcAction: 'AR_CUSTOMER_SINGLE_RECORD', lnCustno: lnCustno},
            dataType: "json",
            success: function (msg) {
                object_ARCUST_Backup_MSG = msg;
                object_ARCUST.set('custno', msg.data[0]['custno']);
                object_ARCUST.set('cc_stage', msg.data[0]['cc_stage']);
                object_ARCUST.set('stage_date', msg.data[0]['stage_date']);
                object_ARCUST.set('name', msg.data[0]['name']);
                object_ARCUST.set('address', msg.data[0]['address']);
                object_ARCUST.set('postcode', msg.data[0]['postcode']);
                object_ARCUST.set('entry_date', msg.data[0]['entry_date']);
                object_ARCUST.set('inv_run', msg.data[0]['inv_run']);
                object_ARCUST.set('balance', msg.data[0]['balance']);
                object_ARCUST.set('overdue', msg.data[0]['overdue']);
                object_ARCUST.set('prev_cust', msg.data[0]['prev_cust']);
                object_ARCUST.set('id_no', msg.data[0]['id_no']);
                object_ARCUST.set('section', msg.data[0]['section']);
                object_ARCUST.set('cus_type', msg.data[0]['cus_type']);
                object_ARCUST.set('suppress', msg.data[0]['suppress']);
                object_ARCUST.set('cust_type', msg.data[0]['cust_type']);
                object_ARCUST.set('debt_coll', msg.data[0]['debt_coll']);
                object_ARCUST.set('collection_house', msg.data[0]['collection_house']);
                object_ARCUST.set('solicitor', msg.data[0]['solicitor']);
                object_ARCUST.set('telephone', msg.data[0]['telephone']);
                object_ARCUST.set('facsimile', msg.data[0]['facsimile']);
                object_ARCUST.set('sup_mobile', msg.data[0]['sup_mobile']);
                object_ARCUST.set('date_suppr', msg.data[0]['date_suppr']);
                object_ARCUST.set('inv_freq', msg.data[0]['inv_freq']);
                object_ARCUST.set('card_type', msg.data[0]['card_type']);
                object_ARCUST.set('card_name', msg.data[0]['card_name']);
                object_ARCUST.set('exp_date', msg.data[0]['exp_date']);
                object_ARCUST.set('amex', msg.data[0]['amex']);
                object_ARCUST.set('dd_type', msg.data[0]['dd_type']);
                object_ARCUST.set('pay_method', msg.data[0]['pay_method']);
                object_ARCUST.set('current', msg.data[0]['current']);
                object_ARCUST.set('over30', msg.data[0]['over30']);
                object_ARCUST.set('over60', msg.data[0]['over60']);
                object_ARCUST.set('over90', msg.data[0]['over90']);
                object_ARCUST.set('over120', msg.data[0]['over120']);
                object_ARCUST.set('over150', msg.data[0]['over150']);
                console.log("OK,  received the data for custno = " + object_ARCUST.attributes.name);
                
                object_ARCUST.set('address1', object_ARCUST.attributes.address.substr(00, 30) );
                object_ARCUST.set('address2', object_ARCUST.attributes.address.substr(30, 30) );
                object_ARCUST.set('address3', object_ARCUST.attributes.address.substr(60, 30) );
                object_ARCUST.set('address4', object_ARCUST.attributes.address.substr(90, 30) );
                
                
                $('#modal_Show_Single_Customer').empty();
                $('#modal_Show_Single_Customer').css('display', 'block');
                $('#modal_Show_Single_Customer').modal('show');
                var template = Handlebars.compile( $('#template_ARCUST_HEADER').html() );
                var html = template(object_ARCUST.toJSON());
                $('#modal_Show_Single_Customer').html(html);                
                
                
                $('#Container_For_ARCUST_BODY').html( $('#template_ARCUST_BODY').html() );        //  ID #Container_For_ARCUST_Body is ALSO found in Template_ARCUST_HEADER.php
                CCF0_Show_Data_Entry();                            //  F0_Show_Data_Entry is found in Template_ARNEWJOB_BODY.php
                F0_Ajax_Client_History_Data(object_ARCUST.attributes.custno);
                F0_Build_Network_History_Table(object_ARCUST.attributes.custno);
                F0_Build_Payment_History_Table(object_ARCUST.attributes.custno);
                F0_Build_Ageing_History_Table(object_ARCUST);
                F0_Build_IMEI_History_Table(object_ARCUST.attributes.custno);

                
            },
            error: function (msg) {
                alert(msg);
            }
        });
    };
    
    
    
    

				
			

When it comes to commercial Business Applications,  one of the best Javascript libraries out there is DataTables.js  –  it’s an excellent way to list multiple records from a database in a highly readable format.  It’s especially useful when you’re dealing with LARGE databases and a user needs to employ Search functions.  You can tune Datatables.js to look any way you want,  and just like React.js you can tune it to respond to Events.  It really is very powerful.

Let’s focus on the Payment History a little bit more.  In our demo Customer User Interface,  the data contained in the Payment History “sub page” is created by a Javascript function named F0_Build_Payment_History_Table. In the next code snippt (shown below) you can see the ajax URL which calls for the JSON data on Line 18.

Keen eyes will note a reference on Line 4 to a library function named dataTable().  This is where DataTables.js does it’s magic.  Like React.js,  DataTables.js works by building a Javascript Object complete with it’s own amazing HTML.

				
					function F0_Build_Payment_History_Table(lnCustno) {
    var IJV_WhereString = "";
    var IJV_OrderString = "";
    $('#Table_Payment_History').dataTable({
        // original with filter box,  "dom": '<"top"lBfp><"bottom"rti><"clear">', 
        "dom": '<"top"><"bottom"rti><"clear">',
        "colReorder": true,
        "pageLength": 500,
        "displayStart": 0,
        "ordering": true,
        "paging": true,
        "processing": true,
        "serverSide": false,
        "responsive": false,
        "autowidth": false,
        "order": [[2, 'desc']],
        "ajax": {
            "url": "/model/Retrieve_Payment_History.php",
            "type": "POST",
            data: {lcAction: 'AR_PAYMENTS', lnCustno: lnCustno, IJV_WhereString: IJV_WhereString, IJV_OrderString: IJV_OrderString}
        },
        select: {
            style: 'os',
            items: 'cell'
        },
        columns: [
            {data: 'custno', "visible": true, orderable: false }, 
            {data: 'invno', "visible": true, orderable: false }, 
            {data: 'invdate', "visible": true, orderable: true,
                "render": function (data, type, row)
                {
                    var lcFormatString = '';
                    lcFormatString = lcFormatString + 
                        '<span style="white-space: nowrap; color: #000000; font-weight: normal" >&nbsp;' + row['invdate'] + '</span>' ;
                    return lcFormatString;
                }
            }, 
            {data: 'reference', "visible": true, orderable: false, 
                "render": function (data, type, row)
                {
                    return  '<span style="white-space: nowrap; color: #000000; font-weight: normal" >&nbsp;' + row['reference'] + '</span>' ;
                }
            }, 
            {data: 'type',    width: '10%', "visible": true, orderable: false }, 
            {data: 'paidamt', width: '10%', className: 'IJV_Minimum_150px', visible: true, orderable: false,
                "render": function (data, type, row)
                {
                    if ( row["amount"] < 0.00 ) {
                        return row['paidamt'] ;
                    } else {
                        return '';
                    }
                }
            }, 
            {data: 'amount',  width: '10%', className: 'IJV_Minimum_150px', visible: true, orderable: true,
                "render": function (data, type, row)
                {
                    if ( row["amount"] >= 0.00 ) {
                        return '<span style="white-space: nowrap; color: darkred; font-weight: bold" >&nbsp;' + row['amount'] + '</span>' ;
                    } else {
                        return '' ;
                    }
                }
            }, 
            {data: 'arrears', width: '10%', className: 'IJV_Minimum_150px', visible: true, orderable: false,
                "render": function (data, type, row)
                {
                    if ( row["arrears"] > 0.00 ) {
                        return '<span style="white-space: nowrap; color: darkred; font-weight: bold" >&nbsp;' + row['arrears'] + '</span>' ;
                    } else {
                        return '' ;
                    }
                }
            }, 
            {data: 'balance', width: '10%', className: 'IJV_Minimum_150px', visible: true, orderable: false }
        ],
        "createdRow": function (row, data, index) {
            if( data["amount"] < 0 ){
               $('td', row).eq(5).css("background-color", "#daf7d7");
               $('td', row).eq(5).css("color", "#005500");
               $('td', row).eq(5).css("font-weight", "700");
               $('td', row).eq(5).css("text-align", "center");
            }
            if( data["amount"] > 0 ){
               $('td', row).eq(6).css("color", "darkred"); 
               $('td', row).eq(6).css("background-color", "#ffe2e2");
               $('td', row).eq(6).css("font-weight", "700");
               $('td', row).eq(6).css("text-align", "center");
            }
            if(data["arrears"] > 0 ){
               $('td', row).eq(7).css("color", "darkred"); 
               $('td', row).eq(7).css("background-color", "#ffe2e2");
               $('td', row).eq(7).css("font-weight", "700");
               $('td', row).eq(7).css("text-align", "center");
            }
               $('td', row).eq(8).css("font-weight", "700");
               $('td', row).eq(8).css("padding-right", "15px");
               $('td', row).eq(8).css("text-align", "right");
        }



    });
//        var otable_Payment_History = $('#Table_Payment_History').DataTable();
//        otable_Payment_History.colReorder.order( [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ], true );

}


				
			

In Line 4 of the above code snippet,  you’ll note a jquery command which refers to an element named #Table_Payment_History.  Where does this DOM element exist?  What does it look like?  In the next code snippet we’re going to look at the raw HTML which contains the Table Structure for that element.

Again,  I’d like to stress all of this program code is currently loaded and active in your web browser as you read this.

Seen below is the raw HTML which acts as the foundation structure of our demo Customer User Interface..  When F0_Build_Payment_History_Table gets called,  it converts the DOM element named #Table_Payment_History into a living breathing Javascript object thanks to DataTable.js.  Contained below (at Line 136) is where that Table lives before it gets converted by the DataTables library.   What we’re looking at here is a classic Script Template!

				
					<?php echo '<script type="text/x-handlebars-template" id="template_ARCUST_BODY">' ?>


    <div class="col-xs-12">
        <ul class="nav nav-tabs ijv-sunken-box" style='margin-bottom: 8px; margin-top: 5px; padding-bottom: 5px; '>
            <li class="active" id="menuTag01" ><a data-toggle="tab" href="#menuTab_User_Data"         onclick='CCF0_Show_Data_Entry()'            ><span ><i class="img sp_7w8upAcl-hQ sx_c81051"></i></span>&nbsp;&nbsp;User Data</a></li>
            <li class="" id="menuTag02"       ><a data-toggle="tab" href="#menuTab_IMEI_History"      onclick='CCF0_Show_IMEI_History()'          ><span ><i class="img sp_7w8upAcl-hQ sx_8b6b00"></i></span>&nbsp;&nbsp;IMEI History</a></li>
            <li class="" id="menuTag03"       ><a data-toggle="tab" href="#menuTab_Payment_History"   onclick='CCF0_Show_Payment_History()'       ><span ><i class="img sp_7w8upAcl-hQ sx_55a1f4"></i></span>&nbsp;&nbsp;Payment History</a></li>
            <li class="" id="menuTag06"       ><a data-toggle="tab" href="#menuTab_Network_History"   onclick='CCF0_Show_Network_History_Tab()'   ><span ><i class="img sp_7w8upAcl-hQ sx_a615fc"></i></span>&nbsp;&nbsp;Network History</a></li>
            <li class="" id="menuTag08"       ><a data-toggle="tab" href="#menuTab_Client_History"    onclick='CCF0_Show_Client_History_Tab()'    ><span ><i class="glyphicon glyphicon-stats ijv-darkred""></i></span>&nbsp;&nbsp;Client History</a></li>
        </ul>
        <div class="col-sm-12 ijv-sunken-box ijv-small-padding" id="" style='background-color: #FFFFFF'>
            <table id="Table_Ageing_History" class="display table " cellspacing="0" width="100%" >
                <thead>
                    <tr>
                        <th>Current</th>
                        <th>Over 30</th>
                        <th>Over 60</th>
                        <th>Over 90</th>
                        <th>Over120</th>
                        <th>Over150</th>
                        <th>Total</th>
                    </tr>
                </thead>
            </table>
        </div>                    
    </div>


    <div class="col-xs-12">
        <div class="tab-content" style='min-height: 550px;'>

            <div id="menuTab_User_Data" class="tab-pane fade in active" >
                <div class='XXrow'>

                    <div class="col-sm-6 ijv-small-padding " id="" style=''>
                        <div class='ijv-sunken-box hidden-xs' id="container_Show_Data_Upper_Left" style=''>
                        </div>
                        <div class='ijv-sunken-box' id="container_Show_Data_Middle_Left" style=''>
                        </div>
                    </div>
                    <div class="col-sm-6 ijv-small-padding " id="" style=''>
                        <div class='ijv-sunken-box' id="container_Data_Entry_Upper_Right" style=''>
                        </div>                    
                        <div class='ijv-sunken-box' id="container_Data_Entry_Middle_Right" style=''>
                        </div>                    
                        <div class='ijv-sunken-box' id="container_Show_Data_Lower_Right" style=''>
                        </div>                    
                    </div>
                </div>
            </div> 

            <div id="menuTab_Hardware_Data" class="tab-pane fade" >
                <div class='XXrow'>
                    <div class="col-sm-6 ijv-small-padding " id="" style=''>
                        <div class='ijv-sunken-box' id="container_Show_Hardware_Left_Upper" style=''>
                        </div>
                    </div>
                    <div class="col-sm-6 ijv-small-padding " id="" style=''>
                        <div class='ijv-sunken-box' id="container_Show_Hardware_Right_Upper" style=''>
                        </div>
                    </div>
                    <div class="col-sm-12 ijv-small-padding " id="" style=''>
                        <div class='ijv-sunken-box' id="container_Show_Hardware_Middle" style=''>
                        </div>
                    </div>
                    <div class="col-sm-8 ijv-small-padding " id="" style=''>
                        <div class='ijv-sunken-box' id="container_Show_Hardware_Left_Lower" style=''>
                        </div>
                    </div>
                    <div class="col-sm-4 ijv-small-padding " id="" style=''>
                        <div class='ijv-sunken-box' id="container_Show_Hardware_Right" style=''>
                        </div>
                    </div>
                </div>
            </div>

            <div id="menuTab_Payment_Data" class="tab-pane fade">
                <div class='XXrow'>
                    <div class="col-sm-6 ijv-small-padding" style=''>
                        <div class='ijv-sunken-box' id="container_Show_Payment_Data_Left" style=''>
                        </div>
                    </div>
                    <div class="col-sm-6 ijv-small-padding" style=''>
                        <div class='ijv-sunken-box' id="container_Show_Payment_Data_Right" style=''>
                        </div>
                    </div>
                </div>
            </div>

            <div id="menuTab_CRAA_Status" class="tab-pane fade">
                <div class='XXrow'>
                    <div class="col-sm-4 ijv-small-padding" id="" style=''>
                        <div class='ijv-sunken-box' id="container_Show_CRAA_Data_Upper_Left" style=''>
                        </div>
                    </div>
                    <div class="col-sm-8 ijv-small-padding" id="" style=''>
                        <div class='ijv-sunken-box' id="container_Show_CRAA_Data_Upper_Right" style=''>
                        </div>
                    </div>
                    <div class="col-sm-12 ijv-small-padding" id="" style=''>
                        <div class='ijv-sunken-box' id="container_Show_CRAA_Data_Middle" style=''>
                        </div>
                    </div>
                </div>
            </div>

            <div id="menuTab_IMEI_History" class="tab-pane fade" onclick=''>
                <div class='XXrow'>
                    <div class="col-sm-12 ijv-sunken-box ijv-small-padding" id="" style='background-color: #FFFFFF'>
                        <table id="Table_IMEI_History" class="display table " cellspacing="0" width="100%" >
                            <thead>
                                <tr>
                                    <th>Customer No</th>
                                    <th>User Name</th>
                                    <th>Mobile No</th>
                                    <th>Sim No</th>
                                    <th>IMEI No</th>
                                    <th>Date Added</th>
                                    <th>Phone Name</th>
                                </tr>
                            </thead>
                        </table>
                        <div class='ijv-sunken-box' id="container_Show_Network_Data_Middle" style=''>
                            <pre id="Network_History_Comments" style="white-space: pre-line; overflow-x: hidden; border: 1px solid rgba(67, 72, 87, 0.58) !important;"> 
                            </pre>;                                        
                        </div>
                    </div>
                </div>
            </div>


            <div id="menuTab_Payment_History" class="tab-pane fade" onclick=''>
                <div class='XXrow'>
                    <div class="col-sm-12 ijv-sunken-box ijv-small-padding" id="" style='background-color: #FFFFFF'>
                        <table id="Table_Payment_History" class="display table " cellspacing="0" width="100%" >
                            <thead>
                                <tr>
                                    <th>Customer No</th>
                                    <th>Invoice No</th>
                                    <th>Invoice Date</th>
                                    <th>Reference</th>
                                    <th>Rec Type</th>
                                    <th>Paid Amount</th>
                                    <th>Inv Amount</th>
                                    <th>Arrears</th>
                                    <th>Balance</th>
                                </tr>
                            </thead>
                        </table>
                        <div class='ijv-sunken-box' id="container_Show_Network_Data_Middle" style=''>
                            <pre id="Payment_History_Comments" style="white-space: pre-line; overflow-x: hidden; border: 1px solid rgba(67, 72, 87, 0.58) !important;"> 
                            </pre>;                                        
                        </div>
                    </div>
                </div>
            </div>

            <div id="menuTab_Network_History" class="tab-pane fade" onclick=''>
                <div class='XXrow'>
                    <div class="col-sm-12 ijv-sunken-box ijv-small-padding" id="" style='background-color: #FFFFFF'>
                        <table id="Table_Network_History" class="display table " cellspacing="0" width="100%" >
                            <thead>
                                <tr>
                                    <th>Account Name</th>
                                    <th>Customer No</th>
                                    <th>Mobile No</th>
                                    <th>User Name</th>
                                    <th>Status</th>
                                    <th>Sim No</th>
                                    <th>Netwk Date</th>
                                    <th>Netwk Time</th>
                                    <th>Code</th>
                                    <th>Netwk Desc</th>
                                    <th>Clerk ID</th>
                                </tr>
                            </thead>
                        </table>
                        <div class='ijv-sunken-box' id="container_Show_Network_Data_Middle" style=''>
                            <pre id="Network_History_Comments" style="white-space: pre-line; overflow-x: hidden; border: 1px solid rgba(67, 72, 87, 0.58) !important;"> 
                            </pre>;                                        
                        </div>
                    </div>
                </div>
            </div>

            <div id="menuTab_Client_History" class="tab-pane fade" onclick=''>
                <div class='XXrow'>
                    <div class="col-sm-12 ijv-small-padding" id="" style=''>
                        <div class='ijv-sunken-box' id="container_Show_Client_History_Middle" style=''>
                            <pre id="Client_History_Comments" style="white-space: pre-line; overflow-x: hidden; border: 1px solid rgba(67, 72, 87, 0.58) !important;"> 
                            </pre>;                                        
                        </div>
                    </div>
                </div>
            </div>




        </div>
    </div>

    <div id="Client_History_Container" style="display: none">

    </div>   <!--   H E R E,   T H I S     I S    W H E R E     T H E    F O R M A T T E D    H I S T O R Y    G O E S   -->
                    
<?php echo '</script>' ?>

				
			

So,  what have we seen in this Tutorial?  We’ve taken a look at an example User Interface,  we’ve familiarised ourselves with the verious drill down functions within that interface and then we’ve looked at some of the Javascript functions which build the interface,  and we’ve looked at some of the raw HTML structure which is required for those Javascript functions to work.

In our demo Customer User Interface,  the HTML structure (seen above) has a series of sub pages.  The page called “User Data” is where we use React.js to build various components.  The other sub Pages contain table structures which get populated by the various functions which are calling DataTables.js 

And thus we can see an example of JSON data being used in different ways  –  depending on how and where we would like that JSON data to be represented at an appearance level.

In a future tutorial we’ll show you how to use JSON data to build an online shopping page,  but that’s for another time.

Building a Business Application in WordPress

Here in Step Three of this Tutorial we’re going to look at the various ways JSON data can be used by a calling client,  in particular we’re going to look at how JSON data can populate HTML Tables using a library named DataTables.js

Step Three  –  Understanding the role of JSON data.

As noted in the previous section,  this software demonstration is a true Single Page Application.  Once it reaches the “document ready” state  (which for those who don’t know, that means the website has completely loaded and there’s nothing else to come)  well,  from that point onwards the only network traffic are tightly bundled json data responses to the various ajax requests which are kicked off when a user negotiates the client software.

For example, when you click on our demo Client Interface,  the first bit of JSON data which comes back to your browser is the Level One customer account data.  If the Level One request is succesful,  five more ajax calls take place and they represent the supplementary data which populates the various drill down functions within the user interface.

				
					{
    "draw": 0,
    "recordsTotal": 1,
    "recordsFiltered": 1,
    "data": [
        {
            "custno": "1001145",
            "cc_stage": "15",
            "stage_date": "1998-05-07 16:15:53",
            "name": "RJ ELITE SECURITY SYSTEMS",
            "address": "",
            "postcode": "3201",
            "entry_date": "1996-10-24 16:52:51",
            "inv_run": "1",
            "balance": "3036.47",
            "overdue": "3036.47",
            "prev_cust": "",
            "id_no": "",
            "section": "AD1",
            "cus_type": "FDC",
            "suppress": "FGM",
            "cust_type": "C",
            "debt_coll": "3",
            "solicitor": null,
            "code": "3",
            "collection_house": "COMMERCIAL CREDIT PTY LTD",
            "telephone": "********",
            "facsimile": null,
            "sup_mobile": null,
            "date_suppr": "1998-03-06 00:00:00",
            "inv_freq": "M",
            "card_type": null,
            "card_name": null,
            "exp_date": null,
            "amex": null,
            "birth_date": "*********",
            "dd_type": "B",
            "pay_method": "C",
            "current": "0.00",
            "over30": "0.00",
            "over60": "298.50",
            "over90": "773.86",
            "over120": "1063.27",
            "over150": "900.84"
        }
    ]
}
				
			

And here’s a snippet of the IMEI History JSON data which gets loaded as a sub page in our demo Client User Interface. You can access this by clicking on the button named “IMEI History”.  If you haven’t seen JSON data before you might now be able to see now how it can represent one record at a time,  or multiple records.

				
					{
    "draw": 0,
    "recordsTotal": 3,
    "recordsFiltered": 3,
    "data": [
        {
            "custno": "1001145",
            "mobile_no": "0414506135",
            "user_name": "PAUL B JONES",
            "sim_no": "89610300000202181852",
            "imei_no": "490113209705510",
            "date_in": "1997-11-15 00:00:00",
            "phone_name": "NOKIA 1610"
        },
        {
            "custno": "1001145",
            "mobile_no": "0414506135",
            "user_name": "PAUL B JONES",
            "sim_no": "89610300000202181852",
            "imei_no": "490113301997350",
            "date_in": "1997-12-31 00:00:00",
            "phone_name": null
        },
        {
            "custno": "1001145",
            "mobile_no": "0414506135",
            "user_name": "PAUL B JONES",
            "sim_no": "89610300000202181852",
            "imei_no": "490113302633330",
            "date_in": "1998-01-06 00:00:00",
            "phone_name": null
        }
    ]
}
				
			

Of course knowing about how JSON data looks is somewhat useless if you don’t know how to produce it.  As noted earlier,  the data repository in these Tutorials makes use of PHP and MySQL,  and that’s because that’s what WordPress uses.  It’s quite possible to use any SQL system you want,  indeed you don’t even have to use PHP for the JSON data hits themselves  –  but assuming you are using PHP,  here’s a code snippet of the function which produces the JSON data for the Payment History sub page in our demo Customer User Interface.

The Payment History (per client) in our demo Client User Interface is a typical ledger system  –  almost identical to how our Bank Accounts work.  All payments received go into a table named AR_CASH,  and all invoices created go into a table named AR_MAST.  When we ask to see a client’s Payment History,  the PHP function shown below merges those two tables using a classic SQL UNION statement and it then sorts the data in Date Order.  It’s fast too. That’s because both tables are indexed on Custno.

				
					    public static function extract_Payment_History($lcAction, $lnCustno) {
        /**
         *  extract the payment history using a UNION statement.
         *
         *  @returns boolean TRUE if successful,  otherwise FALSE
         */
        //xdebug_break();
        $sql_details = include 'mySQL.config.php';
        $db = self::db($sql_details);

        xdebug_break();
        $pdoStmt1 = $db->prepare("
            SELECT custno, invno, invdate, reference, amount, type, paidamt, arrears, balance
            FROM ar_mast WHERE custno=:lnCustno AND amount <> 0

            UNION 

            SELECT custno, invno, invdate, reference, amount, type, amount AS paidamt, amount AS arrears, amount AS balance
            FROM ar_cash WHERE custno=:lnCustno AND amount <> 0 

            ORDER BY invdate
            ");
            
            
        $pdoStmt1->bindValue(':lnCustno', $lnCustno);
        $pdoStmt1->execute();
        //  $result = $statement->execute();
        $data = $pdoStmt1->fetchALL(PDO::FETCH_ASSOC);  //  return $stmt->fetchAll(PDO::FETCH_BOTH);


        $arrlength = count($data);
        if ($arrlength == 0) {
            echo "Zero records have been found to recalibrate WHERE Callid = " . $lnCallid;
            return TRUE;
        }
        $out = array();
        $m0balance = 0.00;
        $m0totalpayments = 0.00;
        for ($i = 0, $ien = count($data); $i < $ien; $i++) {
            if ( $data[$i]['amount'] < 0) {
                $m0totalpayments = $m0totalpayments + $data[$i]['amount']; 
            }
        }
        $m0payments_left = $m0totalpayments * -1;
        for ($i = 0, $ien = count($data); $i < $ien; $i++) {
            $row = array();
            $m0amount = round($data[$i]['amount'], 2) ;
            $m0balance = round($m0balance + $m0amount, 2)  ;
            $row['custno'] = $data[$i]['custno'];
            $row['invno'] = $data[$i]['invno'];
            $row['invdate'] = $data[$i]['invdate'];
            $row['reference'] = $data[$i]['reference'];
            $row['amount'] = $data[$i]['amount'];
            $row['type'] = $data[$i]['type'];
            $row['paidamt'] = $data[$i]['paidamt'];
            if ( $data[$i]['amount'] > 0 ) {
                if ( $m0payments_left <= 0 ) {
                    $row['arrears'] = $data[$i]['amount'] ;
                } else {
                    if ( $m0payments_left > $data[$i]['amount'] ) {
                        $row['arrears'] = 0.00 ;
                        $m0payments_left = $m0payments_left - $data[$i]['amount'];
                    } else {
                        $row['arrears'] = $data[$i]['amount'] - $m0payments_left ;
                        $m0payments_left = 0.00;
                    } 
                }
            } else {
                $row['arrears'] = 0.00 ;
            }
            $row['balance'] = number_format($m0balance, 2, '.', ',');
            $row['invdate'] . ' ' . $row['amount'] . "\n <\p>";
            $out[] = $row;

        }

        return array(
            "draw" => 0,
            "recordsTotal" => intval(count($data)),
            "recordsFiltered" => intval(count($data)),
            "data" => $out
        );
        
        $db = null;
    }


    

				
			

For the uninitiated,  it’s worth noting JSON data can be used in many different ways after it gets returned to the client browser.  JSON data is an excellent way to represent the data contained in a database table.  It can represent just one record at a time  –  such as a single Customer Account Record,  and it can also represent multiple records at a time  –  such as a request for all the client history notes attached to a Customer Account Record.  

As computer programmers,  the way we process JSON data is determined by what a business needs,  and how we want that data to look.  In this demonstration,  some of the JSON data (the individual Customer Account Record) is converted into a powerful Backbone javascript object which has it’s own set of properties and methods.  Meanwhile,  other bits of JSON data are converted into highly functional tables.  

In a future tutorial I’ll show you how to use JSON data to build an online shopping page,  but that’s for later.

Building a Business Application in WordPress

In Step Four of this Tutorial we’re going to look at a WordPress plugin called Code Embed.  Please Note:  There are several plugins for WordPress which all do the same thing,  what’s important is WHY we need to use a plugin like Code Embed to achieve our goal of integrating a Business Application into WordPress.

Step Four  –  Understanding the WordPress Code Embed Plugin.

Firstly,  why is the Code Embed plugin necessary?  The short answer is this…  it provides a location for customised Javascript,  HTML and CSS within a WordPress page.  This,  in turn,  gives you the ability to convert a static WordPress page into a fully featured powerful Single Page Application  –  which,  I might add,  is what you’re looking at in your browser as you read this.

It’s fair to say this particular tutorial is more about “conceptual understanding” than it is about specific software knowledge.  Hopefully,  once you understand the concepts of what we’re doing here you should be able to transfer those concepts to YOUR particular WordPress environment.

Perhaps this is a good time to remember something  –  you still need to know how to design a business application in the first place.  That’s a skill we assume you already have.  And the converse is true too.  You also need to know how to design great looking pages in WordPress,  along with all the menus and sidebars etc etc which make WordPress look so cool.  All these separate skills are integrated together thanks to plugins like Code Embed.

So let’s take a look at three separate Screenshots to see what’s going on.  Please Note:  You can click on any of these screenshots and they’ll automatically display in “large format mode”.

Screenshot One is the Cellular One Customer Care system  –  assuming you’re using a monitor at least 1920 pixels wide.  If you’re using a smaller screen,  two things will happen  –  some of the columns will start to drop off  (based on priority) and the sidebar disappears on screens smaller than 1200 pixels wide.

Main Panel Diagram
Screenshot One - The Cellular One Customer Care System

In Screenshot One we can see a typical Navbar at the top of the screen plus a typical Sidebar on the left side of the screen.  Today we’re only interested in the Main Panel which is where the Cellular One Customer Care software lives.  Indeed,  the Main Panel is where this entire Single File Application lives.

Please Note:  To make a Single Page Application in WordPress,  you also need to know how to design a Single Page template in WordPress.  Now,  if that sounds confusing….  it’s not really.  The Single Page template contains the Navbar and the Sidebar content and the template sets aside empty space for the Main Panel.  Your Business Application logic won’t live in the Single Page Template,  it will live in a specific WordPress page which in turn gets loaded in the Single page Template.  Hope that makes sense.

In the next screenshot  (Screenshot Two)  we get a look at what’s going on under the hood.  At Velodata we’re quite fond of Elementor Pro,  but you can use any WYSIWYG development environment that you’d like.  What’s important are the two sections in the red highlighted circles.  This is where our customed Javascript and HTML structures are entered into the Page using the Code Embed plugin.  

We’ve already mentioned this is a Single Page Application.  How it works is the “Tutorials ” which are defined in the Navigator Panel become visible (or non-visible)  in your browser based on which Menu option you’ve chosen.  All of the Tutorials get loaded at Document startup  –  but not the two major software demonstrations.  The demos get populated in real time within the Main Panel.

If you can find it,  look for a line in the next Screenshot which says CODE_Auditflow.Routes.js.  That’s a really powerful Javascript program using React.js routing functions.  It contains the logic which responds to YOUR clicks in the SideBar menu.  We’ll go into that in more detail a little bit later.  What’s important is a lot of the customised Javascript and HTML structures live in the Custom Fields which are highlighted below.  

Tutorial 04 - Image 01
Screenshot Two - The Tutorial in Development Mode using Elementor Pro

In Screenshot Two we’re able to see where the “customised javascript and HTML” for this Single Page Application gets “inserted” into the WordPress output stream before it leaves the webserver.  However,  there’s something you should know.  When we use the WordPress Code Embed plugin,  for it to work correctly we need to create references which have the double handle bar braces,  followed by the word “CODE”.  Only then will the Code Embed plugin populate those references with your customised Javascript and HTML.  And that’s what we’re going to look at next.

In the next screenshot  (Screenshot Three)  we can see our WordPress software demonstration page in “non WYSIWYG” development mode,  also known as the “traditional WordPress editor”.  This is where we have created the various Custom Fields being used in this software demonstration,  and it’s also where we populate the associated Javascript and HTML within those custom fields.

Defining custom fields using The Code Embed Plugin
Screenshot Three - Defining the various custom fields which support this Software Demonstration

In Screenshot Three (above) we can see the same six custom fields which are referenced in Screenshot Two.  You will note both Screenshots share the same six names.  

Now let’s look at the three custom fields which end in ‘.js’    there’s something important about this.  Yes,  it is perfectly possible to create Javascript files which get loaded by a browser as separate media requests.  However,  during the development phase you definitely DON’T want to do that.  You are much MUCH better off loading your business application logic as we’ve done here because disk caching is your enemy.

Web browsers love to cache stuff like CSS files and JS files to make websites load faster  –  and that’s fair enough.  However,  once you start getting into real world website design it’s very common for services like Cloudflare and Inapsula to come into play,  and they also provide caching services.  Put another way,  when disk caching starts getting involved,  you can’t guarantee that people are using the latest version of your Business Application logic.

So please trust me when I say this…  the method you see above is the way to go.  Your customised HTML and  Javascript  (including your React.js components) get inserted into your WordPress output each time a user loads your WordPress Business Application.  Yes,  in theory,  that can add to your load times but how you get around that is by using a “quasi compiler” version of your WordPress output using WordPress plugins like WP-Cache.

Finally,  let’s take a look at at what’s inside the Custom Field called CODE_Auditflow.Routes.  We mentioned earlier this is where React.js reacts to your Menu selections.  If you look carefully,  you should notice the hashtag data in your browser’s Address Bar change when you make a new Menu selection.  One Line 4 of the next code snippet we can see we’re defining the Rest API Javascript object which is controlling this software demonstration as you read this.

The really interesting stuff starts at Line 38.  All of this programming code is loaded and currently running in your web browser as you read this.

				
					<script>

$(document).ready(function () {
    var restfulApp = React.Router.extend({
        routes: {
            "Main(/:lcSubCategory)(/:lcProduct)": "routeMain",
            "Tutorial(/:lcSubCategory)(/:lcProduct)": "routeTutorial",
            "Documentation(/:lcSubCategory)(/:lcProduct)": "routeDocumentation",
            "About": "routeAbout",
            "Dashboard": "routeDashboard",
            "Diagnostics": "routeDiagnostics"
        },
        defaultAction: function () {


        },
        routeMain: function (lcSubCategory, lcProduct) {
            if ($('html').hasClass("nav-open")) {
                $(".navbar-toggle").trigger("click");
            }
            $('#IJV_Main_Screen').show();
            this.loadMainDashboardRoutes(lcSubCategory, lcProduct);

        },
        routeDocumentation: function (lcSubCategory, lcProduct) {
            $("#IJV_Main_Wrapper").animate({scrollTop: 0}, 600);
            $(".fixed-plugin").css("display", "none");
            if ($('html').hasClass("nav-open")) {
                $(".navbar-toggle").trigger("click");
            }
            $('#IJV_Main_Screen').show();
            $('#IJV_Main_Screen_Modal').removeClass('shown');
            this.loadDocumentationRoutes(lcSubCategory, lcProduct);
        },



        loadMainDashboardRoutes: function (lcSubCategory, lcProduct) {
            if (lcSubCategory === null || lcSubCategory === "")
            {
                $("#CellPRO_Main_Panel").css("display", "none");
                $("#CellPRO_Main_Menu").css("display", "block");
                $("#CellPRO_Tutorial_Step_02").css("display", "none");
                $("#CellPRO_Tutorial_Step_03").css("display", "none");
                $("#CellPRO_Tutorial_Step_04").css("display", "none");
                $("#CellPRO_Main_Menu").scrollTop(0);
                $("html, body").scrollTop(0);
            }
            if (lcSubCategory === "AAPT_Mobile")
            {
                $("#CellPRO_Main_Panel").css("display", "block");
                $("#CellPRO_Main_Menu").css("display", "none");
                $("#CellPRO_Tutorial_Step_02").css("display", "none");
                $("#CellPRO_Tutorial_Step_03").css("display", "none");
                $("#CellPRO_Tutorial_Step_04").css("display", "none");
                $("html, body").scrollTop(0);
                if (lcProduct !== null) {
                    $('#IJV_Main_Screen').addClass("SingleView");
                    this.loadSingleView(lcSubCategory, lcProduct);
                }
                if (lcProduct === null) {
                    $("#IJV_Main_Screen").load("/view/Article_02.MasterList.php", function (responseTxt, statusTxt, xhr) {
                        if (statusTxt === "success") {
                            F0_Build_ARNEWJOB_Masterlist();   //  this routine is found in "view/Article_02.MasterList.php"
                            
                        }
                        if (statusTxt === "error")
                            alert("Error: " + xhr.status + ": " + xhr.statusText);
                    });
                }
            }
            if (lcSubCategory === "Customer_Care")
            {
                $("#CellPRO_Main_Panel").css("display", "block");
                $("#CellPRO_Main_Menu").css("display", "none");
                $("#CellPRO_Tutorial_Step_02").css("display", "none");
                $("#CellPRO_Tutorial_Step_03").css("display", "none");
                $("#CellPRO_Tutorial_Step_04").css("display", "none");
                $("html, body").scrollTop(0);
                if (lcProduct !== null) {
                    $('#IJV_Main_Screen').addClass("SingleView");
                    this.loadSingleView(lcSubCategory, lcProduct);
                }
                if (lcProduct === null) {
                    $("#IJV_Main_Screen").load("/view/Article_02.Customer_Care.php", function (responseTxt, statusTxt, xhr) {
                        if (statusTxt === "success") {
                            $('#modal_search_data').html('0414777');
                            $('#modal_search_column').html('Mobile_Number');
                            $('#modal_search_instruction').html('Search');
                            F0_Build_Handset_Masterlist();   //  this routine is found in "view/Article_02.Customer_Care.php"
                        }
                        if (statusTxt === "error")
                            alert("Error: " + xhr.status + ": " + xhr.statusText);
                    });
                }
            }
            if (lcSubCategory === "Tutorial_Step_02")
            {
                $("#CellPRO_Main_Panel").css("display", "none");
                $("#CellPRO_Main_Menu").css("display", "none");
                $("#CellPRO_Tutorial_Step_02").css("display", "block");
                $("#CellPRO_Tutorial_Step_03").css("display", "none");
                $("#CellPRO_Tutorial_Step_04").css("display", "none");
                $("#CellPRO_Tutorial_Step_02").scrollTop(0);
                $("html, body").scrollTop(0);
            }
            if (lcSubCategory === "Tutorial_Step_03")
            {
                $("#CellPRO_Main_Panel").css("display", "none");
                $("#CellPRO_Main_Menu").css("display", "none");
                $("#CellPRO_Tutorial_Step_02").css("display", "none");
                $("#CellPRO_Tutorial_Step_03").css("display", "block");
                $("#CellPRO_Tutorial_Step_04").css("display", "none");
                $("#CellPRO_Tutorial_Step_03").scrollTop(0);
                $("html, body").scrollTop(0);
            }
            if (lcSubCategory === "Tutorial_Step_04")
            {
                $("#CellPRO_Main_Panel").css("display", "none");
                $("#CellPRO_Main_Menu").css("display", "none");
                $("#CellPRO_Tutorial_Step_02").css("display", "none");
                $("#CellPRO_Tutorial_Step_03").css("display", "none");
                $("#CellPRO_Tutorial_Step_04").css("display", "block");
                $("#CellPRO_Tutorial_Step_04").scrollTop(0);
                $("html, body").scrollTop(0);
            }
        },


        loadDocumentationRoutes: function (lcSubCategory, lcProduct) {
            $("#IJV_Main_Screen").load("view/Article_01.php", function (responseTxt, statusTxt, xhr) {
                if (statusTxt === "success") {
                    $("#IJV_Main_Screen").scrollTop(0);
                    if (lcSubCategory === 'Introduction') {
                        $("#AR01_PlaceHolder").load("view/documentation/About_This_Software.php", function (responseTxt, statusTxt, xhr) {
                        });
                        return;
                    }
                    if (lcSubCategory === 'Documentation') {
                        $("#AR01_PlaceHolder").load("view/documentation/Documentation.php", function (responseTxt, statusTxt, xhr) {
                        });
                        return;
                    }
                    if (lcSubCategory === 'Installation') {
                        $("#AR01_PlaceHolder").load("view/documentation/Installation.php", function (responseTxt, statusTxt, xhr) {
                        });
                        return;
                    }
                    if (lcSubCategory === 'Routes') {
                        $("#AR01_PlaceHolder").load("view/documentation/Routes.php", function (responseTxt, statusTxt, xhr) {
                        });
                        return;
                    }
                    if (lcSubCategory === 'DataTables') {
                        $("#AR01_PlaceHolder").load("view/documentation/Presentation_Layer.php", function (responseTxt, statusTxt, xhr) {
                        });
                        return;
                    }
                    if (lcSubCategory === 'Javascript') {
                        $("#AR01_PlaceHolder").load("view/documentation/OOP_Javascript.php", function (responseTxt, statusTxt, xhr) {
                        });
                        return;
                    }
                    if (lcSubCategory === 'PHP_Classes') {
                        $("#AR01_PlaceHolder").load("view/documentation/OOP_PHP.php", function (responseTxt, statusTxt, xhr) {
                        });
                        return;
                    }
                    if (lcSubCategory === 'Data_Retrieval') {
                        $("#AR01_PlaceHolder").load("view/documentation/Data_Retrieval_Layer.php", function (responseTxt, statusTxt, xhr) {
                        });
                        return;
                    }
                    if (lcSubCategory === 'No_More_Tests') {
                        $("#AR01_PlaceHolder").load("view/documentation/No_More_Tests.php", function (responseTxt, statusTxt, xhr) {
                        });
                        return;
                    }
                }
            });
        }

    });

    new restfulApp;
    //Initiate a new history and controller class
    Backbone.emulateHTTP = true;
    Backbone.emulateJSON = true;
    Backbone.history.start();
});


</script>
				
			

In this tutorial we learnt how to use a Single Page template in WordPress.  We then learnt how to create a WordPress page which gets loaded by the Single Page template.  That WordPress page  (the Single File Application that you’re currently looking at)  uses the WordPress Code Embded plugin to store all the Javascript and HTML which supports this Single File Application.  And lastly,  we looked at the React Routing object which reacts to your Menu choices.

How Can We Help?

At Velodata your privacy is paramount.  We never share customer data with ANYONE.  Our goal is to help make your website and your webservers as secure as possible.

PHONE ENQUIRIES

0408 572 055

EMAIL ENQUIRIES

Velodata Logo

VELODATA

Specialists in Wordpress Cyber Security

IVAN JULIAN  –  VELODATA

GOLD COAST,  QUEENSLAND,  4216

P.O. BOX 359 RUNAWAY BAY,  QLD,  4216

Velodata Logo

VELODATA

Specialists in Wordpress Cyber Security

DOWNLOAD THE SOFTWARE

The Blackhole Cyber Security system is free to download and use,  however we do require a valid email address to prevent unlimited hotlinking to our website.

Step 1:  Please enter your name and a valid email address.  Phone Number is optional.
Step 2:  Confirm you’re a human and then click the download button!

Teddy Bear

Thanks for contacting us. We'll be in touch shortly!

Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex.

How Can We Help?

At Velodata your privacy is paramount.  We never share customer data with ANYONE.

Feel free to answer any or all of the fields in this contact form as best as you can.  You don’t have to fill them all in, but if you do, we can be efficient right from the word go.

Step 1:  Enter some contact details so we can get back to you
Step 2:  Give us some info about your IT systems to help us help you.
Step 3:  Confirm you’re a human and then submit!