Editable narratives in new ui

This commit is contained in:
Everett Sochowski 2012-03-28 17:21:39 +02:00
parent ec84a4b30f
commit 41b5834cc0
6 changed files with 147 additions and 384 deletions

View File

@ -28,73 +28,6 @@ class Management {
val accJObj = JObject(List(JField("holder", JString("Music Pictures Limited"))))
val currentAccount = Account.find(accJObj) getOrElse Account.createRecord
//TODO: This should go. There is too much presentation stuff living here in the code
object CustomSHtml extends SHtml{
def ajaxEditable (displayContents : => NodeSeq, editForm : => NodeSeq, onSubmit : () => JsCmd, defaultValue: String) : NodeSeq = {
import net.liftweb.http.js
import net.liftweb.http.S
import js.{jquery,JsCmd,JsCmds,JE}
import jquery.JqJsCmds
import JsCmds.{Noop,SetHtml}
import JE.Str
import JqJsCmds.{Hide,Show}
import net.liftweb.util.Helpers
val divName = Helpers.nextFuncName
val dispName = divName + "_display"
val editName = divName + "_edit"
def swapJsCmd (show : String, hide : String) : JsCmd = Show(show) & Hide(hide)
def setAndSwap (show : String, showContents : => NodeSeq, hide : String) : JsCmd =
(SHtml.ajaxCall(Str("ignore"), {ignore : String => SetHtml(show, showContents)})._2.cmd & swapJsCmd(show,hide))
val editSrc = "/media/images/edit-off.png"
val addSrc = "/media/images/add-on.png"
val editClass = "edit"
val addClass = "add"
def aClass = if(displayContents.text.equals("")) addClass else editClass
def imgSrc = if(displayContents.text.equals("")) addSrc else editSrc
def displayText = if(displayContents.text.equals("")) defaultValue else displayContents.text
def displayMarkup : NodeSeq = {
displayContents.text match{
case "" => {
<div onclick={setAndSwap(editName, editMarkup, dispName).toJsCmd + " return false;"}><a href="#" class={addClass}>{
" " ++ displayText}</a></div>
}
case _ => {
<div>
<a href="#" class={editClass} onclick={setAndSwap(editName, editMarkup, dispName).toJsCmd + " return false;"}/>
<span class="text">{displayContents}</span>
</div>
}
}
}
def editMarkup : NodeSeq = {
val formData : NodeSeq =
editForm ++
<input class="submit" type="image" src="/media/images/submit.png" /> ++
hidden(onSubmit) ++
<input type="image" src="/media/images/cancel.png" onclick={swapJsCmd(dispName,editName).toJsCmd + " return false;"} />
ajaxForm(formData,
Noop,
setAndSwap(dispName, displayMarkup, editName))
}
<div>
<div id={dispName}>
{displayMarkup}
</div>
<div id={editName} style="display: none;">
{editMarkup}
</div>
</div>
}
}
def getMostUpToDateOtherAccount(holder: String) = {
currentAccount.otherAccounts.get.find(o => {
o.holder.get.equals(holder)
@ -144,7 +77,7 @@ class Management {
}
}
CustomSHtml.ajaxEditable(<span class="text">{currentValue}</span>, SHtml.text(currentValue, currentValue = _), () =>{
CustomEditable.editable(currentValue, SHtml.text(currentValue, currentValue = _), () =>{
saveValue()
Noop
}, defaultValue)
@ -172,7 +105,7 @@ class Management {
val moreInfoSelector = ".information *" #> editableMoreInfo(moreInfo, account)
val imageURLSelector = ".image *" #> editableImageUrl(imageURL, account)
val imageURLSelector = ".imageURL *" #> editableImageUrl(imageURL, account)
(accountSelector &
publicSelector &

View File

@ -64,7 +64,6 @@ class OBPTransactionSnippet extends StatefulSnippet with PaginatorSnippet[OBPEnv
}
var dispatch: DispatchIt = {
case "showAll" => showAll _
case "paginate" => paginate _
case "display" => display _
//case "top" => top _
@ -85,15 +84,32 @@ class OBPTransactionSnippet extends StatefulSnippet with PaginatorSnippet[OBPEnv
val date1 = e1.obp_transaction.get.details.get.mediated_completed(consumer) getOrElse new Date()
val date2 = e2.obp_transaction.get.details.get.mediated_completed(consumer) getOrElse new Date()
date1.after(date2)
}
}
def hasSameDate(e1: OBPEnvelope, e2: OBPEnvelope) : Boolean = {
val t1 = e1.obp_transaction.get
val t2 = e2.obp_transaction.get
t1.details.get.completed.get.equals(t2.details.get.completed.get)
}
def hasSameDate(e1: OBPEnvelope, e2: OBPEnvelope) : Boolean = {
val t1 = e1.obp_transaction.get
val t2 = e2.obp_transaction.get
t1.details.get.completed.get.equals(t2.details.get.completed.get)
}
def editableNarrative(envelope : OBPEnvelope) = {
var narrative = envelope.narrative.get
CustomEditable.editable(narrative, SHtml.text(narrative, narrative = _), () => {
//save the narrative
envelope.narrative(narrative).save
Noop
}, "Narrative")
}
def displayNarrative(envelope : OBPEnvelope): NodeSeq = {
consumer match {
case "my-view" => editableNarrative(envelope)
case _ => Text(envelope.mediated_narrative(consumer).getOrElse(FORBIDDEN))
}
}
/**
* Splits a list of envelopes into a list of lists, where each of these new lists
* is for one day.
@ -148,21 +164,6 @@ class OBPTransactionSnippet extends StatefulSnippet with PaginatorSnippet[OBPEnv
var narrative = env.narrative.get
def editableNarrative() = {
SHtml.ajaxEditable(Text(narrative), SHtml.text(narrative, narrative = _), ()=> {
//save the narrative
env.narrative(narrative).save
Noop
})
}
def displayNarrative() : NodeSeq = {
consumer match{
case "my-view" => editableNarrative()
case _ => Text(env.mediated_narrative(consumer).getOrElse(FORBIDDEN))
}
}
val theAccount = thisAccount.theAccount
val otherUnmediatedHolder = otherAccount.holder.get
val otherMediatedHolder = otherAccount.mediated_holder(consumer)
@ -222,11 +223,13 @@ class OBPTransactionSnippet extends StatefulSnippet with PaginatorSnippet[OBPEnv
else ".alias_image [src]" #> {aliasImageSrc}
} &
{
if(aliasImageSrc.equals("media/images/public_alias.png")){
if(aliasImageSrc.equals("/media/images/public_alias.png")){
//don't show more info if there is a public alias
".narrative *" #> NodeSeq.Empty &
".extra *" #> NodeSeq.Empty
} else {
//show it otherwise
".narrative *" #> displayNarrative(env) &
".other_account_more_info *" #> moreInfo &
".other_account_logo_img [src]" #> logoImageSrc &
{
@ -250,154 +253,6 @@ class OBPTransactionSnippet extends StatefulSnippet with PaginatorSnippet[OBPEnv
}
def showAll(xhtml: NodeSeq): NodeSeq = {
val consumer = S.uri match{
case uri if uri.endsWith("authorities") => "authorities"
case uri if uri.endsWith("board") => "board"
case uri if uri.endsWith("our-network") => "our-network"
case uri if uri.endsWith("team") => "team"
case uri if uri.endsWith("my-view") => "my-view"
case _ => "anonymous"
}
def orderByDateDescending = (e1: OBPEnvelope, e2: OBPEnvelope) => {
val date1 = e1.obp_transaction.get.details.get.mediated_completed(consumer) getOrElse new Date()
val date2 = e2.obp_transaction.get.details.get.mediated_completed(consumer) getOrElse new Date()
date1.after(date2)
}
val envelopes = page.sort(orderByDateDescending)
envelopes.flatMap(obpEnvelope => {
val FORBIDDEN = "---"
val dateFormat = new SimpleDateFormat("MMM dd yyyy")
val envelopeID = obpEnvelope.id
val transaction = obpEnvelope.obp_transaction.get
val transactionDetails = transaction.details.get
val transactionValue = transactionDetails.value.get
val thisAccount = transaction.this_account.get
val otherAccount = transaction.other_account.get
def formatDate(date : Box[Date]) : String = {
date match{
case Full(d) => dateFormat.format(d)
case _ => FORBIDDEN
}
}
var narrative = obpEnvelope.narrative.get
def editableNarrative() = {
SHtml.ajaxEditable(Text(narrative), SHtml.text(narrative, narrative = _), ()=> {
//save the narrative
obpEnvelope.narrative(narrative).save
Noop
})
}
def displayNarrative() : NodeSeq = {
consumer match{
case "my-view" => editableNarrative()
case _ => Text(obpEnvelope.mediated_narrative(consumer).getOrElse(FORBIDDEN))
}
}
val theAccount = thisAccount.theAccount
val otherUnmediatedHolder = otherAccount.holder.get
val otherMediatedHolder = otherAccount.mediated_holder(consumer)
val aliasImageSrc = {
otherMediatedHolder._2 match{
case Full(APublicAlias) => "/images/public_alias.png"
case Full(APrivateAlias) => "/images/private_alias.png"
case _ => ""
}
}
val moreInfo = {
val moreInfo = for{
a <- theAccount
oacc <- a.otherAccounts.get.find(o => otherUnmediatedHolder.equals(o.holder.get))
} yield oacc.moreInfo.get
moreInfo getOrElse ""
}
val logoImageSrc = {
val imageUrl = for{
a <- theAccount
oacc <- a.otherAccounts.get.find(o => otherUnmediatedHolder.equals(o.holder.get))
} yield oacc.imageUrl.get
imageUrl getOrElse ""
}
val otherAccWebsiteUrl = {
val url = for{
a <- theAccount
oacc <- a.otherAccounts.get.find(o => otherUnmediatedHolder.equals(o.holder.get))
} yield oacc.url.get
url getOrElse ""
}
val openCorporatesUrl = {
val theUrl = for{
a <- theAccount
oacc <- a.otherAccounts.get.find(o => otherUnmediatedHolder.equals(o.holder.get))
} yield oacc.openCorporatesUrl.get
theUrl getOrElse("")
}
(
".amount *" #> transactionValue.mediated_amount(consumer).getOrElse(FORBIDDEN) &
".other_account_holder_name *" #> otherMediatedHolder._1.getOrElse(FORBIDDEN) &
{
if(aliasImageSrc.equals("")){
".alias_image" #> NodeSeq.Empty & //remove the img tag
".alias_divider" #> NodeSeq.Empty //remove the divider (br tag)
}
else ".alias_image [src]" #> aliasImageSrc
} &
{
//TODO: This was hacked minutes before for a demo. Needs to be redone.
if(aliasImageSrc.equals("/images/public_alias.png")){
//don't show more info if there is a public alias
".other_account_more_info *" #> NodeSeq.Empty &
".other_account_logo_img" #> NodeSeq.Empty &
".other_acc_link" #> NodeSeq.Empty &
".open_corporates_link" #> NodeSeq.Empty
}else{
//show it otherwise
".other_account_more_info *" #> moreInfo &
".other_account_logo_img [src]" #> logoImageSrc &
{
if(otherAccWebsiteUrl.equals("")) ".other_acc_link" #> NodeSeq.Empty //If there is no link to display, don't render the <a> element
else".other_acc_link [href]" #> otherAccWebsiteUrl
} &
{
if(openCorporatesUrl.equals("")) ".open_corporates_link" #> NodeSeq.Empty
else ".open_corporates_link [href]" #> openCorporatesUrl
}
}
} &
".currency *" #> transactionValue.mediated_currency(consumer).getOrElse(FORBIDDEN) &
".date_cleared *" #> formatDate(transactionDetails.mediated_posted(consumer))&
".narrative *" #> displayNarrative &
".new_balance *" #> {
transactionDetails.new_balance.get.mediated_amount(consumer).getOrElse(FORBIDDEN) + " " +
transactionDetails.new_balance.get.mediated_currency(consumer).getOrElse(FORBIDDEN)} &
".comments_ext [href]" #> {consumer + "/transactions/" + envelopeID + "/comments"} &
".comments_title *" #> {"Comments (" + (obpEnvelope.mediated_comments(consumer) getOrElse List()).size + ")"}
).apply(xhtml)
})
}
}

View File

@ -23,16 +23,10 @@
<tr class=lift:Management.showAll>
<td class="account">Bob Smith</td>
<td class="public">
<!-- IF NO PUBLIC ALIAS -->
<div class="public_not_set">
<a class="add_public add" href="#">Public alias</a>
</div> <!-- ELSE PUBLIC ALIAS -->
<div class="public_set">
<a class="edit" href="#">[edit]</a> <span
class="edit_public text">Software Developer A</span>
</div>
Edit interface gets bound here
</td>
<td class="private">
Edit interface gets bound here
<!-- IF NO PRIVATE ALIAS
<div class="private_not_set">
<a class="add_private add" href="#">Private alias</a>
@ -43,6 +37,7 @@
</div> -->
</td>
<td class="website">
Edit interface gets bound here
<!-- IF NO WEBSITE
<div class="website_not_set">
<a class="add_website add" href="#">Website</a>
@ -53,6 +48,7 @@
</div> -->
</td>
<td class="open_corporates">
Edit interface gets bound here
<!-- IF NO OPEN CORPORATES
<div class="open_corporates_not_set">
<a class="add_open_corporates add" href="#">Open Corporates URL</a>
@ -63,6 +59,7 @@
</div> -->
</td>
<td class="information">
Edit interface gets bound here
<!-- IF NO INFORMATION
<div class="information_not_set">
<a class="add_information add" href="#">Information</a>
@ -72,7 +69,8 @@
<a class="edit" href="#">[edit]</a> <span class="edit_information text">Blah blah</span>
</div> -->
</td>
<td class="image">
<td class="imageURL">
Edit interface gets bound here
<!-- IF NO IMAGE
<a class="add_image upload" href="#">Upload image</a>
<!-- ELSE IMAGE

View File

@ -22,6 +22,52 @@ a.sort {
text-indent: -9999em;
}
a.add {
display: inline-block;
padding: 0 0 0 20px;
background: transparent url(../images/add-off.png) no-repeat 0 0;
line-height: 16px;
font-size: 12px;
text-decoration: none;
color: #999999;
}
a.add:hover {
background-image: url(../images/add-on.png);
text-decoration: underline;
color: #666666;
}
a.edit {
overflow: hidden;
display: inline-block;
width: 16px;
height: 16px;
background: transparent url(../images/edit-off.png) no-repeat 0 0;
text-indent: -9999em;
}
a.edit:hover {
background-image: url(../images/edit-on.png);
}
input.edit {
float: left;
vertical-align: middle;
background: #ffffff;
border: 1px solid #aaaaaa;
border-radius: 2px;
padding: 1px;
font-size: 10px;
width: 60%;
}
input.submit {
float: left;
margin: 0 0 0 2px;
cursor: pointer;
}
#wrapper {
width: 940px;
margin: 0 auto;
@ -265,53 +311,7 @@ table.management th a.sort {
margin: 0 0 0 3px;
}
table.management td a.add {
display: inline-block;
padding: 0 0 0 20px;
background: transparent url(../images/add-off.png) no-repeat 0 0;
line-height: 16px;
font-size: 12px;
text-decoration: none;
color: #999999;
}
table.management td a.add:hover {
background-image: url(../images/add-on.png);
text-decoration: underline;
color: #666666;
}
table.management td a.edit {
overflow: hidden;
display: inline-block;
width: 16px;
height: 16px;
background: transparent url(../images/edit-off.png) no-repeat 0 0;
text-indent: -9999em;
}
table.management td a.edit:hover {
background-image: url(../images/edit-on.png);
}
table.management td input.edit {
float: left;
vertical-align: middle;
background: #ffffff;
border: 1px solid #aaaaaa;
border-radius: 2px;
padding: 1px;
font-size: 10px;
width: 60%;
}
table.management td input.submit {
float: left;
margin: 0 0 0 2px;
cursor: pointer;
}
table.management td a.upload {
td a.upload {
display: inline-block;
padding: 5px 5px 6px 30px;
border: 1px dotted #cccccc;

View File

@ -4,7 +4,7 @@
<thead>
<tr>
<th colspan="6">
<th colspan="7">
<h5 class="date">March 10, 2012</h5>
<h6 class="balance">
<span class="text">Balance</span> <span
@ -16,13 +16,18 @@
<tbody>
<tr class="transaction_row">
<!-- Money going out -->
<td class="icon"><div class="out">Out</div></td>
<td class="symbol">-</td>
<!-- Money coming in -->
<!-- <td class="icon"><div class="out">Out</div></td> -->
<!-- <td class="symbol">-</td> -->
<td class="amount">€24.32</td>
<td class="name"><img class="alias_image"
src="/images/blank.gif" /> <br class="alias_divider" /> <span
class="the_name">Ronald Montgomery</span>
</td>
<td class="narrative">Transaction specific notes</td>
<td class="extra"><img src="/media/images/blank.gif"
class="other_account_logo_img"
style="max-width: 200px; max-height: 100px" /> <span

View File

@ -24,99 +24,71 @@ Open Bank Project (http://www.openbankproject.com)
Simon Redfern : simon AT tesobe DOT com
Everett Sochowski: everett AT tesobe DOT com
-->
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift="http://liftweb.net/">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<meta name="description" content="" />
<meta name="keywords" content="" />
<title class="lift:Menu.title">Open Bank Project: </title>
<style class="lift:CSS.blueprint"></style>
<style class="lift:CSS.fancyType"></style>
<script id="jquery" src="/classpath/jquery.js" type="text/javascript"></script>
<script id="json" src="/classpath/json.js" type="text/javascript"></script>
<style type="text/css">
/* <![CDATA[ */
.edit_error_class {
display: block;
color: red;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Open Bank Project</title>
.sidebar ul.menu {
margin:0;
padding:0;
/*border-bottom:1px solid #ccc; */
}
<link href="/media/css/website.css" rel="stylesheet" type="text/css" />
<script src="/media/js/website.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
.sidebar ul.menu li {
margin:0;
padding:0;
list-style:none;
/*border:1px solid #ccc;
border-bottom:none; */
}
</head>
<body>
<div id="wrapper">
.sidebar ul.menu li a {
display:block;
padding:3px;
text-indent:30px;
text-decoration:none;
}
<div id="header">
.sidebar ul.menu li span {
display:block;
padding:3px;
text-indent:30px;
text-decoration:none;
}
<h1 id="logo">Open Bank Project</h1>
.sidebar ul.menu li a:hover {
background-color: #eee;
}
<div id="account">
.sidebar ul.menu ul {
padding-left: 15px;
}
<!-- LOGGED OUT -->
<div class="lift:Login.loggedOut">
<form class="login" action="." method="post">
<div class="field username">
<label>Username</label>
<input class="username" type="text" name="username" />
</div>
<div class="field password">
<label>Password</label>
<a href="#" class="forgot">Forgotten?</a>
<input class="password" type="password" name="password" />
</div>
<div class="button submit">
<input class="submit" type="submit" value="Login" />
</div>
</form>
</div>
<!-- LOGGED IN -->
<div class="lift:Login.loggedIn">
<div class="profiles">
<h2 class="text">Tesobe</h2>
</div>
<a href="" class="logout">Logout</a>
</div>
</div>
h1.logo {
margin: 10px;
margin-left: 29px;
}
</div>
<div id="nav">
<ul>
<li class="lift:Nav.group?group=owner navitem">
<a class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
</li>
<li class="lift:Nav.group?group=views navitem">
<a class="navlink" href="#">Link name. Has class "selected" if it's the current page.</a>
</li>
</ul>
</div>
.alias_image {
margin-right:10px;
}
<div id="content">
The main content gets bound here
</div>
<div id="footer">
<p><a href="#">Open Bank Project</a> is &copy;2011-2012 TESOBE and distributed under the Apache 2.0 License.</p>
</div>
/* ]]> */
</style>
</head>
<body>
<div class="container">
<div class="column last">
<h1 class="logo"><a href="/index"><img src="/images/OpenBankProject_logo.jpg" width="120px"></a> <img alt="" id="ajax-loader" style="display:none; margin-bottom: 0px; margin-left: 5px" src="/images/ajax-loader.gif"></h1>
</div>
<hr>
<div class="column span-6 colborder sidebar">
<hr class="space" >
<!-- <span class="lift:Menu.builder ul:class='menu'"></span> -->
<lift:Menu.builder ul:class="menu" />
<div class="lift:Msgs?showAll=true"></div>
<hr class="space" />
</div>
<div class="column span-17 last">
<div id="content">The main content will get bound here</div>
</div>
<hr />
<div class="column span-23 last" style="text-align: center">
<h4 class="alt">
<a href="http://www.openbankproject.com"><i>Open Bank Project</i></a>
is ©2011-2012 TESOBE and distributed under the Apache 2.0 License.</h4>
</div>
</div>
</body>
</html>
</body>