web: ui cleanups, replace balance/register with combo view
This commit is contained in:
parent
4467af1aa8
commit
0773dde872
@ -44,7 +44,7 @@ add opts args j
|
|||||||
getAndAddTransactions :: Journal -> [Opt] -> [String] -> Day -> IO ()
|
getAndAddTransactions :: Journal -> [Opt] -> [String] -> Day -> IO ()
|
||||||
getAndAddTransactions j opts args defaultDate = do
|
getAndAddTransactions j opts args defaultDate = do
|
||||||
(t, d) <- getTransaction j opts args defaultDate
|
(t, d) <- getTransaction j opts args defaultDate
|
||||||
j <- journalAddTransaction j t
|
j <- journalAddTransaction j opts t
|
||||||
getAndAddTransactions j opts args d
|
getAndAddTransactions j opts args d
|
||||||
|
|
||||||
-- | Read a transaction from the command line, with history-aware prompting.
|
-- | Read a transaction from the command line, with history-aware prompting.
|
||||||
@ -134,11 +134,12 @@ askFor prompt def validator = do
|
|||||||
-- | Append this transaction to the journal's file. Also, to the journal's
|
-- | Append this transaction to the journal's file. Also, to the journal's
|
||||||
-- transaction list, but we don't bother updating the other fields - this
|
-- transaction list, but we don't bother updating the other fields - this
|
||||||
-- is enough to include new transactions in the history matching.
|
-- is enough to include new transactions in the history matching.
|
||||||
journalAddTransaction :: Journal -> Transaction -> IO Journal
|
journalAddTransaction :: Journal -> [Opt] -> Transaction -> IO Journal
|
||||||
journalAddTransaction j@Journal{jtxns=ts} t = do
|
journalAddTransaction j@Journal{jtxns=ts} opts t = do
|
||||||
appendToJournalFile j $ showTransaction t
|
appendToJournalFile j $ showTransaction t
|
||||||
putStrLn $ printf "\nAdded transaction to %s:" (filepath j)
|
when (Debug `elem` opts) $ do
|
||||||
putStrLn =<< registerFromString (show t)
|
putStrLn $ printf "\nAdded transaction to %s:" (filepath j)
|
||||||
|
putStrLn =<< registerFromString (show t)
|
||||||
return j{jtxns=ts++[t]}
|
return j{jtxns=ts++[t]}
|
||||||
|
|
||||||
-- | Append data to the journal's file, ensuring proper separation from
|
-- | Append data to the journal's file, ensuring proper separation from
|
||||||
|
|||||||
@ -96,9 +96,9 @@ balance report:
|
|||||||
-}
|
-}
|
||||||
|
|
||||||
module Hledger.Cli.Commands.Balance (
|
module Hledger.Cli.Commands.Balance (
|
||||||
balance
|
BalanceReport
|
||||||
,BalanceReport
|
|
||||||
,BalanceReportItem
|
,BalanceReportItem
|
||||||
|
,balance
|
||||||
,balanceReport
|
,balanceReport
|
||||||
,balanceReportAsText
|
,balanceReportAsText
|
||||||
-- ,tests_Balance
|
-- ,tests_Balance
|
||||||
|
|||||||
@ -5,8 +5,13 @@ A ledger-compatible @print@ command.
|
|||||||
|
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Hledger.Cli.Commands.Print
|
module Hledger.Cli.Commands.Print (
|
||||||
where
|
JournalReport
|
||||||
|
,JournalReportItem
|
||||||
|
,print'
|
||||||
|
,journalReport
|
||||||
|
,showTransactions
|
||||||
|
) where
|
||||||
import Hledger.Data
|
import Hledger.Data
|
||||||
import Hledger.Cli.Options
|
import Hledger.Cli.Options
|
||||||
#if __GLASGOW_HASKELL__ <= 610
|
#if __GLASGOW_HASKELL__ <= 610
|
||||||
@ -15,6 +20,12 @@ import System.IO.UTF8
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
-- | A "journal report" is just a list of transactions.
|
||||||
|
type JournalReport = [JournalReportItem]
|
||||||
|
|
||||||
|
-- | The data for a single journal report item, representing one transaction.
|
||||||
|
type JournalReportItem = Transaction
|
||||||
|
|
||||||
-- | Print journal transactions in standard format.
|
-- | Print journal transactions in standard format.
|
||||||
print' :: [Opt] -> [String] -> Journal -> IO ()
|
print' :: [Opt] -> [String] -> Journal -> IO ()
|
||||||
print' opts args j = do
|
print' opts args j = do
|
||||||
@ -22,8 +33,11 @@ print' opts args j = do
|
|||||||
putStr $ showTransactions (optsToFilterSpec opts args t) j
|
putStr $ showTransactions (optsToFilterSpec opts args t) j
|
||||||
|
|
||||||
showTransactions :: FilterSpec -> Journal -> String
|
showTransactions :: FilterSpec -> Journal -> String
|
||||||
showTransactions filterspec j =
|
showTransactions fspec j = journalReportAsText [] fspec $ journalReport [] fspec j
|
||||||
concatMap (showTransactionForPrint effective) $ sortBy (comparing tdate) txns
|
|
||||||
where
|
journalReportAsText :: [Opt] -> FilterSpec -> JournalReport -> String -- XXX unlike the others, this one needs fspec not opts
|
||||||
effective = EffectiveDate == whichdate filterspec
|
journalReportAsText _ fspec items = concatMap (showTransactionForPrint effective) items
|
||||||
txns = jtxns $ filterJournalTransactions filterspec j
|
where effective = EffectiveDate == whichdate fspec
|
||||||
|
|
||||||
|
journalReport :: [Opt] -> FilterSpec -> Journal -> JournalReport
|
||||||
|
journalReport _ fspec j = sortBy (comparing tdate) $ jtxns $ filterJournalTransactions fspec j
|
||||||
@ -6,9 +6,9 @@ A ledger-compatible @register@ command.
|
|||||||
-}
|
-}
|
||||||
|
|
||||||
module Hledger.Cli.Commands.Register (
|
module Hledger.Cli.Commands.Register (
|
||||||
register
|
RegisterReport
|
||||||
,RegisterReport
|
|
||||||
,RegisterReportItem
|
,RegisterReportItem
|
||||||
|
,register
|
||||||
,registerReport
|
,registerReport
|
||||||
,registerReportAsText
|
,registerReportAsText
|
||||||
,showPostingWithBalanceForVty
|
,showPostingWithBalanceForVty
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import Hledger.Cli.Commands.Print
|
|||||||
import Hledger.Cli.Commands.Register
|
import Hledger.Cli.Commands.Register
|
||||||
import Hledger.Cli.Options hiding (value)
|
import Hledger.Cli.Options hiding (value)
|
||||||
import Hledger.Cli.Utils
|
import Hledger.Cli.Utils
|
||||||
|
import Hledger.Cli.Version (version)
|
||||||
import Hledger.Data
|
import Hledger.Data
|
||||||
import Hledger.Read (journalFromPathAndString)
|
import Hledger.Read (journalFromPathAndString)
|
||||||
import Hledger.Read.Journal (someamount)
|
import Hledger.Read.Journal (someamount)
|
||||||
@ -47,14 +48,17 @@ data HledgerWebApp = HledgerWebApp {
|
|||||||
mkYesod "HledgerWebApp" [$parseRoutes|
|
mkYesod "HledgerWebApp" [$parseRoutes|
|
||||||
/ IndexPage GET
|
/ IndexPage GET
|
||||||
/journal JournalPage GET POST
|
/journal JournalPage GET POST
|
||||||
/edit EditPage GET POST
|
|
||||||
/register RegisterPage GET
|
/register RegisterPage GET
|
||||||
/balance BalancePage GET
|
/balance BalancePage GET
|
||||||
|
/ledger LedgerPage GET
|
||||||
/style.css StyleCss GET
|
/style.css StyleCss GET
|
||||||
|]
|
|]
|
||||||
|
|
||||||
instance Yesod HledgerWebApp where approot = appRoot
|
instance Yesod HledgerWebApp where approot = appRoot
|
||||||
|
|
||||||
|
-- defaultroute = LedgerPage
|
||||||
|
defaultroute = JournalPage
|
||||||
|
|
||||||
-- | A bundle of useful data passed to templates.
|
-- | A bundle of useful data passed to templates.
|
||||||
data TemplateData = TD {
|
data TemplateData = TD {
|
||||||
here :: HledgerWebAppRoute -- ^ the current page's route
|
here :: HledgerWebAppRoute -- ^ the current page's route
|
||||||
@ -62,18 +66,14 @@ data TemplateData = TD {
|
|||||||
,msg :: Maybe (Html ()) -- ^ transient message
|
,msg :: Maybe (Html ()) -- ^ transient message
|
||||||
,a :: String -- ^ a (filter pattern) parameter
|
,a :: String -- ^ a (filter pattern) parameter
|
||||||
,p :: String -- ^ p (period expression) parameter
|
,p :: String -- ^ p (period expression) parameter
|
||||||
,content :: Html () -- ^ html for the content area
|
|
||||||
,contentplain :: String -- ^ or plain text content
|
|
||||||
}
|
}
|
||||||
|
|
||||||
td = TD {
|
mktd = TD {
|
||||||
here = IndexPage
|
here = IndexPage
|
||||||
,title = "hledger"
|
,title = "hledger"
|
||||||
,msg = Nothing
|
,msg = Nothing
|
||||||
,a = ""
|
,a = ""
|
||||||
,p = ""
|
,p = ""
|
||||||
,content = nulltemplate id
|
|
||||||
,contentplain = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | The web command.
|
-- | The web command.
|
||||||
@ -104,9 +104,10 @@ server baseurl port opts args j = do
|
|||||||
}
|
}
|
||||||
withStore "hledger" $ do
|
withStore "hledger" $ do
|
||||||
putValue "hledger" "journal" j
|
putValue "hledger" "journal" j
|
||||||
basicHandler port app
|
basicHandler' port Nothing app
|
||||||
|
|
||||||
-- handlers
|
----------------------------------------------------------------------
|
||||||
|
-- handlers & templates
|
||||||
|
|
||||||
getStyleCss :: Handler HledgerWebApp ()
|
getStyleCss :: Handler HledgerWebApp ()
|
||||||
getStyleCss = do
|
getStyleCss = do
|
||||||
@ -115,158 +116,107 @@ getStyleCss = do
|
|||||||
sendFile "text/css" $ dir </> "style.css"
|
sendFile "text/css" $ dir </> "style.css"
|
||||||
|
|
||||||
getIndexPage :: Handler HledgerWebApp ()
|
getIndexPage :: Handler HledgerWebApp ()
|
||||||
getIndexPage = redirect RedirectTemporary BalancePage
|
getIndexPage = redirect RedirectTemporary defaultroute
|
||||||
|
|
||||||
-- | Gather all the stuff we want for a typical hledger web request handler.
|
----------------------------------------------------------------------
|
||||||
getHandlerParameters :: Handler HledgerWebApp
|
|
||||||
(String, String, [Opt], FilterSpec, Journal, Maybe (Html ()), HledgerWebAppRoute)
|
|
||||||
getHandlerParameters = do
|
|
||||||
Just here <- getCurrentRoute
|
|
||||||
(a, p, opts, fspec) <- getReportParameters
|
|
||||||
(j, err) <- getLatestJournal opts
|
|
||||||
msg <- getMessage' err
|
|
||||||
return (a, p, opts, fspec, j, msg, here)
|
|
||||||
where
|
|
||||||
-- | Get current report parameters for this request.
|
|
||||||
getReportParameters :: Handler HledgerWebApp (String, String, [Opt], FilterSpec)
|
|
||||||
getReportParameters = do
|
|
||||||
app <- getYesod
|
|
||||||
t <- liftIO $ getCurrentLocalTime
|
|
||||||
a <- fromMaybe "" <$> lookupGetParam "a"
|
|
||||||
p <- fromMaybe "" <$> lookupGetParam "p"
|
|
||||||
let opts = appOpts app ++ [Period p]
|
|
||||||
args = appArgs app ++ [a]
|
|
||||||
fspec = optsToFilterSpec opts args t
|
|
||||||
return (a, p, opts, fspec)
|
|
||||||
|
|
||||||
-- | Update our copy of the journal if the file changed. If there is an
|
|
||||||
-- error while reloading, keep the old one and return the error, and set a
|
|
||||||
-- ui message.
|
|
||||||
getLatestJournal :: [Opt] -> Handler HledgerWebApp (Journal, Maybe String)
|
|
||||||
getLatestJournal opts = do
|
|
||||||
j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
|
|
||||||
(jE, changed) <- liftIO $ journalReloadIfChanged opts j
|
|
||||||
if not changed
|
|
||||||
then return (j,Nothing)
|
|
||||||
else case jE of
|
|
||||||
Right j' -> do liftIO $ putValue "hledger" "journal" j'
|
|
||||||
return (j',Nothing)
|
|
||||||
Left e -> do setMessage $ string "error while reading" {- ++ ": " ++ e-}
|
|
||||||
return (j, Just e)
|
|
||||||
|
|
||||||
-- | Helper to work around a yesod feature (can't set and get a message in the same request.)
|
|
||||||
getMessage' :: Maybe String -> Handler HledgerWebApp (Maybe (Html ()))
|
|
||||||
getMessage' newmsgstr = do
|
|
||||||
oldmsg <- getMessage
|
|
||||||
return $ maybe oldmsg (Just . string) newmsgstr
|
|
||||||
|
|
||||||
-- renderLatestJournalWith :: ([Opt] -> FilterSpec -> Journal -> Html ()) -> Handler HledgerWebApp RepHtml
|
|
||||||
-- renderLatestJournalWith reportHtml = do
|
|
||||||
-- (a, p, opts, fspec, j, msg, here) <- getHandlerParameters
|
|
||||||
-- let td' = td{here=here, title="hledger", msg=msg, a=a, p=p, content=reportHtml opts fspec j}
|
|
||||||
-- hamletToRepHtml $ pageLayout td'
|
|
||||||
|
|
||||||
|
-- | A basic journal view, like hledger print, with editing.
|
||||||
getJournalPage :: Handler HledgerWebApp RepHtml
|
getJournalPage :: Handler HledgerWebApp RepHtml
|
||||||
getJournalPage = do
|
getJournalPage = do
|
||||||
(a, p, _, fspec, j, msg, here) <- getHandlerParameters
|
|
||||||
let td' = td{here=here, title="hledger", msg=msg, a=a, p=p, content=
|
|
||||||
stringToPre $ showTransactions fspec j
|
|
||||||
}
|
|
||||||
hamletToRepHtml $ pageLayout td'
|
|
||||||
|
|
||||||
getBalancePage :: Handler HledgerWebApp RepHtml
|
|
||||||
getBalancePage = do
|
|
||||||
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters
|
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters
|
||||||
let td' = td{here=here, title="hledger", msg=msg, a=a, p=p, content=
|
let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p}
|
||||||
balanceReportAsHtml opts td' $ balanceReport opts fspec j
|
editform' = editform td $ jtext j
|
||||||
}
|
txns = journalReportAsHtml opts td $ journalReport opts fspec j
|
||||||
hamletToRepHtml $ pageLayout td'
|
hamletToRepHtml $ pageLayout td [$hamlet|
|
||||||
|
%div.journal
|
||||||
|
^journalScripts^
|
||||||
|
%div.nav2
|
||||||
|
%a#addformlink!href!onclick="return addformToggle()" add one transaction
|
||||||
|
\ | $
|
||||||
|
%a#editformlink!href!onclick="return editformToggle()" edit the whole journal
|
||||||
|
^addform^
|
||||||
|
^editform'^
|
||||||
|
#transactions ^txns^
|
||||||
|
|]
|
||||||
|
|
||||||
-- | Render a balance report as HTML.
|
-- | Render a journal report as HTML.
|
||||||
balanceReportAsHtml :: [Opt] -> TemplateData -> BalanceReport -> Html ()
|
journalReportAsHtml :: [Opt] -> TemplateData -> JournalReport -> Hamlet HledgerWebAppRoute
|
||||||
balanceReportAsHtml _ td (items,total) = [$hamlet|
|
journalReportAsHtml _ td items = [$hamlet|
|
||||||
%table.balancereport
|
%table.journalreport
|
||||||
$forall items i
|
$forall number.items i
|
||||||
%tr.itemrule
|
|
||||||
%td!colspan=2
|
|
||||||
^itemAsHtml' i^
|
^itemAsHtml' i^
|
||||||
%tr.totalrule
|
|]
|
||||||
%td!colspan=2
|
|
||||||
%tr
|
|
||||||
%td
|
|
||||||
%td!align=right $mixedAmountAsHtml.total$
|
|
||||||
|] id
|
|
||||||
where
|
where
|
||||||
|
number = zip [1..]
|
||||||
itemAsHtml' = itemAsHtml td
|
itemAsHtml' = itemAsHtml td
|
||||||
itemAsHtml :: TemplateData -> BalanceReportItem -> Hamlet String
|
itemAsHtml :: TemplateData -> (Int, JournalReportItem) -> Hamlet HledgerWebAppRoute
|
||||||
itemAsHtml TD{p=p} (a, adisplay, adepth, abal) = [$hamlet|
|
itemAsHtml _ (n, t) = [$hamlet|
|
||||||
%tr.item
|
%tr.item.$evenodd$
|
||||||
%td.account
|
%td.transaction
|
||||||
$indent$
|
%pre $txn$
|
||||||
%a!href=$aurl$ $adisplay$
|
|
||||||
%td.balance!align=right $mixedAmountAsHtml.abal$
|
|
||||||
|] where
|
|] where
|
||||||
indent = preEscapedString $ concat $ replicate (2 * adepth) " "
|
evenodd = if even n then "even" else "odd"
|
||||||
aurl = printf "../register?a=^%s%s" a p' :: String
|
txn = trimnl $ showTransaction t where trimnl = reverse . dropWhile (=='\n') . reverse
|
||||||
p' = if null p then "" else printf "&p=%s" p
|
|
||||||
|
|
||||||
--mixedAmountAsHtml = intercalate ", " . lines . show
|
journalScripts = [$hamlet|
|
||||||
mixedAmountAsHtml = preEscapedString . intercalate "<br>" . lines . show
|
<script type="text/javascript">
|
||||||
|
|
||||||
getRegisterPage :: Handler HledgerWebApp RepHtml
|
function addformToggle() {
|
||||||
getRegisterPage = do
|
a = document.getElementById('addform');
|
||||||
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters
|
e = document.getElementById('editform');
|
||||||
let td' = td{here=here, title="hledger", msg=msg, a=a, p=p, content=
|
t = document.getElementById('transactions');
|
||||||
registerReportAsHtml opts td' $ registerReport opts fspec j
|
alink = document.getElementById('addformlink');
|
||||||
}
|
elink = document.getElementById('editformlink');
|
||||||
hamletToRepHtml $ pageLayout td'
|
if (a.style.display == 'none') {
|
||||||
|
alink.style['font-weight'] = 'bold';
|
||||||
|
elink.style['font-weight'] = 'normal';
|
||||||
|
a.style.display = 'block';
|
||||||
|
e.style.display = 'none';
|
||||||
|
t.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
alink.style['font-weight'] = 'normal';
|
||||||
|
elink.style['font-weight'] = 'normal';
|
||||||
|
a.style.display = 'none';
|
||||||
|
e.style.display = 'none';
|
||||||
|
t.style.display = 'block';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
-- | Render a register report as HTML.
|
function editformToggle() {
|
||||||
registerReportAsHtml :: [Opt] -> TemplateData -> RegisterReport -> Html ()
|
a = document.getElementById('addform');
|
||||||
registerReportAsHtml _ td items = [$hamlet|
|
e = document.getElementById('editform');
|
||||||
%table.registerreport
|
t = document.getElementById('transactions');
|
||||||
$forall items i
|
alink = document.getElementById('addformlink');
|
||||||
%tr.itemrule
|
elink = document.getElementById('editformlink');
|
||||||
%td!colspan=5
|
if (e.style.display == 'none') {
|
||||||
^itemAsHtml' i^
|
alink.style['font-weight'] = 'normal';
|
||||||
|] id
|
elink.style['font-weight'] = 'bold';
|
||||||
where
|
a.style.display = 'none';
|
||||||
itemAsHtml' = itemAsHtml td
|
e.style.display = 'block';
|
||||||
itemAsHtml :: TemplateData -> RegisterReportItem -> Hamlet String
|
t.style.display = 'none';
|
||||||
itemAsHtml TD{p=p} (ds, posting, b) = [$hamlet|
|
} else {
|
||||||
%tr.item
|
alink.style['font-weight'] = 'normal';
|
||||||
%td.date $date$
|
elink.style['font-weight'] = 'normal';
|
||||||
%td.description $desc$
|
a.style.display = 'none';
|
||||||
%td.account
|
e.style.display = 'none';
|
||||||
%a!href=$aurl$ $acct$
|
t.style.display = 'block';
|
||||||
%td.amount!align=right $mixedAmountAsHtml.pamount.posting$
|
}
|
||||||
%td.balance!align=right $mixedAmountAsHtml.b$
|
return false;
|
||||||
|] where
|
}
|
||||||
(date, desc) = case ds of Just (da, de) -> (show da, de)
|
|
||||||
Nothing -> ("", "")
|
|
||||||
acct = paccount posting
|
|
||||||
aurl = printf "../register?a=^%s%s" acct p' :: String
|
|
||||||
p' = if null p then "" else printf "&p=%s" p
|
|
||||||
|
|
||||||
queryStringFromAP a p = if null ap then "" else "?" ++ ap
|
</script>
|
||||||
where
|
|]
|
||||||
ap = intercalate "&" [a',p']
|
|
||||||
a' = if null a then "" else printf "&a=%s" a
|
|
||||||
p' = if null p then "" else printf "&p=%s" p
|
|
||||||
|
|
||||||
getEditPage :: Handler HledgerWebApp RepHtml
|
|
||||||
getEditPage = do
|
|
||||||
(a, p, _, _, _, msg, here) <- getHandlerParameters
|
|
||||||
-- reload journal's text without parsing, if changed
|
|
||||||
j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
|
|
||||||
changed <- liftIO $ journalFileIsNewer j
|
|
||||||
s <- liftIO $ if changed then readFile (filepath j) else return (jtext j) -- XXX readFile may throw an error
|
|
||||||
let td' = td{here=here, title="hledger", msg=msg, a=a, p=p,
|
|
||||||
content=(editform td') show, contentplain=s} -- XXX provide both to squeeze editform into pageLayout
|
|
||||||
hamletToRepHtml $ pageLayout td'
|
|
||||||
|
|
||||||
postJournalPage :: Handler HledgerWebApp RepPlain
|
postJournalPage :: Handler HledgerWebApp RepPlain
|
||||||
postJournalPage = do
|
postJournalPage = do
|
||||||
|
edit <- runFormPost' $ maybeStringInput "edit"
|
||||||
|
if isJust edit then postEditForm else postAddForm
|
||||||
|
|
||||||
|
-- | Handle a journal add form post.
|
||||||
|
postAddForm :: Handler HledgerWebApp RepPlain
|
||||||
|
postAddForm = do
|
||||||
|
(_, _, opts, _, _, _, _) <- getHandlerParameters
|
||||||
today <- liftIO getCurrentDay
|
today <- liftIO getCurrentDay
|
||||||
-- get form input values. M means a Maybe value.
|
-- get form input values. M means a Maybe value.
|
||||||
(dateM, descM, acct1M, amt1M, acct2M, amt2M) <- runFormPost'
|
(dateM, descM, acct1M, amt1M, acct2M, amt2M) <- runFormPost'
|
||||||
@ -315,12 +265,13 @@ postJournalPage = do
|
|||||||
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 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 JournalPage
|
redirect RedirectTemporary JournalPage
|
||||||
|
|
||||||
postEditPage :: Handler HledgerWebApp RepPlain
|
-- | Handle a journal edit form post.
|
||||||
postEditPage = do
|
postEditForm :: Handler HledgerWebApp RepPlain
|
||||||
|
postEditForm = do
|
||||||
-- 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.
|
||||||
textM <- runFormPost' $ maybeStringInput "text"
|
textM <- runFormPost' $ maybeStringInput "text"
|
||||||
let textE = maybe (Left "No value provided") Right textM
|
let textE = maybe (Left "No value provided") Right textM
|
||||||
@ -343,134 +294,23 @@ postEditPage = do
|
|||||||
if not changed
|
if not changed
|
||||||
then do
|
then do
|
||||||
setMessage $ string $ "No change"
|
setMessage $ string $ "No change"
|
||||||
redirect RedirectTemporary EditPage
|
redirect RedirectTemporary JournalPage
|
||||||
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 EditPage)
|
redirect RedirectTemporary JournalPage)
|
||||||
(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 JournalPage)
|
redirect RedirectTemporary JournalPage)
|
||||||
jE
|
jE
|
||||||
|
|
||||||
-- templates
|
|
||||||
|
|
||||||
nulltemplate = [$hamlet||]
|
|
||||||
|
|
||||||
stringToPre :: String -> Html ()
|
|
||||||
stringToPre s = [$hamlet|%pre $s$|] id
|
|
||||||
|
|
||||||
pageLayout :: TemplateData -> Hamlet HledgerWebAppRoute
|
|
||||||
pageLayout td@TD{here=here, title=title, msg=msg, content=content} = [$hamlet|
|
|
||||||
!!!
|
|
||||||
%html
|
|
||||||
%head
|
|
||||||
%title $title$
|
|
||||||
%meta!http-equiv=Content-Type!content=$metacontent$
|
|
||||||
%link!rel=stylesheet!type=text/css!href=@stylesheet@!media=all
|
|
||||||
%body
|
|
||||||
^navbar.td^
|
|
||||||
#messages $m$
|
|
||||||
^addform'.here^
|
|
||||||
#content
|
|
||||||
$content$
|
|
||||||
|]
|
|
||||||
where m = fromMaybe (string "") msg
|
|
||||||
addform' JournalPage = addform
|
|
||||||
addform' _ = nulltemplate
|
|
||||||
stylesheet = StyleCss
|
|
||||||
metacontent = "text/html; charset=utf-8"
|
|
||||||
|
|
||||||
navbar :: TemplateData -> Hamlet HledgerWebAppRoute
|
|
||||||
navbar td = [$hamlet|
|
|
||||||
#navbar
|
|
||||||
%a.toprightlink!href=$hledgerurl$ hledger.org
|
|
||||||
\ $
|
|
||||||
%a.toprightlink!href=$manualurl$ manual
|
|
||||||
\ $
|
|
||||||
^navlinks.td^
|
|
||||||
^searchform.td^
|
|
||||||
|]
|
|
||||||
|
|
||||||
navlinks :: TemplateData -> Hamlet HledgerWebAppRoute
|
|
||||||
navlinks TD{here=here,a=a,p=p} = [$hamlet|
|
|
||||||
#navlinks
|
|
||||||
^journallink^ $
|
|
||||||
(^editlink^) $
|
|
||||||
| ^balancelink^ $
|
|
||||||
| ^registerlink^ $
|
|
||||||
|]
|
|
||||||
where
|
|
||||||
journallink = navlink here "journal" JournalPage
|
|
||||||
editlink = navlink here "edit" EditPage
|
|
||||||
registerlink = navlink here "register" RegisterPage
|
|
||||||
balancelink = navlink here "balance" BalancePage
|
|
||||||
navlink here s dest = [$hamlet|%a.$style$!href=@?u@ $s$|]
|
|
||||||
where u = (dest, concat [(if null a then [] else [("a", a)])
|
|
||||||
,(if null p then [] else [("p", p)])])
|
|
||||||
style | here == dest = "navlinkcurrent"
|
|
||||||
| otherwise = "navlink"
|
|
||||||
|
|
||||||
searchform :: TemplateData -> Hamlet HledgerWebAppRoute
|
|
||||||
searchform TD{here=here,a=a,p=p} = [$hamlet|
|
|
||||||
%form#searchform!method=GET
|
|
||||||
^resetlink^ $
|
|
||||||
%span!style=white-space:nowrap;
|
|
||||||
filter by: $
|
|
||||||
%input!name=a!size=30!value=$a$
|
|
||||||
^ahelp^ $
|
|
||||||
in period: $
|
|
||||||
%input!name=p!size=30!value=$p$
|
|
||||||
^phelp^ $
|
|
||||||
%input!type=submit!value=filter
|
|
||||||
|]
|
|
||||||
where
|
|
||||||
ahelp = helplink "filter-patterns" "?"
|
|
||||||
phelp = helplink "period-expressions" "?"
|
|
||||||
resetlink
|
|
||||||
| null a && null p = nulltemplate
|
|
||||||
| otherwise = [$hamlet|%span#resetlink!style=font-weight:bold; $
|
|
||||||
%a!href=@here@ stop filtering|]
|
|
||||||
|
|
||||||
helplink topic label = [$hamlet|%a!href=$u$ $label$|]
|
|
||||||
where u = manualurl ++ if null topic then "" else '#':topic
|
|
||||||
|
|
||||||
editform :: TemplateData -> Hamlet HledgerWebAppRoute
|
|
||||||
editform TD{contentplain=t} = [$hamlet|
|
|
||||||
%form!method=POST
|
|
||||||
%table.form#editform!cellpadding=0!cellspacing=0!border=0
|
|
||||||
%tr.formheading
|
|
||||||
%td!colspan=2
|
|
||||||
%span!style=float:right; ^formhelp^
|
|
||||||
%span#formheading Edit journal:
|
|
||||||
%tr
|
|
||||||
%td!colspan=2
|
|
||||||
%textarea!name=text!rows=30!cols=80
|
|
||||||
$t$
|
|
||||||
%tr#addbuttonrow
|
|
||||||
%td
|
|
||||||
%a!href=@JournalPage@ cancel
|
|
||||||
%td!align=right
|
|
||||||
%input!type=submit!value=$submitlabel$
|
|
||||||
%tr.helprow
|
|
||||||
%td
|
|
||||||
%td!align=right
|
|
||||||
#help Are you sure ? All previous data will be replaced
|
|
||||||
|]
|
|
||||||
where
|
|
||||||
submitlabel = "save journal"
|
|
||||||
formhelp = helplink "file-format" "file format help"
|
|
||||||
|
|
||||||
addform :: Hamlet HledgerWebAppRoute
|
addform :: Hamlet HledgerWebAppRoute
|
||||||
addform = [$hamlet|
|
addform = [$hamlet|
|
||||||
%form!method=POST
|
%form#addform!method=POST!style=display:none;
|
||||||
%table.form#addform!cellpadding=0!cellspacing=0!border=0
|
%table.form!cellpadding=0!cellspacing=0!border=0
|
||||||
%tr.formheading
|
|
||||||
%td!colspan=4
|
|
||||||
%span#formheading Add a transaction:
|
|
||||||
%tr
|
%tr
|
||||||
%td!colspan=4
|
%td!colspan=4
|
||||||
%table!cellpadding=0!cellspacing=0!border=0
|
%table!cellpadding=0!cellspacing=0!border=0
|
||||||
@ -486,21 +326,21 @@ addform = [$hamlet|
|
|||||||
%tr.helprow
|
%tr.helprow
|
||||||
%td
|
%td
|
||||||
%td
|
%td
|
||||||
#help $datehelp$ ^datehelplink^ $
|
.help $datehelp$ ^datehelplink^ $
|
||||||
%td
|
%td
|
||||||
%td
|
%td
|
||||||
#help $deschelp$
|
.help $deschelp$
|
||||||
^transactionfields1^
|
^transactionfields1^
|
||||||
^transactionfields2^
|
^transactionfields2^
|
||||||
%tr#addbuttonrow
|
%tr#addbuttonrow
|
||||||
%td!colspan=4
|
%td!colspan=4
|
||||||
%input!type=submit!value=$addlabel$
|
%input!type=hidden!name=add!value=1
|
||||||
|
%input!type=submit!name=submit!value="add transaction"
|
||||||
|]
|
|]
|
||||||
where
|
where
|
||||||
datehelplink = helplink "dates" "..."
|
datehelplink = helplink "dates" "..."
|
||||||
datehelp = "eg: 7/20, 2010/1/1, "
|
datehelp = "eg: 7/20, 2010/1/1, "
|
||||||
deschelp = "eg: supermarket (optional)"
|
deschelp = "eg: supermarket (optional)"
|
||||||
addlabel = "add transaction"
|
|
||||||
date = "today"
|
date = "today"
|
||||||
desc = ""
|
desc = ""
|
||||||
transactionfields1 = transactionfields 1
|
transactionfields1 = transactionfields 1
|
||||||
@ -517,10 +357,10 @@ transactionfields n = [$hamlet|
|
|||||||
%tr.helprow
|
%tr.helprow
|
||||||
%td
|
%td
|
||||||
%td
|
%td
|
||||||
#help $accthelp$
|
.help $accthelp$
|
||||||
%td
|
%td
|
||||||
%td
|
%td
|
||||||
#help $amthelp$
|
.help $amthelp$
|
||||||
|]
|
|]
|
||||||
where
|
where
|
||||||
label | n == 1 = "To account"
|
label | n == 1 = "To account"
|
||||||
@ -542,3 +382,255 @@ transactionfields n = [$hamlet|
|
|||||||
acctvar = numbered "accountname"
|
acctvar = numbered "accountname"
|
||||||
amtvar = numbered "amount"
|
amtvar = numbered "amount"
|
||||||
|
|
||||||
|
editform :: TemplateData -> String -> Hamlet HledgerWebAppRoute
|
||||||
|
editform _ content = [$hamlet|
|
||||||
|
%form#editform!method=POST!style=display:none;
|
||||||
|
%table.form#editform!cellpadding=0!cellspacing=0!border=0
|
||||||
|
%tr
|
||||||
|
%td!colspan=2
|
||||||
|
%textarea!name=text!rows=30!cols=80
|
||||||
|
$content$
|
||||||
|
%tr#addbuttonrow
|
||||||
|
%td
|
||||||
|
%span.help ^formathelp^
|
||||||
|
%td!align=right
|
||||||
|
%span.help Are you sure ? Your journal will be overwritten. $
|
||||||
|
%input!type=hidden!name=edit!value=1
|
||||||
|
%input!type=submit!name=submit!value="save journal"
|
||||||
|
\ or $
|
||||||
|
%a!href!onclick="return editformToggle()" cancel
|
||||||
|
|]
|
||||||
|
where
|
||||||
|
formathelp = helplink "file-format" "file format help"
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | A combined accounts and postings view, like hledger balance + hledger register.
|
||||||
|
getLedgerPage :: Handler HledgerWebApp RepHtml
|
||||||
|
getLedgerPage = do
|
||||||
|
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters
|
||||||
|
-- in this view, balance report is filtered only by period, not account/description filters
|
||||||
|
app <- getYesod
|
||||||
|
t <- liftIO $ getCurrentLocalTime
|
||||||
|
let args = appArgs app
|
||||||
|
fspec' = optsToFilterSpec opts args t
|
||||||
|
br = balanceReportAsHtml opts td $ balanceReport opts fspec' j
|
||||||
|
rr = registerReportAsHtml opts td $ registerReport opts fspec j
|
||||||
|
td = mktd{here=here, title="hledger", msg=msg, a=a, p=p}
|
||||||
|
hamletToRepHtml $ pageLayout td [$hamlet|
|
||||||
|
%div.ledger
|
||||||
|
%div.accounts!style=float:left; ^br^
|
||||||
|
%div.register ^rr^
|
||||||
|
|]
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | An accounts and balances view, like hledger balance.
|
||||||
|
getBalancePage :: Handler HledgerWebApp RepHtml
|
||||||
|
getBalancePage = do
|
||||||
|
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters
|
||||||
|
let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p}
|
||||||
|
hamletToRepHtml $ pageLayout td $ balanceReportAsHtml opts td $ balanceReport opts fspec j
|
||||||
|
|
||||||
|
-- | Render a balance report as HTML.
|
||||||
|
balanceReportAsHtml :: [Opt] -> TemplateData -> BalanceReport -> Hamlet HledgerWebAppRoute
|
||||||
|
balanceReportAsHtml _ td (items,total) = [$hamlet|
|
||||||
|
%table.balancereport
|
||||||
|
$forall items i
|
||||||
|
^itemAsHtml' i^
|
||||||
|
%tr.totalrule
|
||||||
|
%td!colspan=2
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
%td!align=right $mixedAmountAsHtml.total$
|
||||||
|
|]
|
||||||
|
where
|
||||||
|
itemAsHtml' = itemAsHtml td
|
||||||
|
itemAsHtml :: TemplateData -> BalanceReportItem -> Hamlet HledgerWebAppRoute
|
||||||
|
itemAsHtml TD{a=a,p=p} (acct, adisplay, adepth, abal) = [$hamlet|
|
||||||
|
%tr.item.$current$
|
||||||
|
%td.account
|
||||||
|
$indent$
|
||||||
|
%a!href=$aurl$ $adisplay$
|
||||||
|
%td.balance!align=right $mixedAmountAsHtml.abal$
|
||||||
|
|] where
|
||||||
|
current = if not (null a) && containsRegex a acct then "current" else ""
|
||||||
|
indent = preEscapedString $ concat $ replicate (2 * adepth) " "
|
||||||
|
aurl = printf "../ledger?a=^%s%s" acct p' :: String
|
||||||
|
p' = if null p then "" else printf "&p=%s" p
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | A postings view, like hledger register.
|
||||||
|
getRegisterPage :: Handler HledgerWebApp RepHtml
|
||||||
|
getRegisterPage = do
|
||||||
|
(a, p, opts, fspec, j, msg, here) <- getHandlerParameters
|
||||||
|
let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p}
|
||||||
|
hamletToRepHtml $ pageLayout td $ registerReportAsHtml opts td $ registerReport opts fspec j
|
||||||
|
|
||||||
|
-- | Render a register report as HTML.
|
||||||
|
registerReportAsHtml :: [Opt] -> TemplateData -> RegisterReport -> Hamlet HledgerWebAppRoute
|
||||||
|
registerReportAsHtml _ td items = [$hamlet|
|
||||||
|
%table.registerreport
|
||||||
|
$forall number.items i
|
||||||
|
^itemAsHtml' i^
|
||||||
|
|]
|
||||||
|
where
|
||||||
|
number = zip [1..]
|
||||||
|
itemAsHtml' = itemAsHtml td
|
||||||
|
itemAsHtml :: TemplateData -> (Int, RegisterReportItem) -> Hamlet HledgerWebAppRoute
|
||||||
|
itemAsHtml TD{p=p} (n, (ds, posting, b)) = [$hamlet|
|
||||||
|
%tr.item.$evenodd$.$firstposting$
|
||||||
|
%td.date $date$
|
||||||
|
%td.description $desc$
|
||||||
|
%td.account
|
||||||
|
%a!href=$aurl$ $acct$
|
||||||
|
%td.amount!align=right $mixedAmountAsHtml.pamount.posting$
|
||||||
|
%td.balance!align=right $mixedAmountAsHtml.b$
|
||||||
|
|] where
|
||||||
|
evenodd = if even n then "even" else "odd"
|
||||||
|
(firstposting, date, desc) = case ds of Just (da, de) -> ("firstposting", show da, de)
|
||||||
|
Nothing -> ("", "", "")
|
||||||
|
acct = paccount posting
|
||||||
|
aurl = printf "../ledger?a=^%s%s" acct p' :: String
|
||||||
|
p' = if null p then "" else printf "&p=%s" p
|
||||||
|
|
||||||
|
--mixedAmountAsHtml = intercalate ", " . lines . show
|
||||||
|
mixedAmountAsHtml = preEscapedString . intercalate "<br>" . lines . show
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | A standalone journal edit form page.
|
||||||
|
getEditPage :: Handler HledgerWebApp RepHtml
|
||||||
|
getEditPage = do
|
||||||
|
(a, p, _, _, _, msg, here) <- getHandlerParameters
|
||||||
|
-- reload journal's text without parsing, if changed
|
||||||
|
j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
|
||||||
|
changed <- liftIO $ journalFileIsNewer j
|
||||||
|
s <- liftIO $ if changed then readFile (filepath j) else return (jtext j) -- XXX readFile may throw an error
|
||||||
|
let td = mktd{here=here, title="hledger", msg=msg, a=a, p=p}
|
||||||
|
hamletToRepHtml $ pageLayout td $ editform td s
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Gather all the stuff we want for a typical hledger web request handler.
|
||||||
|
getHandlerParameters :: Handler HledgerWebApp
|
||||||
|
(String, String, [Opt], FilterSpec, Journal, Maybe (Html ()), HledgerWebAppRoute)
|
||||||
|
getHandlerParameters = do
|
||||||
|
Just here <- getCurrentRoute
|
||||||
|
(a, p, opts, fspec) <- getReportParameters
|
||||||
|
(j, err) <- getLatestJournal opts
|
||||||
|
msg <- getMessage' err
|
||||||
|
return (a, p, opts, fspec, j, msg, here)
|
||||||
|
where
|
||||||
|
-- | Get current report parameters for this request.
|
||||||
|
getReportParameters :: Handler HledgerWebApp (String, String, [Opt], FilterSpec)
|
||||||
|
getReportParameters = do
|
||||||
|
app <- getYesod
|
||||||
|
t <- liftIO $ getCurrentLocalTime
|
||||||
|
a <- fromMaybe "" <$> lookupGetParam "a"
|
||||||
|
p <- fromMaybe "" <$> lookupGetParam "p"
|
||||||
|
let opts = appOpts app ++ [Period p]
|
||||||
|
args = appArgs app ++ [a]
|
||||||
|
fspec = optsToFilterSpec opts args t
|
||||||
|
return (a, p, opts, fspec)
|
||||||
|
|
||||||
|
-- | Update our copy of the journal if the file changed. If there is an
|
||||||
|
-- error while reloading, keep the old one and return the error, and set a
|
||||||
|
-- ui message.
|
||||||
|
getLatestJournal :: [Opt] -> Handler HledgerWebApp (Journal, Maybe String)
|
||||||
|
getLatestJournal opts = do
|
||||||
|
j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
|
||||||
|
(jE, changed) <- liftIO $ journalReloadIfChanged opts j
|
||||||
|
if not changed
|
||||||
|
then return (j,Nothing)
|
||||||
|
else case jE of
|
||||||
|
Right j' -> do liftIO $ putValue "hledger" "journal" j'
|
||||||
|
return (j',Nothing)
|
||||||
|
Left e -> do setMessage $ string "error while reading" {- ++ ": " ++ e-}
|
||||||
|
return (j, Just e)
|
||||||
|
|
||||||
|
-- | Helper to work around a yesod feature (can't set and get a message in the same request.)
|
||||||
|
getMessage' :: Maybe String -> Handler HledgerWebApp (Maybe (Html ()))
|
||||||
|
getMessage' newmsgstr = do
|
||||||
|
oldmsg <- getMessage
|
||||||
|
return $ maybe oldmsg (Just . string) newmsgstr
|
||||||
|
|
||||||
|
pageLayout :: TemplateData -> Hamlet HledgerWebAppRoute -> Hamlet HledgerWebAppRoute
|
||||||
|
pageLayout td@TD{title=title, msg=msg} content = [$hamlet|
|
||||||
|
!!!
|
||||||
|
%html
|
||||||
|
%head
|
||||||
|
%title $title$
|
||||||
|
%meta!http-equiv=Content-Type!content=$metacontent$
|
||||||
|
%link!rel=stylesheet!type=text/css!href=@StyleCss@!media=all
|
||||||
|
%body
|
||||||
|
^navbar.td^
|
||||||
|
#messages $m$
|
||||||
|
#content
|
||||||
|
^content^
|
||||||
|
|]
|
||||||
|
where m = fromMaybe (string "") msg
|
||||||
|
metacontent = "text/html; charset=utf-8"
|
||||||
|
|
||||||
|
navbar :: TemplateData -> Hamlet HledgerWebAppRoute
|
||||||
|
navbar td = [$hamlet|
|
||||||
|
#navbar
|
||||||
|
%a.toprightlink!href=$hledgerurl$ hledger $version$
|
||||||
|
\ $
|
||||||
|
%a.toprightlink!href=$manualurl$ manual
|
||||||
|
\ $
|
||||||
|
^navlinks.td^
|
||||||
|
^filterform.td^
|
||||||
|
|]
|
||||||
|
|
||||||
|
navlinks :: TemplateData -> Hamlet HledgerWebAppRoute
|
||||||
|
navlinks td = [$hamlet|
|
||||||
|
#navlinks
|
||||||
|
^journallink^ $
|
||||||
|
| ^ledgerlink^ $
|
||||||
|
|]
|
||||||
|
where
|
||||||
|
journallink = navlink td "journal" JournalPage
|
||||||
|
ledgerlink = navlink td "ledger" LedgerPage
|
||||||
|
-- | ^balancelink^ $
|
||||||
|
-- | ^registerlink^ $
|
||||||
|
-- balancelink = navlink td "balance" BalancePage
|
||||||
|
-- registerlink = navlink td "register" RegisterPage
|
||||||
|
|
||||||
|
navlink :: TemplateData -> String -> HledgerWebAppRoute -> Hamlet HledgerWebAppRoute
|
||||||
|
navlink TD{here=here,a=a,p=p} s dest = [$hamlet|%a.$style$!href=@?u@ $s$|]
|
||||||
|
where u = (dest, concat [(if null a then [] else [("a", a)])
|
||||||
|
,(if null p then [] else [("p", p)])])
|
||||||
|
style | dest == here = "navlinkcurrent"
|
||||||
|
| otherwise = "navlink"
|
||||||
|
|
||||||
|
filterform :: TemplateData -> Hamlet HledgerWebAppRoute
|
||||||
|
filterform TD{here=here,a=a,p=p} = [$hamlet|
|
||||||
|
%form#filterform.$filtering$!method=GET
|
||||||
|
%span!style=white-space:nowrap;
|
||||||
|
^filterformlabel^ $
|
||||||
|
%input!name=a!size=30!value=$a$
|
||||||
|
^ahelp^ $
|
||||||
|
in period: $
|
||||||
|
%input!name=p!size=30!value=$p$
|
||||||
|
^phelp^ $
|
||||||
|
%input!type=submit!value=filter
|
||||||
|
|]
|
||||||
|
where
|
||||||
|
ahelp = helplink "filter-patterns" "?"
|
||||||
|
phelp = helplink "period-expressions" "?"
|
||||||
|
(filtering, filterformlabel)
|
||||||
|
| null a && null p = ("", [$hamlet|filter by: $|])
|
||||||
|
| otherwise = ("filtering", [$hamlet|
|
||||||
|
%a#stopfilterlink!href=@here@ stop filtering
|
||||||
|
\ $
|
||||||
|
by $
|
||||||
|
|])
|
||||||
|
|
||||||
|
helplink :: String -> String -> Hamlet HledgerWebAppRoute
|
||||||
|
helplink topic label = [$hamlet|%a!href=$u$!target=hledgerhelp $label$|]
|
||||||
|
where u = manualurl ++ if null topic then "" else '#':topic
|
||||||
|
|
||||||
|
nulltemplate = [$hamlet||]
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,19 @@
|
|||||||
/* hledger web ui stylesheet */
|
/* hledger web ui stylesheet */
|
||||||
|
|
||||||
body { font-family: "helvetica","arial", "sans serif"; margin:0; }
|
/* font families */
|
||||||
#navbar { background-color:#eeeeee; border-bottom:2px solid #dddddd; padding:4px 4px 6px 4px; }
|
body { font-family:helvetica,arial,"sans serif"; }
|
||||||
|
/* pre { font-family:monospace,courier,"courier new"; } */
|
||||||
|
#editform textarea { font-family:courier,"courier new",monospace; }
|
||||||
|
|
||||||
|
body { margin:0; }
|
||||||
|
#navbar { /* background-color:#eeeeee; */ /* border-bottom:2px solid #dddddd; */ padding:4px 4px 6px 4px; }
|
||||||
#navlinks { display:inline; }
|
#navlinks { display:inline; }
|
||||||
.navlink { }
|
.navlink { }
|
||||||
.navlinkcurrent { font-weight:bold; }
|
.navlinkcurrent { font-weight:bold; }
|
||||||
#searchform { font-size:small; display:inline; margin-left:1em; }
|
.nav2 { font-size:small; }
|
||||||
#resetlink { font-size:small; }
|
#filterform { font-size:small; display:inline; margin-left:1em; }
|
||||||
|
.filtering { background-color:#eee; font-weight:bold; }
|
||||||
|
#stopfilterlink { font-size:small; }
|
||||||
.toprightlink { font-size:small; margin-left:1em; float:right; }
|
.toprightlink { font-size:small; margin-left:1em; float:right; }
|
||||||
#messages { color:red; background-color:#ffeeee; margin:0.5em;}
|
#messages { color:red; background-color:#ffeeee; margin:0.5em;}
|
||||||
.form { margin:1em; font-size:small; }
|
.form { margin:1em; font-size:small; }
|
||||||
@ -16,25 +23,50 @@ body { font-family: "helvetica","arial", "sans serif"; margin:0; }
|
|||||||
#addform #postingrow { }
|
#addform #postingrow { }
|
||||||
#addform #addbuttonrow { text-align:right; }
|
#addform #addbuttonrow { text-align:right; }
|
||||||
#editform { width:95%; }
|
#editform { width:95%; }
|
||||||
#editform textarea { background-color:#eeeeee; font-family:monospace; font-size:medium; width:100%; }
|
#editform textarea { /* background-color:#eeeeee; */ width:100%; }
|
||||||
#content { margin:1em; }
|
#content { margin:1em; }
|
||||||
.formheading td { padding-bottom:8px; }
|
.formheading td { padding-bottom:8px; }
|
||||||
#formheading { font-size:medium; font-weight:bold; }
|
#formheading { font-size:medium; font-weight:bold; }
|
||||||
.helprow td { padding-bottom:8px; }
|
.helprow td { padding-bottom:8px; }
|
||||||
#help {font-style: italic; font-size:smaller; }
|
.help {font-style: italic; font-size:smaller; }
|
||||||
|
|
||||||
/* for -fweb610 */
|
/* for -fweb610 */
|
||||||
#hledgerorglink, #helplink { float:right; margin-left:1em; }
|
/* #hledgerorglink, #helplink { float:right; margin-left:1em; } */
|
||||||
|
|
||||||
/* .balancereport { font-size:small; } */
|
.current { font-weight:bold; background-color:#eee; }
|
||||||
|
.description { padding-left:1em; }
|
||||||
|
.account { white-space:nowrap; padding-left:1em; }
|
||||||
|
.amount { white-space:nowrap; padding-left:1em; }
|
||||||
|
.balance { white-space:nowrap; padding-left:1em; }
|
||||||
|
/* don't let fields get too small in emptyish reports */
|
||||||
|
.description { width:4em; }
|
||||||
|
.account, .amount, .balance { width:2em; }
|
||||||
|
/* .odd { background-color:#e8e8e8; } */
|
||||||
|
/* .even { background-color:#e8f8e8; } */
|
||||||
|
/* .even { background-color:#f0fff0; } */
|
||||||
|
|
||||||
|
.journalreport { font-size:small; }
|
||||||
|
table.journalreport { margin-top:1em; }
|
||||||
|
.journalreport td { border-top:thin solid #ddd; }
|
||||||
|
.journalreport pre { margin-top:0; }
|
||||||
|
|
||||||
|
.ledger .accounts {padding-right:1em; margin-right:1em; border-right:thin solid #ddd;}
|
||||||
|
.ledger .register {}
|
||||||
|
|
||||||
|
.balancereport { font-size:small; }
|
||||||
.balancereport tr { vertical-align:top; }
|
.balancereport tr { vertical-align:top; }
|
||||||
|
table.balancereport { border-spacing:0; }
|
||||||
|
.ledger .balancereport td { padding:0; }
|
||||||
/* .itemrule td { border-top:thin solid #ddd; } */
|
/* .itemrule td { border-top:thin solid #ddd; } */
|
||||||
.totalrule td { border-top:thin solid black; }
|
.totalrule td { border-top:thin solid black; }
|
||||||
|
|
||||||
|
table.registerreport { border-spacing:0; }
|
||||||
.registerreport { font-size:small; }
|
.registerreport { font-size:small; }
|
||||||
.registerreport tr { vertical-align:top; }
|
.registerreport tr { vertical-align:top; }
|
||||||
|
.registerreport td { padding-bottom:0.2em; }
|
||||||
|
/* .registerreport td { margin-left:0em; margin-right:0; } */
|
||||||
.registerreport .date { white-space:nowrap; }
|
.registerreport .date { white-space:nowrap; }
|
||||||
/* .registerreport .description { font-size:small; } */
|
/* .registerreport .description { font-size:small; } */
|
||||||
.registerreport .account { white-space:nowrap; }
|
/* .firstposting { background-color:#eee; } */
|
||||||
.registerreport .amount { white-space:nowrap; }
|
.registerreport .even { background-color:#f0f0f0; }
|
||||||
.registerreport .balance { white-space:nowrap; }
|
|
||||||
|
|||||||
@ -104,7 +104,7 @@ ensureJournalFile f = do
|
|||||||
emptyJournal :: IO String
|
emptyJournal :: IO String
|
||||||
emptyJournal = do
|
emptyJournal = do
|
||||||
d <- getCurrentDay
|
d <- getCurrentDay
|
||||||
return $ printf "; journal created %s; see http://hledger.org/MANUAL.html#journal-file\n\n" (show d)
|
return $ printf "; journal created %s by hledger\n\n" (show d)
|
||||||
|
|
||||||
-- | Read a Journal from this string, using the specified data format or
|
-- | Read a Journal from this string, using the specified data format or
|
||||||
-- trying all known formats, or give an error string.
|
-- trying all known formats, or give an error string.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user