web: UI cleanup
This commit is contained in:
parent
c952ab881b
commit
5f6da96baa
@ -12,7 +12,7 @@ import Import
|
||||
import Hledger
|
||||
import Hledger.Cli.CliOptions
|
||||
import Hledger.Web.WebOptions
|
||||
import Widget.AddForm (addForm)
|
||||
import Widget.AddForm (addModal)
|
||||
import Widget.Common (accountQuery, mixedAmountAsHtml)
|
||||
|
||||
-- | The formatted journal view, with sidebar.
|
||||
@ -27,7 +27,6 @@ getJournalR = do
|
||||
acctlink a = (RegisterR, [("q", accountQuery a)])
|
||||
(_, items) = journalTransactionsReport (reportopts_ $ cliopts_ opts) j m
|
||||
|
||||
(addView, addEnctype) <- generateFormPost (addForm j today)
|
||||
defaultLayout $ do
|
||||
setTitle "journal - hledger-web"
|
||||
$(widgetFile "journal")
|
||||
|
||||
@ -17,8 +17,8 @@ import Text.Hamlet (hamletFile)
|
||||
import Hledger
|
||||
import Hledger.Cli.CliOptions
|
||||
import Hledger.Web.WebOptions
|
||||
import Widget.AddForm (addForm)
|
||||
import Widget.Common (mixedAmountAsHtml, numberTransactionsReportItems)
|
||||
import Widget.AddForm (addModal)
|
||||
import Widget.Common (mixedAmountAsHtml)
|
||||
|
||||
-- | The main journal/account register view, with accounts sidebar.
|
||||
getRegisterR :: Handler Html
|
||||
@ -31,13 +31,6 @@ getRegisterR = do
|
||||
|
||||
let r@(balancelabel,items) = accountTransactionsReport (reportopts_ $ cliopts_ opts) j m $ fromMaybe Any $ inAccountQuery qopts
|
||||
balancelabel' = if isJust (inAccount qopts) then balancelabel else "Total"
|
||||
evenodd x = if even x then "even" else "odd" :: Text
|
||||
datetransition newm newd
|
||||
| newm = "newmonth"
|
||||
| newd = "newday"
|
||||
| otherwise = "" :: Text
|
||||
|
||||
(addView, addEnctype) <- generateFormPost (addForm j today)
|
||||
defaultLayout $ do
|
||||
setTitle "register - hledger-web"
|
||||
$(widgetFile "register")
|
||||
|
||||
@ -2,10 +2,12 @@
|
||||
{-# LANGUAGE GADTs #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
{-# LANGUAGE RankNTypes #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
|
||||
module Widget.AddForm
|
||||
( addForm
|
||||
, addModal
|
||||
) where
|
||||
|
||||
import Control.Monad.State.Strict (evalStateT)
|
||||
@ -26,6 +28,27 @@ import Settings (widgetFile)
|
||||
|
||||
-- XXX <select> which journal to add to
|
||||
|
||||
addModal ::
|
||||
( MonadWidget m
|
||||
, r ~ Route (HandlerSite m)
|
||||
, m ~ WidgetFor (HandlerSite m)
|
||||
, RenderMessage (HandlerSite m) FormMessage
|
||||
)
|
||||
=> r -> Journal -> Day -> m ()
|
||||
addModal addR j today = do
|
||||
(addView, addEnctype) <- generateFormPost (addForm j today)
|
||||
[whamlet|
|
||||
<div .modal.fade #addmodal tabindex="-1" role="dialog" aria-labelledby="addLabel" aria-hidden="true">
|
||||
<div .modal-dialog .modal-lg>
|
||||
<div .modal-content>
|
||||
<div .modal-header>
|
||||
<button type="button" .close data-dismiss="modal" aria-hidden="true">×
|
||||
<h3 .modal-title #addLabel>Add a transaction
|
||||
<div .modal-body>
|
||||
<form#addform.form action=@{addR} method=POST enctype=#{addEnctype}>
|
||||
^{addView}
|
||||
|]
|
||||
|
||||
addForm ::
|
||||
(site ~ HandlerSite m, RenderMessage site FormMessage, MonadHandler m)
|
||||
=> Journal
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE BangPatterns #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE NamedFieldPuns #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
@ -10,7 +9,6 @@ module Widget.Common
|
||||
, balanceReportAsHtml
|
||||
, helplink
|
||||
, mixedAmountAsHtml
|
||||
, numberTransactionsReportItems
|
||||
, fromFormSuccess
|
||||
, writeValidJournal
|
||||
, journalFile404
|
||||
@ -18,11 +16,9 @@ module Widget.Common
|
||||
|
||||
import Data.Default (def)
|
||||
import Data.Foldable (find, for_)
|
||||
import Data.List (mapAccumL)
|
||||
import Data.Semigroup ((<>))
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as T
|
||||
import Data.Time.Calendar (Day, toGregorian)
|
||||
import System.FilePath (takeFileName)
|
||||
import Text.Blaze ((!), textValue)
|
||||
import qualified Text.Blaze.Html5 as H
|
||||
@ -50,7 +46,6 @@ writeValidJournal f txt =
|
||||
liftIO (readJournal def (Just f) txt) >>= \case
|
||||
Left e -> return (Left e)
|
||||
Right _ -> do
|
||||
-- And write to the file
|
||||
_ <- liftIO (writeFileWithBackupIfChanged f txt)
|
||||
return (Right ())
|
||||
|
||||
@ -102,19 +97,6 @@ accountQuery = ("inacct:" <>) . quoteIfSpaced
|
||||
accountOnlyQuery :: AccountName -> Text
|
||||
accountOnlyQuery = ("inacctonly:" <>) . quoteIfSpaced
|
||||
|
||||
numberTransactionsReportItems :: [TransactionsReportItem] -> [(Int, Bool, Bool, TransactionsReportItem)]
|
||||
numberTransactionsReportItems = snd . mapAccumL number (0, nulldate)
|
||||
where
|
||||
number :: (Int, Day) -> TransactionsReportItem -> ((Int, Day), (Int, Bool, Bool, TransactionsReportItem))
|
||||
number (!n, !prevd) i@(t, _, _, _, _, _) = ((n', d), (n', newday, newmonth, i))
|
||||
where
|
||||
n' = n + 1
|
||||
d = tdate t
|
||||
newday = d /= prevd
|
||||
newmonth = dm /= prevdm || dy /= prevdy
|
||||
(dy, dm, _) = toGregorian d
|
||||
(prevdy, prevdm, _) = toGregorian prevd
|
||||
|
||||
mixedAmountAsHtml :: MixedAmount -> HtmlUrl a
|
||||
mixedAmountAsHtml b _ =
|
||||
for_ (lines (showMixedAmountWithoutPrice b)) $ \t -> do
|
||||
|
||||
@ -20,37 +20,6 @@
|
||||
/*------------------------------------------------------------------------------------------*/
|
||||
/* 4. typeahead styles */
|
||||
|
||||
/*
|
||||
.typeahead,
|
||||
.tt-query,
|
||||
.tt-hint {
|
||||
width: 396px;
|
||||
height: 30px;
|
||||
padding: 8px 12px;
|
||||
font-size: 24px;
|
||||
line-height: 30px;
|
||||
border: 2px solid #ccc;
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.typeahead {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.typeahead:focus {
|
||||
border: 2px solid #0097cf;
|
||||
}
|
||||
|
||||
.tt-query {
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
*/
|
||||
.tt-hint {
|
||||
color: #bbb;
|
||||
}
|
||||
@ -200,24 +169,6 @@ ul {
|
||||
whitespace: nowrap;
|
||||
}
|
||||
|
||||
#main-content {
|
||||
/*
|
||||
-webkit-transition: width 0.3s ease, margin 0.3s ease;
|
||||
-moz-transition: width 0.3s ease, margin 0.3s ease;
|
||||
-o-transition: width 0.3s ease, margin 0.3s ease;
|
||||
transition: width 0.3s ease, margin 0.3s ease;
|
||||
*/
|
||||
}
|
||||
|
||||
#sidebar-menu {
|
||||
/*
|
||||
-webkit-transition: width 0.3s ease, margin 0.3s ease,opacity 0.3s ease,height 1s ease 1s;
|
||||
-moz-transition: width 0.3s ease, margin 0.3s ease,opacity 0.3s ease,height 1s ease 1s;
|
||||
-o-transition: width 0.3s ease, margin 0.3s ease,opacity 0.3s ease,height 1s ease 1s;
|
||||
transition: width 0.3s ease, margin 0.3s ease,opacity 0.3s ease,height 1s ease 1s;
|
||||
*/
|
||||
}
|
||||
|
||||
.col-any-0 {
|
||||
width:0 !important;
|
||||
height:0 !important;
|
||||
@ -229,10 +180,6 @@ ul {
|
||||
font-size:large;
|
||||
}
|
||||
|
||||
#searchbar {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.row-offcanvas {
|
||||
position: relative;
|
||||
|
||||
@ -4,9 +4,13 @@
|
||||
// STARTUP
|
||||
|
||||
$(document).ready(function() {
|
||||
// cache the input element as a variable
|
||||
// for minor performance benefits
|
||||
var dateEl = $('#dateWrap');
|
||||
// date picker
|
||||
// http://bootstrap-datepicker.readthedocs.io/en/latest/options.html
|
||||
var dateEl = $('#dateWrap').datepicker({
|
||||
showOnFocus: false,
|
||||
autoclose: true,
|
||||
format: 'yyyy-mm-dd'
|
||||
});;
|
||||
|
||||
// ensure add form always focuses its first field
|
||||
$('#addmodal')
|
||||
@ -18,21 +22,6 @@ $(document).ready(function() {
|
||||
dateEl.datepicker('hide');
|
||||
});
|
||||
|
||||
// show add form if ?add=1
|
||||
if ($.url.param('add')) { addformShow(true); }
|
||||
|
||||
// date picker
|
||||
// http://bootstrap-datepicker.readthedocs.io/en/latest/options.html
|
||||
dateEl.datepicker({
|
||||
showOnFocus: false,
|
||||
autoclose: true,
|
||||
format: 'yyyy-mm-dd'
|
||||
});
|
||||
|
||||
// sidebar account hover handlers
|
||||
$('#sidebar td a').mouseenter(function(){ $(this).parent().addClass('mouseover'); });
|
||||
$('#sidebar td').mouseleave(function(){ $(this).removeClass('mouseover'); });
|
||||
|
||||
// keyboard shortcuts
|
||||
// 'body' seems to hold focus better than document in FF
|
||||
$('body').bind('keydown', 'h', function(){ $('#helpmodal').modal('toggle'); return false; });
|
||||
@ -42,12 +31,12 @@ $(document).ready(function() {
|
||||
$('body').bind('keydown', 'a', function(){ addformShow(); return false; });
|
||||
$('body').bind('keydown', 'n', function(){ addformShow(); return false; });
|
||||
$('body').bind('keydown', 'f', function(){ $('#searchform input').focus(); return false; });
|
||||
$('body, #addform input, #addform select').bind('keydown', 'ctrl++', addformAddPosting);
|
||||
$('body, #addform input, #addform select').bind('keydown', 'ctrl+shift+=', addformAddPosting);
|
||||
$('body, #addform input, #addform select').bind('keydown', 'ctrl+=', addformAddPosting);
|
||||
$('body, #addform input, #addform select').bind('keydown', 'ctrl+-', addformDeletePosting);
|
||||
$('.amount-input:last').keypress(addformAddPosting);
|
||||
|
||||
|
||||
// highlight the entry from the url hash
|
||||
if (window.location.hash && $(window.location.hash)[0]) {
|
||||
$(window.location.hash).addClass('highlighted');
|
||||
@ -173,49 +162,38 @@ function focus($el) {
|
||||
|
||||
// Insert another posting row in the add form.
|
||||
function addformAddPosting() {
|
||||
$('.amount-input:last').off('keypress');
|
||||
// do nothing if it's not currently visible
|
||||
if (!$('#addform').is(':visible')) return;
|
||||
// save a copy of last row
|
||||
var lastrow = $('#addform .form-group:last').clone();
|
||||
if (!$('#addform').is(':visible')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// replace the submit button with an amount field, clear and renumber it, add the keybindings
|
||||
var prevLastRow = $('.amount-input:last');
|
||||
prevLastRow.off('keypress');
|
||||
|
||||
// Clone the currently last row
|
||||
$('#addform .account-postings').append(prevLastRow.clone());
|
||||
var num = $('#addform .account-group').length;
|
||||
|
||||
// insert the new last row
|
||||
$('#addform .account-postings').append(lastrow);
|
||||
// TODO: Enable typehead on dynamically created inputs
|
||||
|
||||
var $acctinput = $('.account-input:last');
|
||||
var $amntinput = $('.amount-input:last');
|
||||
// clear and renumber the field, add keybindings
|
||||
$acctinput
|
||||
// XXX Enable typehead on dynamically created inputs
|
||||
$('.amount-input:last')
|
||||
.val('')
|
||||
.prop('name', 'account')
|
||||
.prop('placeholder', 'Account ' + (num + 1));
|
||||
//lastrow.find('input') // not :last this time
|
||||
$acctinput
|
||||
.bind('keydown', 'ctrl+shift+=', addformAddPosting)
|
||||
.bind('keydown', 'ctrl+=', addformAddPosting)
|
||||
.bind('keydown', 'ctrl+-', addformDeletePosting);
|
||||
|
||||
$amntinput
|
||||
.val('')
|
||||
.prop('name','amount')
|
||||
.prop('placeholder','Amount ' + (num + 1))
|
||||
.prop('placeholder','Amount ' + num)
|
||||
.keypress(addformAddPosting);
|
||||
|
||||
$acctinput
|
||||
$('.account-input:last')
|
||||
.val('')
|
||||
.prop('placeholder', 'Account ' + num)
|
||||
.bind('keydown', 'ctrl++', addformAddPosting)
|
||||
.bind('keydown', 'ctrl+shift+=', addformAddPosting)
|
||||
.bind('keydown', 'ctrl+=', addformAddPosting)
|
||||
.bind('keydown', 'ctrl+-', addformDeletePosting);
|
||||
|
||||
}
|
||||
|
||||
// Remove the add form's last posting row, if empty, keeping at least two.
|
||||
function addformDeletePosting() {
|
||||
var num = $('#addform .account-group').length;
|
||||
if (num <= 2) return;
|
||||
if ($('#addform .account-group').length <= 2) {
|
||||
return;
|
||||
}
|
||||
// remember if the last row's field or button had focus
|
||||
var focuslost =
|
||||
$('.account-input:last').is(':focus')
|
||||
|
||||
@ -11,13 +11,10 @@
|
||||
<table .main-menu .table>
|
||||
^{accounts}
|
||||
|
||||
<div .col-xs-12.#{mainShowmd}.#{mainShowsm}>
|
||||
<div#main-content .col-xs-12.#{mainShowmd}.#{mainShowsm}>
|
||||
$maybe m <- msg
|
||||
<div #message .alert.alert-info>#{m}
|
||||
<div .row>
|
||||
<form#searchform .form-inline method=GET>
|
||||
<div .form-group .col-md-12 .col-sm-12 .col-xs-12>
|
||||
<div #searchbar .input-group>
|
||||
<form#searchform.input-group method=GET>
|
||||
<input .form-control name=q value=#{q} placeholder="Search"
|
||||
title="Enter hledger search patterns to filter the data below">
|
||||
<div .input-group-btn>
|
||||
@ -26,10 +23,11 @@
|
||||
<span .glyphicon .glyphicon-remove-circle>
|
||||
<button .btn .btn-default type=submit title="Apply search terms">
|
||||
<span .glyphicon .glyphicon-search>
|
||||
<button .btn .btn-default type=button data-toggle="modal" data-target="#helpmodal"
|
||||
title="Show search and general help">?
|
||||
<a href="@{ManageR}" .btn.btn-default title="Manage journal files">
|
||||
<span .glyphicon .glyphicon-wrench>
|
||||
<button .btn .btn-default type=button data-toggle="modal" data-target="#helpmodal"
|
||||
title="Show search and general help">
|
||||
<span .glyphicon .glyphicon-question-sign>
|
||||
^{widget}
|
||||
|
||||
<div .modal.fade #helpmodal tabindex="-1" role="dialog" aria-labelledby="helpLabel" aria-hidden="true">
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
<td .date nowrap>#{show (tdate torig)}
|
||||
<td .description colspan=2>#{textElideRight 60 (tdescription torig)}
|
||||
<td .amount style="text-align:right;">
|
||||
$if not split || not (isZeroMixedAmount amt)
|
||||
$if not split && not (isZeroMixedAmount amt)
|
||||
\^{mixedAmountAsHtml amt}
|
||||
|
||||
$forall Posting { paccount = acc, pamount = amt } <- tpostings torig
|
||||
@ -30,12 +30,4 @@
|
||||
<td .amount .nonhead style="text-align:right;">
|
||||
^{mixedAmountAsHtml amt}
|
||||
|
||||
<div .modal #addmodal tabindex="-1" role="dialog" aria-labelledby="addLabel" aria-hidden="true">
|
||||
<div .modal-dialog .modal-lg>
|
||||
<div .modal-content>
|
||||
<div .modal-header>
|
||||
<button type="button" .close data-dismiss="modal" aria-hidden="true">×
|
||||
<h3 .modal-title #addLabel>Add a transaction
|
||||
<div .modal-body>
|
||||
<form#addform.form action=@{AddR} method=POST enctype=#{addEnctype}>
|
||||
^{addView}
|
||||
^{addModal AddR j today}
|
||||
|
||||
@ -11,14 +11,12 @@
|
||||
<tbody>
|
||||
$forall (path, _) <- jfiles j
|
||||
<tr>
|
||||
<td>
|
||||
<td style="vertical-align:middle">
|
||||
#{path}
|
||||
<td style="text-align:right">
|
||||
<a href=@{EditR path}>
|
||||
<a.btn.btn-default href=@{EditR path}>
|
||||
Edit
|
||||
|
||||
<a href=@{UploadR path}>
|
||||
<a.btn.btn-default href=@{UploadR path}>
|
||||
Upload
|
||||
|
||||
<a href=@{DownloadR path}>
|
||||
<a.btn.btn-default href=@{DownloadR path}>
|
||||
Download
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
^{registerChartHtml $ transactionsReportByCommodity r}
|
||||
|
||||
<div.table-responsive>
|
||||
<table.registerreport .table .table-striped .table-condensed>
|
||||
<table .table.table-striped.table-condensed>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align:left;">
|
||||
@ -18,9 +18,8 @@
|
||||
#{balancelabel'}
|
||||
|
||||
<tbody>
|
||||
$forall (n, newd, newm, (torig, tacct, split, acct, amt, bal)) <- numberTransactionsReportItems items
|
||||
<tr ##{tindex torig} .item.#{evenodd n}.#{datetransition newm newd}
|
||||
title="#{show torig}" style="vertical-align:top;">
|
||||
$forall (torig, tacct, split, acct, amt, bal) <- items
|
||||
<tr ##{tindex torig} .item title="#{show torig}" style="vertical-align:top;">
|
||||
<td .date>
|
||||
<a href="@{JournalR}#transaction-#{tindex torig}">
|
||||
#{show (tdate tacct)}
|
||||
@ -28,15 +27,7 @@
|
||||
<td .account>#{elideRight 40 acct}
|
||||
<td .amount style="text-align:right; white-space:nowrap;">
|
||||
$if not split || not (isZeroMixedAmount amt)
|
||||
\^{mixedAmountAsHtml amt}
|
||||
^{mixedAmountAsHtml amt}
|
||||
<td .balance style="text-align:right;">^{mixedAmountAsHtml bal}
|
||||
|
||||
<div .modal #addmodal tabindex="-1" role="dialog" aria-labelledby="addLabel" aria-hidden="true">
|
||||
<div .modal-dialog .modal-lg>
|
||||
<div .modal-content>
|
||||
<div .modal-header>
|
||||
<button type="button" .close data-dismiss="modal" aria-hidden="true">×
|
||||
<h3 .modal-title #addLabel>Add a transaction
|
||||
<div .modal-body>
|
||||
<form#addform.form action=@{AddR} method=POST enctype=#{addEnctype}>
|
||||
^{addView}
|
||||
^{addModal AddR j today}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user