mirror of
https://github.com/simonmichael/hledger.git
synced 2024-11-08 07:09:28 +03:00
web: more register chart improvements
- show a background color for future and less-than-zero regions - show points for transactions, not all line corners - hovering over point shows balance, date, posted amount and transaction - clicking a point scrolls towards that date
This commit is contained in:
parent
470835adc2
commit
f2d9c6e9c1
@ -75,6 +75,7 @@ module Hledger.Data.Amount (
|
||||
mixed,
|
||||
amounts,
|
||||
filterMixedAmount,
|
||||
filterMixedAmountByCommodity,
|
||||
normaliseMixedAmountPreservingFirstPrice,
|
||||
normaliseMixedAmountPreservingPrices,
|
||||
-- ** arithmetic
|
||||
@ -429,6 +430,17 @@ amounts (Mixed as) = as
|
||||
filterMixedAmount :: (Amount -> Bool) -> MixedAmount -> MixedAmount
|
||||
filterMixedAmount p (Mixed as) = Mixed $ filter p as
|
||||
|
||||
-- | Return an unnormalised MixedAmount containing exactly one Amount
|
||||
-- with the specified commodity and the quantity of that commodity
|
||||
-- found in the original. NB if Amount's quantity is zero it will be
|
||||
-- discarded next time the MixedAmount gets normalised.
|
||||
filterMixedAmountByCommodity :: Commodity -> MixedAmount -> MixedAmount
|
||||
filterMixedAmountByCommodity c (Mixed as) = Mixed as'
|
||||
where
|
||||
as' = case filter ((==c) . acommodity) as of
|
||||
[] -> [nullamt{acommodity=c}]
|
||||
as'' -> [sum as'']
|
||||
|
||||
-- | Convert a mixed amount's component amounts to the commodity of their
|
||||
-- assigned price, if any.
|
||||
costOfMixedAmount :: MixedAmount -> MixedAmount
|
||||
|
@ -11,9 +11,12 @@ a some base account. They are used by hledger-web.
|
||||
module Hledger.Reports.TransactionsReports (
|
||||
TransactionsReport,
|
||||
TransactionsReportItem,
|
||||
triOrigTransaction,
|
||||
triDate,
|
||||
triAmount,
|
||||
triBalance,
|
||||
triSimpleBalance,
|
||||
triCommodityAmount,
|
||||
triCommodityBalance,
|
||||
journalTransactionsReport,
|
||||
accountTransactionsReport,
|
||||
transactionsReportByCommodity
|
||||
@ -51,11 +54,12 @@ type TransactionsReportItem = (Transaction -- the original journal transaction,
|
||||
,MixedAmount -- the running balance for the current account(s) after this transaction
|
||||
)
|
||||
|
||||
triDate (t,_,_,_,_,_) = tdate t
|
||||
triOrigTransaction (torig,_,_,_,_,_) = torig
|
||||
triDate (_,tacct,_,_,_,_) = tdate tacct
|
||||
triAmount (_,_,_,_,a,_) = a
|
||||
triBalance (_,_,_,_,_,a) = a
|
||||
triSimpleBalance (_,_,_,_,_,Mixed a) = case a of [] -> "0"
|
||||
(Amount{aquantity=q}):_ -> show q
|
||||
triCommodityAmount c = filterMixedAmountByCommodity c . triAmount
|
||||
triCommodityBalance c = filterMixedAmountByCommodity c . triBalance
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
@ -237,9 +241,5 @@ filterTransactionsReportByCommodity c (label,items) =
|
||||
go bal ((t,t2,s,o,amt,_):is) = (t,t2,s,o,amt,bal'):go bal' is
|
||||
where bal' = bal + amt
|
||||
|
||||
-- | Filter out all but the specified commodity from this amount.
|
||||
filterMixedAmountByCommodity :: Commodity -> MixedAmount -> MixedAmount
|
||||
filterMixedAmountByCommodity c (Mixed as) = Mixed $ filter ((==c). acommodity) as
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
@ -134,6 +134,7 @@ instance Yesod App where
|
||||
addScript $ StaticR js_jquery_hotkeys_js
|
||||
addScript $ StaticR js_jquery_flot_min_js
|
||||
addScript $ StaticR js_jquery_flot_time_min_js
|
||||
addScript $ StaticR js_jquery_flot_tooltip_min_js
|
||||
toWidget [hamlet| \<!--[if lte IE 8]> <script type="text/javascript" src="@{StaticR js_excanvas_min_js}"></script> <![endif]--> |]
|
||||
addStylesheet $ StaticR hledger_css
|
||||
addScript $ StaticR hledger_js
|
||||
|
@ -4,6 +4,7 @@ module Handler.RegisterR where
|
||||
|
||||
import Import
|
||||
|
||||
import Data.List
|
||||
import Data.Maybe
|
||||
import Safe
|
||||
|
||||
@ -105,31 +106,69 @@ registerChartHtml percommoditytxnreports =
|
||||
<div#register-chart style="width:85%; height:150px; margin-bottom:1em; display:block;">
|
||||
<script type=text/javascript>
|
||||
\$(document).ready(function() {
|
||||
var chartdiv = $('#register-chart');
|
||||
if (chartdiv.is(':visible')) {
|
||||
var $chartdiv = $('#register-chart');
|
||||
if ($chartdiv.is(':visible')) {
|
||||
\$('#register-chart-label').text('#{charttitle}');
|
||||
registerChart(
|
||||
chartdiv,
|
||||
[
|
||||
$forall (comm,(_,items)) <- percommoditytxnreports
|
||||
{
|
||||
data: [
|
||||
$forall i <- reverse items
|
||||
[#{dayToJsTimestamp $ triDate i}, #{triSimpleBalance i}],
|
||||
/* [] */
|
||||
var seriesData = [
|
||||
$forall (c,(_,items)) <- percommoditytxnreports
|
||||
/* we render each commodity using two series:
|
||||
* one with extra data points added to show a stepped balance line */
|
||||
{
|
||||
data: [
|
||||
$forall i <- reverse items
|
||||
[
|
||||
#{dayToJsTimestamp $ triDate i},
|
||||
#{simpleMixedAmountQuantity $ triCommodityBalance c i},
|
||||
],
|
||||
label: '#{comm}',
|
||||
color: #{colorForCommodity comm},
|
||||
},
|
||||
]
|
||||
);
|
||||
/* [] */
|
||||
],
|
||||
label: '#{c}',
|
||||
color: #{colorForCommodity c},
|
||||
lines: {
|
||||
show: true,
|
||||
steps: true,
|
||||
},
|
||||
points: {
|
||||
show: false,
|
||||
},
|
||||
clickable: false,
|
||||
hoverable: false,
|
||||
},
|
||||
/* and one with the original data, showing one clickable, hoverable point per transaction */
|
||||
{
|
||||
data: [
|
||||
$forall i <- reverse items
|
||||
[
|
||||
#{dayToJsTimestamp $ triDate i},
|
||||
#{simpleMixedAmountQuantity $ triCommodityBalance c i},
|
||||
'#{show $ triCommodityAmount c i}',
|
||||
'#{show $ triCommodityBalance c i}',
|
||||
'#{concat $ intersperse "\\n" $ lines $ show $ triOrigTransaction i}',
|
||||
],
|
||||
/* [] */
|
||||
],
|
||||
label: '',
|
||||
color: '#{colorForCommodity c}',
|
||||
lines: {
|
||||
show: false,
|
||||
},
|
||||
points: {
|
||||
show: true,
|
||||
color: '#{colorForCommodity c}',
|
||||
},
|
||||
},
|
||||
]
|
||||
var plot = registerChart($chartdiv, seriesData);
|
||||
\$chartdiv.bind("plotclick", registerChartClick);
|
||||
};
|
||||
});
|
||||
|]
|
||||
-- [#{dayToJsTimestamp $ ltrace "\ndate" $ triDate i}, #{ltrace "balancequantity" $ simpleMixedAmountQuantity $ triCommodityBalance c i}, '#{ltrace "balance" $ show $ triCommodityBalance c i}, '#{ltrace "amount" $ show $ triCommodityAmount c i}''],
|
||||
where
|
||||
charttitle = case maybe "" (fst.snd) $ headMay percommoditytxnreports
|
||||
of "" -> ""
|
||||
s -> s++":"
|
||||
colorForCommodity = fromMaybe 0 . flip lookup commoditiesIndex
|
||||
commoditiesIndex = zip (map fst percommoditytxnreports) [0..] :: [(Commodity,Int)]
|
||||
simpleMixedAmountQuantity = maybe 0 aquantity . headMay . amounts
|
||||
|
||||
|
@ -32,65 +32,97 @@ $(document).ready(function() {
|
||||
// REGISTER CHART
|
||||
|
||||
function registerChart($container, series) {
|
||||
// https://github.com/flot/flot/blob/master/API.md
|
||||
return $container.plot(
|
||||
// https://github.com/flot/flot/blob/master/API.md
|
||||
return $container.plot(
|
||||
series,
|
||||
{ /* general chart options */
|
||||
series: {
|
||||
points: {
|
||||
show: true,
|
||||
},
|
||||
lines: {
|
||||
show: true,
|
||||
steps: true,
|
||||
},
|
||||
bars: {
|
||||
// show: true,
|
||||
// barWidth: 1000 * 60 * 60, // ms
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
/* mode: "time", */
|
||||
/* timeformat: "%y/%m/%d", */
|
||||
/* ticks: 6, */
|
||||
},
|
||||
// series: {
|
||||
// },
|
||||
// yaxis: {
|
||||
// /* ticks: 6, */
|
||||
// },
|
||||
xaxis: {
|
||||
mode: "time",
|
||||
timeformat: "%Y/%m/%d"
|
||||
/* ticks: 6, */
|
||||
},
|
||||
grid: {
|
||||
// clickable: true,
|
||||
// hoverable: true,
|
||||
// autoHighlight: true,
|
||||
markings:
|
||||
function (axes) {
|
||||
// console.log(axes);
|
||||
// var markings = [];
|
||||
// for (var x = Math.floor(axes.xaxis.min); x < axes.xaxis.max; x += 2)
|
||||
// markings.push({ xaxis: { from: x, to: x + 1 } });
|
||||
// midx = Math.floor(axes.xaxis.min + (axes.xaxis.max - axes.xaxis.min) / 2);
|
||||
var now = Date.now();
|
||||
var now = Date.now();
|
||||
var markings = [
|
||||
// {
|
||||
// xaxis: { to: now }, // past
|
||||
// yaxis: { from: 0, to: 0 }, // =0
|
||||
// color: '#d88',
|
||||
// lineWidth:1
|
||||
// },
|
||||
{
|
||||
xaxis: { from: now, to: now },
|
||||
color: '#888',
|
||||
lineWidth:1
|
||||
xaxis: { to: now }, // past
|
||||
yaxis: { to: 0 }, // <0
|
||||
color: '#ffdddd',
|
||||
},
|
||||
|
||||
// {
|
||||
// xaxis: { from: now, to: now }, // now
|
||||
// color: '#bbb',
|
||||
// },
|
||||
|
||||
{
|
||||
xaxis: { from: now }, // future
|
||||
yaxis: { from: 0 }, // >0
|
||||
// color: '#dddddd',
|
||||
color: '#e0e0e0',
|
||||
},
|
||||
{
|
||||
yaxis: { from: 0, to: 0 },
|
||||
color: '#bb0000',
|
||||
lineWidth:1
|
||||
xaxis: { from: now }, // future
|
||||
yaxis: { to: 0 }, // <0
|
||||
// color: '#ddbbbb',
|
||||
color: '#e8c8c8',
|
||||
},
|
||||
{
|
||||
// xaxis: { from: now }, // future
|
||||
yaxis: { from: 0, to: 0 }, // =0
|
||||
color: '#bb0000',
|
||||
lineWidth:1
|
||||
},
|
||||
];
|
||||
// console.log(markings);
|
||||
return markings;
|
||||
}
|
||||
},
|
||||
hoverable: true,
|
||||
autoHighlight: true,
|
||||
clickable: true,
|
||||
},
|
||||
/* https://github.com/krzysu/flot.tooltip */
|
||||
tooltip: true,
|
||||
tooltipOpts: {
|
||||
xDateFormat: "%Y/%m/%d",
|
||||
content:
|
||||
function(label, x, y, flotitem) {
|
||||
var data = flotitem.series.data[flotitem.dataIndex];
|
||||
return data[3]+" balance on %x after "+data[2]+" posted by transaction:<pre>"+data[4]+"</pre>";
|
||||
},
|
||||
onHover: function(flotitem, $tooltipel) {
|
||||
$tooltipel.css('border-color',flotitem.series.color);
|
||||
},
|
||||
},
|
||||
}
|
||||
).data("plot");
|
||||
}
|
||||
|
||||
function registerChartClick(ev, pos, item) {
|
||||
if (item) {
|
||||
var date = $.plot.dateGenerator(item.datapoint[0], {});
|
||||
var dateid = $.plot.formatDate(date, '%Y-%m-%d');
|
||||
$target = $('#'+dateid);
|
||||
if ($target.length)
|
||||
$('html, body').animate({
|
||||
scrollTop: $target.offset().top
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// ADD FORM
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user