web: cleanup, enable jquery, use auto-completing combo fields on add form

This commit is contained in:
Simon Michael 2010-08-10 00:13:47 +00:00
parent f3ffef2d8a
commit 8c8395778c
4 changed files with 415 additions and 234 deletions

View File

@ -12,6 +12,7 @@ import System.FilePath ((</>), takeFileName)
import System.IO.Storage (withStore, putValue, getValue) import System.IO.Storage (withStore, putValue, getValue)
import Text.ParserCombinators.Parsec (parse) import Text.ParserCombinators.Parsec (parse)
import Yesod import Yesod
import Yesod.Helpers.Static
import Hledger.Cli.Commands.Add (journalAddTransaction) import Hledger.Cli.Commands.Add (journalAddTransaction)
import Hledger.Cli.Commands.Balance import Hledger.Cli.Commands.Balance
@ -28,36 +29,43 @@ import Paths_hledger_make (getDataFileName)
#else #else
import Paths_hledger (getDataFileName) import Paths_hledger (getDataFileName)
#endif #endif
-- import Hledger.Cli.Commands.Web.Templates
defhost = "localhost" defhost = "localhost"
defport = 5000 defport = 5000
defbaseurl = printf "http://%s:%d" defhost defport :: String
browserstartdelay = 100000 -- microseconds browserstartdelay = 100000 -- microseconds
hledgerurl = "http://hledger.org" hledgerorgurl = "http://hledger.org"
manualurl = hledgerurl++"/MANUAL.html" manualurl = hledgerorgurl++"/MANUAL.html"
data HledgerWebApp = HledgerWebApp { data HledgerWebApp = HledgerWebApp {
appRoot :: String appRoot :: String
,appWebdir :: FilePath ,appDir :: FilePath
,appOpts :: [Opt] ,appOpts :: [Opt]
,appArgs :: [String] ,appArgs :: [String]
,appJournal :: Journal ,appJournal :: Journal
,appStatic :: Static
} }
mkYesod "HledgerWebApp" [$parseRoutes| mkYesod "HledgerWebApp" [$parseRoutes|
/ IndexPage GET /static StaticR Static appStatic
/style.css StyleCss GET / IndexR GET
/journalonly JournalOnlyPage GET POST /journalonly JournalOnlyR GET POST
/registeronly RegisterOnlyPage GET /registeronly RegisterOnlyR GET
/accounts AccountsPage GET /accounts AccountsOnlyR GET
/journal AccountsJournalPage GET POST /journal JournalR GET POST
/register AccountsRegisterPage GET POST /register RegisterR GET POST
|] |]
style_css = StaticRoute ["style.css"]
hledger_js = StaticRoute ["hledger.js"]
jquery_js = StaticRoute ["jquery.js"]
dhtmlxcommon_js = StaticRoute ["dhtmlxcommon.js"]
dhtmlxcombo_js = StaticRoute ["dhtmlxcombo.js"]
instance Yesod HledgerWebApp where approot = appRoot instance Yesod HledgerWebApp where approot = appRoot
defaultpage = AccountsJournalPage defaultroute = JournalR
-- | A bundle of useful data passed to templates. -- | A bundle of useful data passed to templates.
data TemplateData = TD { data TemplateData = TD {
@ -71,7 +79,7 @@ data TemplateData = TD {
} }
mktd = TD { mktd = TD {
here = IndexPage here = IndexR
,title = "hledger" ,title = "hledger"
,msg = Nothing ,msg = Nothing
,a = "" ,a = ""
@ -83,8 +91,9 @@ mktd = TD {
-- | The web command. -- | The web command.
web :: [Opt] -> [String] -> Journal -> IO () web :: [Opt] -> [String] -> Journal -> IO ()
web opts args j = do web opts args j = do
let baseurl = fromMaybe defbaseurl $ baseUrlFromOpts opts let host = defhost
port = fromMaybe defport $ portFromOpts opts port = fromMaybe defport $ portFromOpts opts
baseurl = fromMaybe (printf "http://%s:%d" host port) $ baseUrlFromOpts opts
unless (Debug `elem` opts) $ forkIO (browser baseurl) >> return () unless (Debug `elem` opts) $ forkIO (browser baseurl) >> return ()
server baseurl port opts args j server baseurl port opts args j
@ -98,10 +107,11 @@ browser baseurl = do
server :: String -> Int -> [Opt] -> [String] -> Journal -> IO () server :: String -> Int -> [Opt] -> [String] -> Journal -> IO ()
server baseurl port opts args j = do server baseurl port opts args j = do
printf "starting web server on port %d with base url %s\n" port baseurl printf "starting web server on port %d with base url %s\n" port baseurl
fp <- getDataFileName "web" dir <- getDataFileName "web"
let app = HledgerWebApp{ let app = HledgerWebApp{
appRoot=baseurl appRoot=baseurl
,appWebdir=fp ,appDir=dir
,appStatic=fileLookupDir (dir </> "static") $ typeByExt -- ++[("hamlet","text/plain")]
,appOpts=opts ,appOpts=opts
,appArgs=args ,appArgs=args
,appJournal=j ,appJournal=j
@ -156,22 +166,14 @@ getHandlerParameters = do
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- handlers & templates -- handlers & templates
getStyleCss :: Handler HledgerWebApp () getIndexR :: Handler HledgerWebApp ()
getStyleCss = do getIndexR = redirect RedirectTemporary defaultroute
app <- getYesod
let dir = appWebdir app
sendFile "text/css" $ dir </> "style.css"
----------------------------------------------------------------------
getIndexPage :: Handler HledgerWebApp ()
getIndexPage = redirect RedirectTemporary defaultpage
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- | A combined accounts and journal view. -- | A combined accounts and journal view.
getAccountsJournalPage :: Handler HledgerWebApp RepHtml getJournalR :: Handler HledgerWebApp RepHtml
getAccountsJournalPage = do getJournalR = do
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
today <- liftIO getCurrentDay today <- liftIO getCurrentDay
-- app <- getYesod -- app <- getYesod
@ -183,11 +185,10 @@ getAccountsJournalPage = do
td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today} td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
editform' = editform td $ jtext j editform' = editform td $ jtext j
hamletToRepHtml $ pageLayout td [$hamlet| hamletToRepHtml $ pageLayout td [$hamlet|
^scripts^
%div.ledger %div.ledger
%div.accounts!style=float:left; ^br^ %div.accounts!style=float:left; ^br^
^navlinks.td^ ^navlinks.td^
^addform^ ^addform.td^
^editform'^ ^editform'^
^importform^ ^importform^
%div#transactions.journal %div#transactions.journal
@ -195,14 +196,14 @@ getAccountsJournalPage = do
^jr^ ^jr^
|] |]
postAccountsJournalPage :: Handler HledgerWebApp RepPlain postJournalR :: Handler HledgerWebApp RepPlain
postAccountsJournalPage = postJournalOnlyPage postJournalR = postJournalOnlyR
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- | A combined accounts and register view. -- | A combined accounts and register view.
getAccountsRegisterPage :: Handler HledgerWebApp RepHtml getRegisterR :: Handler HledgerWebApp RepHtml
getAccountsRegisterPage = do getRegisterR = do
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
today <- liftIO getCurrentDay today <- liftIO getCurrentDay
-- app <- getYesod -- app <- getYesod
@ -215,11 +216,10 @@ getAccountsRegisterPage = do
td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today} td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
editform' = editform td $ jtext j editform' = editform td $ jtext j
hamletToRepHtml $ pageLayout td [$hamlet| hamletToRepHtml $ pageLayout td [$hamlet|
^scripts^
%div.ledger %div.ledger
%div.accounts!style=float:left; ^br^ %div.accounts!style=float:left; ^br^
^navlinks.td^ ^navlinks.td^
^addform^ ^addform.td^
^editform'^ ^editform'^
^importform^ ^importform^
%div#transactions.register %div#transactions.register
@ -227,14 +227,14 @@ getAccountsRegisterPage = do
^rr^ ^rr^
|] |]
postAccountsRegisterPage :: Handler HledgerWebApp RepPlain postRegisterR :: Handler HledgerWebApp RepPlain
postAccountsRegisterPage = postJournalOnlyPage postRegisterR = postJournalOnlyR
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- | A simple accounts and balances view like hledger balance. -- | A simple accounts and balances view like hledger balance.
getAccountsPage :: Handler HledgerWebApp RepHtml getAccountsOnlyR :: Handler HledgerWebApp RepHtml
getAccountsPage = do getAccountsOnlyR = do
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
today <- liftIO getCurrentDay today <- liftIO getCurrentDay
let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today} let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
@ -308,8 +308,8 @@ isAccountRegex s = take 1 s == "^" && (take 5 $ reverse s) == ")$|:("
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- | A basic journal view, like hledger print, with editing. -- | A basic journal view, like hledger print, with editing.
getJournalOnlyPage :: Handler HledgerWebApp RepHtml getJournalOnlyR :: Handler HledgerWebApp RepHtml
getJournalOnlyPage = do getJournalOnlyR = do
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
today <- liftIO getCurrentDay today <- liftIO getCurrentDay
let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today} let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
@ -317,12 +317,11 @@ getJournalOnlyPage = do
txns = journalReportAsHtml opts td $ journalReport opts fspec j txns = journalReportAsHtml opts td $ journalReport opts fspec j
hamletToRepHtml $ pageLayout td [$hamlet| hamletToRepHtml $ pageLayout td [$hamlet|
%div.journal %div.journal
^scripts^
%div.nav2 %div.nav2
%a#addformlink!href!onclick="return addformToggle()" add one transaction %a#addformlink!href!onclick="return addformToggle()" add one transaction
\ | $ \ | $
%a#editformlink!href!onclick="return editformToggle()" edit the whole journal %a#editformlink!href!onclick="return editformToggle()" edit the whole journal
^addform^ ^addform.td^
^editform'^ ^editform'^
#transactions ^txns^ #transactions ^txns^
|] |]
@ -346,9 +345,25 @@ journalReportAsHtml _ td items = [$hamlet|
evenodd = if even n then "even" else "odd" evenodd = if even n then "even" else "odd"
txn = trimnl $ showTransaction t where trimnl = reverse . dropWhile (=='\n') . reverse txn = trimnl $ showTransaction t where trimnl = reverse . dropWhile (=='\n') . reverse
addform :: Hamlet HledgerWebAppRoute addform :: TemplateData -> Hamlet HledgerWebAppRoute
addform = [$hamlet| addform td = [$hamlet|
%form#addform!method=POST!style=display:none; %script!type=text/javascript
$$(document).ready(function() {
/* dhtmlxcombo setup */
window.dhx_globalImgPath="../static/images/";
var desccombo = new dhtmlXCombo("description");
var acct1combo = new dhtmlXCombo("account1");
var acct2combo = new dhtmlXCombo("account2");
desccombo.enableFilteringMode(true);
acct1combo.enableFilteringMode(true);
acct2combo.enableFilteringMode(true);
desccombo.setSize(300);
acct1combo.setSize(300);
acct2combo.setSize(300);
/* desccombo.enableOptionAutoHeight(true, 20); */
/* desccombo.setOptionHeight(200); */
});
%form#addform!method=POST!style=display:none;
%table.form %table.form
%tr %tr
%td!colspan=4 %td!colspan=4
@ -361,7 +376,10 @@ addform = [$hamlet|
%td!style=padding-left:1em; %td!style=padding-left:1em;
Description: Description:
%td %td
%input.textinput!size=35!name=description!value=$desc$ %select!id=description!name=description
%option
$forall descriptions d
%option!value=$d$ $d$
%tr.helprow %tr.helprow
%td %td
%td %td
@ -369,8 +387,7 @@ addform = [$hamlet|
%td %td
%td %td
.help $deschelp$ .help $deschelp$
^transactionfields1^ ^postingsfields.td^
^transactionfields2^
%tr#addbuttonrow %tr#addbuttonrow
%td!colspan=4 %td!colspan=4
%input!type=hidden!name=action!value=add %input!type=hidden!name=action!value=add
@ -378,20 +395,29 @@ addform = [$hamlet|
|] |]
where where
-- datehelplink = helplink "dates" "..." -- datehelplink = helplink "dates" "..."
datehelp = "eg: 7/20, 2010/1/1, " datehelp = "eg: 2010/7/20"
deschelp = "eg: supermarket (optional)" deschelp = "eg: supermarket (optional)"
date = "today" date = "today"
desc = "" descriptions = sort $ nub $ map tdescription $ jtxns $ j td
transactionfields1 = transactionfields 1
transactionfields2 = transactionfields 2
transactionfields :: Int -> Hamlet HledgerWebAppRoute postingsfields :: TemplateData -> Hamlet HledgerWebAppRoute
transactionfields n = [$hamlet| postingsfields td = [$hamlet|
^p1^
^p2^
|]
where
p1 = postingfields td 1
p2 = postingfields td 2
postingfields :: TemplateData -> Int -> Hamlet HledgerWebAppRoute
postingfields td n = [$hamlet|
%tr#postingrow %tr#postingrow
%td!align=right %td!align=right $acctlabel$:
$label$:
%td %td
%input.textinput!size=35!name=$acctvar$!value=$acct$ %select!id=$acctvar$!name=$acctvar$
%option
$forall acctnames a
%option!value=$a$ $a$
^amtfield^ ^amtfield^
%tr.helprow %tr.helprow
%td %td
@ -402,24 +428,26 @@ transactionfields n = [$hamlet|
.help $amthelp$ .help $amthelp$
|] |]
where where
label | n == 1 = "To account" numbered = (++ show n)
| otherwise = "From account" acctvar = numbered "account"
accthelp | n == 1 = "eg: expenses:food" amtvar = numbered "amount"
| otherwise = "eg: assets:bank:checking" acctnames = sort $ journalAccountNamesUsed $ j td
amtfield | n == 1 = [$hamlet| (acctlabel, accthelp, amtfield, amthelp)
| n == 1 = ("To account"
,"eg: expenses:food"
,[$hamlet|
%td!style=padding-left:1em; %td!style=padding-left:1em;
Amount: Amount:
%td %td
%input.textinput!size=15!name=$amtvar$!value=$amt$ %input.textinput!size=15!name=$amtvar$!value=""
|] |]
| otherwise = nulltemplate ,"eg: $6"
amthelp | n == 1 = "eg: 5, $6, €7.01" )
| otherwise = "" | otherwise = ("From account"
acct = "" ,"eg: assets:bank:checking"
amt = "" ,nulltemplate
numbered = (++ show n) ,""
acctvar = numbered "accountname" )
amtvar = numbered "amount"
editform :: TemplateData -> String -> Hamlet HledgerWebAppRoute editform :: TemplateData -> String -> Hamlet HledgerWebAppRoute
editform _ content = [$hamlet| editform _ content = [$hamlet|
@ -455,142 +483,8 @@ importform = [$hamlet|
%a!href!onclick="return importformToggle()" cancel %a!href!onclick="return importformToggle()" cancel
|] |]
scripts = [$hamlet| postJournalOnlyR :: Handler HledgerWebApp RepPlain
<script type="text/javascript"> postJournalOnlyR = do
function filterformToggle() {
var a = document.getElementById('addform');
var e = document.getElementById('editform');
var f = document.getElementById('filterform');
var i = document.getElementById('importform');
var t = document.getElementById('transactions');
var alink = document.getElementById('addformlink');
var elink = document.getElementById('editformlink');
var flink = document.getElementById('filterformlink');
var ilink = document.getElementById('importformlink');
var jlink = document.getElementById('journallink');
var rlink = document.getElementById('registerlink');
if (f.style.display == 'none') {
flink.style['font-weight'] = 'bold';
f.style.display = 'block';
} else {
flink.style['font-weight'] = 'normal';
f.style.display = 'none';
}
return false;
}
function addformToggle() {
var a = document.getElementById('addform');
var e = document.getElementById('editform');
var f = document.getElementById('filterform');
var i = document.getElementById('importform');
var t = document.getElementById('transactions');
var alink = document.getElementById('addformlink');
var elink = document.getElementById('editformlink');
var flink = document.getElementById('filterformlink');
var ilink = document.getElementById('importformlink');
var jlink = document.getElementById('journallink');
var rlink = document.getElementById('registerlink');
if (a.style.display == 'none') {
alink.style['font-weight'] = 'bold';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'normal';
jlink.style['font-weight'] = 'normal';
rlink.style['font-weight'] = 'normal';
a.style.display = 'block';
e.style.display = 'none';
i.style.display = 'none';
t.style.display = 'none';
} else {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'none';
i.style.display = 'none';
t.style.display = 'block';
}
return false;
}
function editformToggle() {
var a = document.getElementById('addform');
var e = document.getElementById('editform');
var f = document.getElementById('filterform');
var i = document.getElementById('importform');
var t = document.getElementById('transactions');
var alink = document.getElementById('addformlink');
var elink = document.getElementById('editformlink');
var flink = document.getElementById('filterformlink');
var ilink = document.getElementById('importformlink');
var jlink = document.getElementById('journallink');
var rlink = document.getElementById('registerlink');
if (e.style.display == 'none') {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'bold';
ilink.style['font-weight'] = 'normal';
jlink.style['font-weight'] = 'normal';
rlink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'block';
i.style.display = 'none';
t.style.display = 'none';
} else {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'none';
i.style.display = 'none';
t.style.display = 'block';
}
return false;
}
function importformToggle() {
var a = document.getElementById('addform');
var e = document.getElementById('editform');
var f = document.getElementById('filterform');
var i = document.getElementById('importform');
var t = document.getElementById('transactions');
var alink = document.getElementById('addformlink');
var elink = document.getElementById('editformlink');
var flink = document.getElementById('filterformlink');
var ilink = document.getElementById('importformlink');
var jlink = document.getElementById('journallink');
var rlink = document.getElementById('registerlink');
if (i.style.display == 'none') {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'bold';
jlink.style['font-weight'] = 'normal';
rlink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'none';
i.style.display = 'block';
t.style.display = 'none';
} else {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'none';
i.style.display = 'none';
t.style.display = 'block';
}
return false;
}
</script>
|]
postJournalOnlyPage :: Handler HledgerWebApp RepPlain
postJournalOnlyPage = do
action <- runFormPost' $ maybeStringInput "action" action <- runFormPost' $ maybeStringInput "action"
case action of Just "edit" -> postEditForm case action of Just "edit" -> postEditForm
Just "import" -> postImportForm Just "import" -> postImportForm
@ -606,9 +500,9 @@ postAddForm = do
$ (,,,,,) $ (,,,,,)
<$> maybeStringInput "date" <$> maybeStringInput "date"
<*> maybeStringInput "description" <*> maybeStringInput "description"
<*> maybeStringInput "accountname1" <*> maybeStringInput "account1"
<*> maybeStringInput "amount1" <*> maybeStringInput "amount1"
<*> maybeStringInput "accountname2" <*> maybeStringInput "account2"
<*> maybeStringInput "amount2" <*> maybeStringInput "amount2"
-- supply defaults and parse date and amounts, or get errors. -- supply defaults and parse date and amounts, or get errors.
let dateE = maybe (Left "date required") (either (\e -> Left $ showDateParseError e) Right . fixSmartDateStrEither today) dateM let dateE = maybe (Left "date required") (either (\e -> Left $ showDateParseError e) Right . fixSmartDateStrEither today) dateM
@ -643,14 +537,14 @@ postAddForm = do
Left errs -> do Left errs -> do
-- save current form values in session -- save current form values in session
setMessage $ string $ intercalate "; " errs setMessage $ string $ intercalate "; " errs
redirect RedirectTemporary AccountsRegisterPage redirect RedirectTemporary RegisterR
Right t -> do Right t -> do
let t' = txnTieKnot t -- XXX move into balanceTransaction let t' = txnTieKnot t -- XXX move into balanceTransaction
j <- liftIO $ fromJust `fmap` getValue "hledger" "journal" j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
liftIO $ journalAddTransaction j opts t' liftIO $ journalAddTransaction j opts t'
setMessage $ string $ printf "Added transaction:\n%s" (show t') setMessage $ string $ printf "Added transaction:\n%s" (show t')
redirect RedirectTemporary AccountsRegisterPage redirect RedirectTemporary RegisterR
-- | Handle a journal edit form post. -- | Handle a journal edit form post.
postEditForm :: Handler HledgerWebApp RepPlain postEditForm :: Handler HledgerWebApp RepPlain
@ -663,7 +557,7 @@ postEditForm = do
Left errs -> do Left errs -> do
-- XXX should save current form values in session -- XXX should save current form values in session
setMessage $ string errs setMessage $ string errs
redirect RedirectTemporary AccountsJournalPage redirect RedirectTemporary JournalR
Right t' -> do Right t' -> do
-- try to avoid unnecessary backups or saving invalid data -- try to avoid unnecessary backups or saving invalid data
@ -677,24 +571,24 @@ postEditForm = do
if not changed if not changed
then do then do
setMessage $ string $ "No change" setMessage $ string $ "No change"
redirect RedirectTemporary AccountsJournalPage redirect RedirectTemporary JournalR
else do else do
jE <- liftIO $ journalFromPathAndString Nothing f tnew jE <- liftIO $ journalFromPathAndString Nothing f tnew
either either
(\e -> do (\e -> do
setMessage $ string e setMessage $ string e
redirect RedirectTemporary AccountsJournalPage) redirect RedirectTemporary JournalR)
(const $ do (const $ do
liftIO $ writeFileWithBackup f tnew liftIO $ writeFileWithBackup f tnew
setMessage $ string $ printf "Saved journal %s\n" (show f) setMessage $ string $ printf "Saved journal %s\n" (show f)
redirect RedirectTemporary AccountsJournalPage) redirect RedirectTemporary JournalR)
jE jE
-- | Handle an import page post. -- | Handle an import page post.
postImportForm :: Handler HledgerWebApp RepPlain postImportForm :: Handler HledgerWebApp RepPlain
postImportForm = do postImportForm = do
setMessage $ string $ "can't handle file upload yet" setMessage $ string $ "can't handle file upload yet"
redirect RedirectTemporary AccountsJournalPage redirect RedirectTemporary JournalR
-- -- get form input values, or basic validation errors. E means an Either value. -- -- get form input values, or basic validation errors. E means an Either value.
-- fileM <- runFormPost' $ maybeFileInput "file" -- fileM <- runFormPost' $ maybeFileInput "file"
-- let fileE = maybe (Left "No file provided") Right fileM -- let fileE = maybe (Left "No file provided") Right fileM
@ -702,17 +596,17 @@ postImportForm = do
-- case fileE of -- case fileE of
-- Left errs -> do -- Left errs -> do
-- setMessage $ string errs -- setMessage $ string errs
-- redirect RedirectTemporary AccountsJournalPage -- redirect RedirectTemporary JournalR
-- Right s -> do -- Right s -> do
-- setMessage $ string $ s -- setMessage $ string $ s
-- redirect RedirectTemporary AccountsJournalPage -- redirect RedirectTemporary JournalR
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- | A simple postings view like hledger register. -- | A simple postings view like hledger register.
getRegisterOnlyPage :: Handler HledgerWebApp RepHtml getRegisterOnlyR :: Handler HledgerWebApp RepHtml
getRegisterOnlyPage = do getRegisterOnlyR = do
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
today <- liftIO getCurrentDay today <- liftIO getCurrentDay
let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today} let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p, j=j, today=today}
@ -753,8 +647,8 @@ mixedAmountAsHtml b = preEscapedString $ addclass $ intercalate "<br>" $ lines $
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- | A standalone journal edit form page. -- | A standalone journal edit form page.
getEditPage :: Handler HledgerWebApp RepHtml getEditR :: Handler HledgerWebApp RepHtml
getEditPage = do getEditR = do
(a, p, _, _, _, msg, here) <- getHandlerParameters (a, p, _, _, _, msg, here) <- getHandlerParameters
today <- liftIO getCurrentDay today <- liftIO getCurrentDay
-- reload journal's text without parsing, if changed -- XXX are we doing this right ? -- reload journal's text without parsing, if changed -- XXX are we doing this right ?
@ -774,7 +668,11 @@ pageLayout td@TD{title=title, msg=msg} content = [$hamlet|
%head %head
%title $title$ %title $title$
%meta!http-equiv=Content-Type!content=$metacontent$ %meta!http-equiv=Content-Type!content=$metacontent$
%link!rel=stylesheet!type=text/css!href=@StyleCss@!media=all %script!type=text/javascript!src=@StaticR.jquery_js@
%script!type=text/javascript!src=@StaticR.dhtmlxcommon_js@
%script!type=text/javascript!src=@StaticR.dhtmlxcombo_js@
%script!type=text/javascript!src=@StaticR.hledger_js@
%link!rel=stylesheet!type=text/css!media=all!href=@StaticR.style_css@
%body %body
^navbar.td^ ^navbar.td^
#messages $m$ #messages $m$
@ -787,7 +685,7 @@ pageLayout td@TD{title=title, msg=msg} content = [$hamlet|
navbar :: TemplateData -> Hamlet HledgerWebAppRoute navbar :: TemplateData -> Hamlet HledgerWebAppRoute
navbar TD{p=p,j=j,today=today} = [$hamlet| navbar TD{p=p,j=j,today=today} = [$hamlet|
#navbar #navbar
%a.topleftlink!href=$hledgerurl$ %a.topleftlink!href=$hledgerorgurl$
hledger hledger
<br /> <br />
$version$ $version$
@ -817,8 +715,8 @@ navlinks td = [$hamlet|
|] |]
-- \ | $ -- \ | $
where where
accountsjournallink = navlink td "journal" AccountsJournalPage accountsjournallink = navlink td "journal" JournalR
accountsregisterlink = navlink td "register" AccountsRegisterPage accountsregisterlink = navlink td "register" RegisterR
navlink :: TemplateData -> String -> HledgerWebAppRoute -> Hamlet HledgerWebAppRoute navlink :: TemplateData -> String -> HledgerWebAppRoute -> Hamlet HledgerWebAppRoute
navlink TD{here=here,a=a,p=p} s dest = [$hamlet|%a#$s$link.$style$!href=@?u@ $s$|] navlink TD{here=here,a=a,p=p} s dest = [$hamlet|%a#$s$link.$style$!href=@?u@ $s$|]

135
data/web/static/hledger.js Normal file
View File

@ -0,0 +1,135 @@
/* hledger web ui javascripts */
/* depends on jquery, other support libs, and additional js inserted inline */
$(document).ready(function() {
});
function filterformToggle() {
var a = document.getElementById('addform');
var e = document.getElementById('editform');
var f = document.getElementById('filterform');
var i = document.getElementById('importform');
var t = document.getElementById('transactions');
var alink = document.getElementById('addformlink');
var elink = document.getElementById('editformlink');
var flink = document.getElementById('filterformlink');
var ilink = document.getElementById('importformlink');
var jlink = document.getElementById('journallink');
var rlink = document.getElementById('registerlink');
if (f.style.display == 'none') {
flink.style['font-weight'] = 'bold';
f.style.display = 'block';
} else {
flink.style['font-weight'] = 'normal';
f.style.display = 'none';
}
return false;
}
function addformToggle() {
var a = document.getElementById('addform');
var e = document.getElementById('editform');
var f = document.getElementById('filterform');
var i = document.getElementById('importform');
var t = document.getElementById('transactions');
var alink = document.getElementById('addformlink');
var elink = document.getElementById('editformlink');
var flink = document.getElementById('filterformlink');
var ilink = document.getElementById('importformlink');
var jlink = document.getElementById('journallink');
var rlink = document.getElementById('registerlink');
if (a.style.display == 'none') {
alink.style['font-weight'] = 'bold';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'normal';
jlink.style['font-weight'] = 'normal';
rlink.style['font-weight'] = 'normal';
a.style.display = 'block';
e.style.display = 'none';
i.style.display = 'none';
t.style.display = 'none';
} else {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'none';
i.style.display = 'none';
t.style.display = 'block';
}
return false;
}
function editformToggle() {
var a = document.getElementById('addform');
var e = document.getElementById('editform');
var f = document.getElementById('filterform');
var i = document.getElementById('importform');
var t = document.getElementById('transactions');
var alink = document.getElementById('addformlink');
var elink = document.getElementById('editformlink');
var flink = document.getElementById('filterformlink');
var ilink = document.getElementById('importformlink');
var jlink = document.getElementById('journallink');
var rlink = document.getElementById('registerlink');
if (e.style.display == 'none') {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'bold';
ilink.style['font-weight'] = 'normal';
jlink.style['font-weight'] = 'normal';
rlink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'block';
i.style.display = 'none';
t.style.display = 'none';
} else {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'none';
i.style.display = 'none';
t.style.display = 'block';
}
return false;
}
function importformToggle() {
var a = document.getElementById('addform');
var e = document.getElementById('editform');
var f = document.getElementById('filterform');
var i = document.getElementById('importform');
var t = document.getElementById('transactions');
var alink = document.getElementById('addformlink');
var elink = document.getElementById('editformlink');
var flink = document.getElementById('filterformlink');
var ilink = document.getElementById('importformlink');
var jlink = document.getElementById('journallink');
var rlink = document.getElementById('registerlink');
if (i.style.display == 'none') {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'bold';
jlink.style['font-weight'] = 'normal';
rlink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'none';
i.style.display = 'block';
t.style.display = 'none';
} else {
alink.style['font-weight'] = 'normal';
elink.style['font-weight'] = 'normal';
ilink.style['font-weight'] = 'normal';
a.style.display = 'none';
e.style.display = 'none';
i.style.display = 'none';
t.style.display = 'block';
}
return false;
}

View File

@ -4,7 +4,7 @@
/* overspecified for cross-browser robustness */ /* overspecified for cross-browser robustness */
body { font-family:helvetica,arial,"sans serif"; } body { font-family:helvetica,arial,"sans serif"; }
pre { font-family:courier,"courier new",monospace; } pre { font-family:courier,"courier new",monospace; }
#addform input.textinput { font-family:courier,"courier new",monospace; font-size:small; } input.textinput, .dhx_combo_input, .dhx_combo_list { font-size:small; }
#editform textarea { font-family:courier,"courier new",monospace; font-size:small; } #editform textarea { font-family:courier,"courier new",monospace; font-size:small; }
.nav2 { font-size:small; } .nav2 { font-size:small; }
#filterform { font-size:small; } #filterform { font-size:small; }
@ -75,9 +75,152 @@ table.registerreport { border-spacing:0; }
.firstposting td { } .firstposting td { }
.registerreport .odd { background-color:#f0f0f0; } .registerreport .odd { background-color:#f0f0f0; }
#addform input.textinput { background-color:#eee; padding:4px; } #addform input.textinput, #addform .dhx_combo_input, .dhx_combo_list { background-color:#eee; padding:4px; }
#addform table { } #addform table { }
#addform #addbuttonrow { text-align:right; } #addform #addbuttonrow { text-align:right; }
/* #editform { width:95%; } */ /* #editform { width:95%; } */
#editform textarea { width:100%; background-color:#eee; padding:4px; } #editform textarea { width:100%; background-color:#eee; padding:4px; }
#filterform table { border-spacing:0; padding-left:1em; } #filterform table { border-spacing:0; padding-left:1em; }
/*------------------------------------------------------------------------------------------*/
.dhx_combo_input{
/* color:#333333; */
/* font-family: Arial; */
/* font-size: 9pt; */
/* border:0px; */
/* padding:2px 2px 2px 2px; */
/* position:absolute; */
/* top:0px; */
}
/* table {border:thin solid red} */
/* div {border:thin solid yellow} */
.dhx_combo_box{
position:relative;
display:inline-block;
/* text-align:left; */
/* height:20px; */
/* _height:22px; */
/* overflow:hidden; */
/* background-color: white; */
}
.dhx_combo_list{
position:absolute;
z-index:230;
overflow-y:auto;
overflow-x:hidden;
white-space:nowrap;
border:1px solid black;
height:50%;
/* background-color: white; */
}
.dhx_combo_list div{
cursor:default;
padding:2px 2px 2px 2px;
}
.dhx_selected_option{
background-color:navy;
color:white;
}
.dhx_combo_img{
/* display:none; */
width:18px;
height:20px;
position:absolute;
top:12px;
right:-10px;
}
.dhx_combo_option_img{
position:relative;
top:1px;
margin:0px;
margin-left:2px;
left:0px;
width:18px; height:18px;
}
/* .combo_dhx_sel{ */
/* .dhx_selected_option{ */
/* background-image: url("../static/images/bg_selection.gif") !important; */
/* background-position: bottom; */
/* background-repeat: repeat-x; */
/* color:black; */
/* } */
/* .dhx_combo_img_rtl{ */
/* position:absolute; */
/* top:0px; */
/* left:1px; */
/* width:17px; */
/* height:20px; */
/* } */
/* .dhx_combo_option_img_rtl{ */
/* float:right; */
/* margin-right :0px; */
/* width:18px; height:18px; */
/* } */
/* .dhx_combo_list_rtl{ */
/* direction: rtl; */
/* unicode-bidi : bidi-override; */
/* position:absolute; */
/* z-index:230; */
/* overflow-y:auto; */
/* overflow-x:hidden; */
/* border:1px solid black; */
/* height:100px; */
/* /\* font-family: Arial; *\/ */
/* font-size: 9pt; */
/* background-color: white; */
/* } */
/* .dhx_combo_list_rtl div{ */
/* direction: rtl; */
/* unicode-bidi : bidi-override; */
/* padding:2px 2px 2px 2px; */
/* } */
/* .dhx_combo_list_rtl div div{ */
/* float :right !important; */
/* cursor:default; */
/* } */
/* .dhx_combo_list_rtl div img{ */
/* float :right !important; */
/* } */
/* .dhx_combo_list_rtl div input{ */
/* float :right !important; */
/* } */
/* .dhx_combo_box.dhx_skyblue{ */
/* border:1px solid #a4bed4; */
/* } */
/* .dhx_combo_box.dhx_skyblue .dhx_combo_input { */
/* font-family:Tahoma; */
/* font-size: 11px; */
/* padding:3px; */
/* } */
/* .dhx_combo_list.dhx_skyblue_list{ */
/* background-color: #eaf2fb; */
/* border:1px solid #a4bed4; */
/* font-family:Tahoma; */
/* font-size: 11px; */
/* } */
/* .dhx_combo_list.dhx_skyblue_list div{ */
/* cursor:default; */
/* padding:3px 4px; */
/* } */
/* .dhx_combo_list_rtl.dhx_skyblue_list{ */
/* background-color: #eaf2fb; */
/* border:1px solid #a4bed4; */
/* font-family:Tahoma; */
/* font-size: 11px; */
/* } */

View File

@ -23,7 +23,12 @@ cabal-version: >= 1.2
build-type: Custom build-type: Custom
data-dir: data data-dir: data
data-files: data-files:
web/style.css web/static/style.css
web/static/hledger.js
web/static/jquery.js
web/static/dhtmlxcommon.js
web/static/dhtmlxcombo.js
web/static/images/combo_select.gif
extra-tmp-files: extra-tmp-files:
extra-source-files: extra-source-files:
README.rst README.rst