Creating a Collaborative Dashboard

Full Collaborative Dashboard.pngThis tutorial shows how to build a Collaborative Dashboard, which enables you to visualize and collaborate on data from multiple systems at once.  If you haven't done so already, you should check out the video.

Overview

The dashboard has three parts:

  • Collective Intelligence shows collaborative information such as meeting notes, files, and follow-up items.
  • Enterprise Intelligence gives you an aggregate view of data from multiple systems, such as CRM, financials, and order history.
  • Web Intelligence augments your in-house knowledge with the latest information from the web, news, blogs, and even Twitter!

 

Let's take a more in-depth look of the individual parts.

Collective Intelligence

The top of the dashboard has the collaboration area.  This area automatically includes the contents of a dedicated account page, which is used to share notes, files, and comments.  Access to the page is provided by a link at the top of the area.  If the page doesn't exist yet, the link will create the missing account page and pre-populate it with default content.

Enterprise Intelligence

The middle of the dashboard is used for displaying information from various data sources.  For this example, we combine data from customer maintenance records, past orders, and the payment history.  This gives us a fairly complete idea of the quality of this customer, as well as a reminder of any upcoming maintenance renewals.  Additional information, such as the order details, are displayed on demand when needed.

Web Intelligence

The bottom of the dashboard uses multiple web-services to create an always up-to-date report of online information.  Using LinkedIn, it displays a list of contacts at the account's company.  Using Google, it shows a web search results for the company's name, recent news about the company, and results from various blogs that discuss it.  It also shows the current stock price, if the company is public.  And finally, it displays the most recent buzz about the company on Twitter.

Requirements

The Collaborative Dashboard tutorial requires the following components:

Pages & Templates

The complete experience requires 2 pages, 6 templates, and 5 extensions!  However, each component of the dashboard is fairly independent from the other.  So you can pick and chose the ones that fit your purpose best and leave the rest.

These are the dependencies:

  • Collaborative Dashboard is always required.
  • Account Notes, Template:Collaborate, and Template:NewAccountPage are only required for Collective Intelligence.
  • Template:Dashboard and Template:StockQuote are only required for Web Intelligence.
  • Template:Maintenance and Template:Orders are only required for Enterprise Intelligence.
Collaborative Dashboard

This is the main page of the dashboard with all the components.  Lines 2-3 show an input box that allows free entry of an account name.  This is an optional component that is not required, but is useful for test driving the dashboard.  In line 4 we use the if attribute to only render the <div> element and its contents if an account was specified in the URI of the page.  The rest of the page invokes the templates corresponding to each section passing in the account name.

<h1>Collaborative Dashboard</h1>
<p>Enter an account name.<br />
{{ dhtml.inputbox("Lookup account information:", __request.args.account, "Show", 24, "account", wiki.uri('Collaborative Dashboard')) }}</p>
<div if="__request.args.account" block="var account = __request.args.account">
<p>&nbsp;</p>
<p style="text-align: center;"><span style="font-size: x-large;"><u><strong>{{ account }}</strong></u></span></p>
<h3>Account Notes</h3>
{{ template.collaborate(account)}}
<h3>Maintenance History</h3>
{{ template.Maintenance(account) }}
<h3>Order History</h3>
{{ template.Orders(account) }}
<h3>Online Information</h3>
{{ template.Dashboard(account) }}
</div>
Account Notes

This page shows a tree of immediate child pages that have been created to collaborate on accounts.  Usually, users will not see this page, but if they do, it provides them with quick access to existing account collaboration pages.

<h1>Account Notes</h1>
<p>{{ wiki.tree(page.path, 1) }}</p>

Collective Intelligence

The following templates are required to enable the collective intelligence component of the dashboard.

Template:Collaborate

This template checks if a collaboration page already exists under "Account Notes" using the passed in account name as child page title.  If no page exists, the template shows a corresponding message with a link to create a new page.  If the page already exists, it provides a short summary of the number of files attached on the page, the number of comments left, who edited the collaboration page, a link to view the full page, and finally includes the "Notes" section from the page directly in place.  This last step enables users of the dashboard to see content from the collaboration page in place without having to click on a link and lose their current context.

<h1>Template:Collaborate</h1>
<div block="var account = $0 ?? $account; var path = wiki.appendpath('Account Notes', account); var exists = wiki.pageexists(path)">
<div block="var collab = wiki.getpage(path)" if="exists"><span style="color: rgb(128, 128, 128);">There are {{ #collab.files }} file(s) attached and {{ #collab.comments }} comment(s). &nbsp;Last edited on {{ date.format(collab.date, 'MMM d, yyyy') }} by {{ collab.author.name }}.</span>&nbsp; {{ web.link(collab.uri, 'View full page.') }}<br />
{{ wiki.page(collab.path, 'Notes') }}</div>
<div if="!exists">No collaboration page exists for this account.<br />
{{wiki.create('Click here to create one.', 'Account Notes', 'NewAccountPage', false, account, { account: account })}}</div>
</div>
Template:NewAccountPage

This template is used to populate a new account page with default content.  The top of the page has class="comment" attribute, which will make it only show in edit mode, but not when the page is viewed.  Below it, the page shows a link back to the dashboard for the current account.  Then it shows the "Notes" section, which is automatically included on the dashboard page.  Finally, it shows a summary of the top contributors for the accounts page.

<h1>Template:NewAccountPage</h1>
<div class="comment">
<table width="100%" cellspacing="0" cellpadding="1" border="1">
    <tbody>
        <tr>
            <td style="background-color: rgb(255, 255, 153);"><strong>Note:</strong> use this page to capture notes and files for the <em>{{edit: __request.args.account}}</em> account.</td>
        </tr>
    </tbody>
</table>
</div>
<p>{{edit: web.link(uri.build(wiki.uri('Collaborative Dashboard'), _, { account: __request.args.account }), 'Click here to return to the Collaborative Dashboard.') }}</p>
<h2>Notes</h2>
<p>(replace this text with your notes)</p>
<h2>Page Summary</h2>
<p>Page created on {{edit: date.format(date.now, 'MMM d, yyyy') }}.</p>
<p><strong>Most active contributors</strong><br />
{{ wiki.contributors(page.path) }}</p>

Enterprise Intelligence

The following templates are required to enable the enterprise intelligence component of the dashboard.

NOTE: these templates are designed for a specific setup that will not suite your needs out of the box.  They are only provided for educational purposes.

Template:Maintenance

This template fetches information about maintenance contracts for the current account.  In line 3, we use the if attribute to check if any items where found.  If not, we emit the <p> element stating that there is no information to display.  Otherwise, we emit the <table> where we iterate over each entry in the returned list of maintenance records.

<h1>Template:Maintenance</h1>
<div block="var items = list.sort(snaplogic.json('Finance/MaintenanceHistory/MaintenanceHistory', { ACCOUNT: $0 ?? $account }), _, true, 'date.compare($left[3], $right[3])')">
<p if="#items == 0">No maintenance information found for this account.</p>
<table width="100%" cellspacing="0" cellpadding="1" border="1" if="#items != 0">
    <tbody>
        <tr valign="top" foreach="var item in items">
            <td width="70%"><strong>{{ item[0] /* product_name */ }}</strong> <span style="color: rgb(255, 0, 0);"><span if="item[9] == 'Expired'">(Expired)</span></span><span style="color: rgb(51, 153, 102);"><span if="item[9] == 'Renewal'">(Upcoming renewal)</span></span><br />
            <span style="font-size: small;"><span style="color: rgb(128, 128, 128);">{{ item[1] /* description */ }}</span></span></td>
            <td width="15%">Qty: {{ item[6] }}<br />
            Price: {{ item[7] }}</td>
            <td width="15%"><strong>Start: {{ item[2] }}<br />
            End: {{ item[3] }}<br />
            </strong></td>
        </tr>
    </tbody>
</table>
</div>
Template:Orders

This template fetches information about past order for the current account.  Similarly to Template:Maintenance, we first check if any orders where found.  If so, we emit the <table> element with a row for each order.  Furthermore, we emit an inner <table> that is initially hidden that contains the information for each item in the order.

To make the inner <table> appear and disappear dynamically, we use the dhtml.link and dhtml.toggle functions.  The dhtml.link function allows us to embed a link that when clicked sends a message to another component.  In this case, the other component is dhtml.toggle, which toggles the display state of any HTML element with a matching id attribute.

<h1>Template:Orders</h1>
<div block="var account = $0 ?? $account; var orders = list.sort(snaplogic.json('Finance/Orders/Orders', { ACCOUNT: account }), _, true, 'date.compare($left[1], $right[1])')">
{{ dhtml.toggle(@toggle, _, 'fast') }}
<p if="#orders == 0">No orders found for this account.</p>
<table width="100%" cellspacing="0" cellpadding="1" border="1" if="#orders != 0">
    <tbody>
        <tr foreach="var order in orders" block="var items = snaplogic.json('Finance/OrderDetail/OrderDetail', { ORDER: order[0] })">
            <td>
            <table width="100%" cellspacing="0" cellpadding="1" border="0">
                <tbody>
                    <tr valign="top">
                        <td width="50%"><strong>ID: {{ order[0] }}</strong><br />
                        Invoice Date:&nbsp;{{ order[1] }}<br />
                        <br />
                        {{ dhtml.link('Order Details (' .. #items .. ( #items == 1 ? ' item)' : ' items)' ), { id: 'order-details-' .. __count }, @toggle) }}</td>
                        <td width="50%"><strong>Total Invoice:&nbsp;{{ order[4] }}</strong> <span if="order[5] == '0%'" style="color: rgb(128, 128, 128); font-size: small;">(no discount)</span><span if="order[5] != '0%'" style="font-size: small;" class="warning">({{ order[5] }} discount)</span><br />
                        <span class="{{ order[10] != 'Paid in Full' &amp;&amp; date.diffdays(date.now, order[1]) &gt; 75 ? 'warning' : nil }}">Invoice Status: {{ order[10] }}</span><br />
                        Paid: {{ order[8] }} <span if="order[7] != ''" style="font-size: small;"><span if="num.cast(order[7]) &lt;= 75" style="color: rgb(128, 128, 128);">(after {{ order[7] }} days)</span><span if="num.cast(order[7]) &gt; 75" class="warning">(after {{ order[7] }} days)</span></span><br />
                        Method: {{ order[9] != '' ? order[9] : 'N/A' }}</td>
                    </tr>
                </tbody>
            </table>
            <table width="90%" cellspacing="0" cellpadding="1" border="1" align="right" id="{{ 'order-details-' .. __count }}" style="display: none;">
                <tbody>
                    <tr valign="top" foreach="var item in items">
                        <td width="70%"><strong>{{ item[8] /* product_name */ }}</strong><br />
                        <span style="font-size: small;"><span style="color: rgb(128, 128, 128);">{{ item[10] /* description */ }}</span></span></td>
                        <td width="15%">Qty: {{ item[6] }}<br />
                        Price: {{ item[9] }}</td>
                        <td width="15%" style="text-align: right;"><strong>{{ item[7] /* price */ }}</strong></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</div>

Web Intelligence

The following templates are required to enable the web intelligence component of the dashboard.

Template:Dashboard

This template embeds widgets from LinkedIn, Yahoo!, and Google.  Each widget is parametrized with the name of the current account.

<h1>Template:Dashboard</h1>
<div block="var account = $0 ?? $account">
<div style="padding: 0px 20px 20px; width: 320px; float: left;">
<h4>Contacts</h4>
<p>{{ linkedin.CompanyInsider{company: account, style: "noborder"} }}</p>
<h4>Company Stock</h4>
<p>{{ template.StockQuote{company: account} }}</p>
<h4>Twitter Updates</h4>
<p>{{ google.feed(uri.build("http://search.twitter.com/search.atom", _, { q: string.replace(account, " ", "") })) }}</p>
</div>
<div style="padding: 0px 20px 20px; width: 320px; float: left;">
<h4>Web &amp; Blog Search</h4>
<p>{{ google.Search{search: account, options: { web: 'partial', news: 'partial', blogs: 'partial' }} }}</p>
</div>
</div>
Template:StockQuote

This template is used to convert the account name into a stock symbol.  To accomplish this, we piggyback of the auto-complete web-service that exists on the Yahoo! Finance portal.  This service returns a JSON response that is a list of possible matche for given account name.  Using a bit of DekiScript, we remove the extra data, then convert the JSON response to a DekiScript value and use it to dynamically embed the Yahoo! Finance widget with the best-guess stock symbol.  If we can't find a matching stock symbol, we show a message indicating so.

<h1>Template:StockQuote</h1>
<p>{{ var msg = web.text(uri.build("http://d.yimg.com/autoc.finance.yahoo.com/autoc?callback=YAHOO.Finance.SymbolSuggest.ssCallback", _, { query: $company ?? 'Google' }));<br />
let msg = string.deserialize(string.substr(msg, 39, -1));<br />
if(#msg.resultset.result &gt; 0) { yahoo.stockchart(msg.resultset.result[0].symbol) } else { 'No Stock Data Available...' }<br />
}}</p>

 

Tag page
Viewing 3 of 3 comments: view all
There is an error:
wiki.uri('Account Information Dashboard' ...
should be
wiki.uri('Collaborative Dashboard'
Posted 23:06, 7 Nov 2008
Good catch! I fixed the mistake.
Posted 23:26, 7 Nov 2008
1. There's no reference that says just where one finds out how to create a template.
2. The instructions include a warning that the templates for Enterprise Intelligence are not really suitable for most people's applications, but there's no indication of what specifically makes these unsuitable--in other words, how would a user go about creating these templates for their own application.

One major problem I have with this application's documentation (what limited amount of it there is) is that is seems to assume the reader pretty much already knows how to do whatever needs to be done. But you're marketing the product as being extremely user friendly, so you should be getting folks who aren't always tech savvy.
Posted 21:47, 21 Nov 2008
Viewing 3 of 3 comments: view all
You must login to post a comment.