Files
discourse-hledger/assets/javascripts/discourse/components/hledger-dashboard.gjs
T

322 lines
9.9 KiB
Plaintext

import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { fn } from "@ember/helper";
import { action } from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import { and, eq, isEmpty } from "discourse/truth-helpers";
import DButton from "discourse/ui-kit/d-button";
import DConditionalLoadingSpinner from "discourse/ui-kit/d-conditional-loading-spinner";
import DDatePicker from "discourse/ui-kit/d-date-picker";
import { i18n } from "discourse-i18n";
import { amountText } from "../lib/hledger-report";
import HledgerAccountCell from "./hledger-account-cell";
import HledgerAccountPath from "./hledger-account-path";
const REPORT_IDS = [
"accounts",
"balance_sheet",
"income_statement",
"equity",
"transactions",
];
export default class HledgerDashboard extends Component {
@tracked selectedReport = "accounts";
@tracked report = null;
@tracked loading = false;
@tracked error = null;
@tracked beginDate = null;
@tracked endDate = null;
#inflight = null;
#lastKey = null;
#requestId = 0;
constructor() {
super(...arguments);
this.loadReport();
}
get reports() {
return REPORT_IDS.map((id) => ({
id,
label: `hledger.reports.${id}`,
icon: {
accounts: "list",
balance_sheet: "scale-balanced",
income_statement: "chart-line",
equity: "users",
transactions: "receipt",
}[id],
}));
}
get topicId() {
return this.args.data?.post?.topic_id;
}
get isEquity() {
return this.selectedReport === "equity";
}
get isTransactions() {
return this.selectedReport === "transactions";
}
get labels() {
return {
total: i18n("hledger.total"),
net: i18n("hledger.net"),
noData: i18n("hledger.no_data"),
holder: i18n("hledger.equity.holder"),
amount: i18n("hledger.equity.amount"),
share: i18n("hledger.equity.share"),
note: i18n("hledger.equity.note"),
beginDate: i18n("hledger.filters.begin"),
endDate: i18n("hledger.filters.end"),
};
}
@action
select(reportId) {
if (this.selectedReport === reportId) {
return;
}
this.selectedReport = reportId;
this.loadReport();
}
@action
setBeginDate(value) {
this.beginDate = value;
this.loadReport();
}
@action
setEndDate(value) {
this.endDate = value;
this.loadReport();
}
@action
async loadReport() {
const topicId = this.topicId;
if (!topicId) {
return;
}
const key = `${this.selectedReport}|${this.beginDate}|${this.endDate}`;
if (key === this.#lastKey && !this.error) {
return;
}
this.#lastKey = key;
const requestId = ++this.#requestId;
this.#inflight?.abort();
this.loading = true;
this.error = null;
const data = {};
if (this.beginDate) {
data.begin = this.beginDate;
}
if (this.endDate) {
data.end = this.endDate;
}
try {
const promise = ajax(
`/hledger/topics/${topicId}/reports/${this.selectedReport}`,
{ data }
);
this.#inflight = promise;
const report = await promise;
if (requestId === this.#requestId) {
this.report = report;
}
} catch (e) {
if (requestId === this.#requestId) {
this.report = null;
this.error =
e?.jqXHR?.responseJSON?.errors?.[0] || i18n("hledger.errors.generic");
}
} finally {
if (requestId === this.#requestId) {
this.loading = false;
this.#inflight = null;
}
}
}
<template>
<div class="hledger-dashboard__inner">
<div class="hledger-dashboard__toolbar">
{{#each this.reports as |report|}}
<DButton
class={{if (eq this.selectedReport report.id) "is-active"}}
@label={{report.label}}
@icon={{report.icon}}
@action={{fn this.select report.id}}
@disabled={{this.loading}}
/>
{{/each}}
</div>
<div class="hledger-dashboard__filters">
<DDatePicker
@value={{this.beginDate}}
@onSelect={{this.setBeginDate}}
@placeholder={{this.labels.beginDate}}
/>
<DDatePicker
@value={{this.endDate}}
@onSelect={{this.setEndDate}}
@placeholder={{this.labels.endDate}}
/>
</div>
<DConditionalLoadingSpinner @condition={{this.loading}}>
{{#if this.error}}
<div
class="hledger-dashboard__error alert alert-error"
>{{this.error}}</div>
{{else if this.report}}
{{#if this.isEquity}}
{{#each this.report.groups as |group|}}
<div class="hledger-dashboard__section">
<h3
class="hledger-dashboard__section-title"
>{{group.commodity}}</h3>
<table class="hledger-dashboard__table">
<thead>
<tr>
<th>{{this.labels.holder}}</th>
<th
class="hledger-dashboard__amount"
>{{this.labels.amount}}</th>
<th
class="hledger-dashboard__amount"
>{{this.labels.share}}</th>
</tr>
</thead>
<tbody>
{{#each group.rows as |row|}}
<tr>
<HledgerAccountCell @row={{row}} />
<td class="hledger-dashboard__amount">{{amountText
row.amounts
}}</td>
<td
class="hledger-dashboard__amount"
>{{row.percentage}}%</td>
</tr>
{{/each}}
<tr class="hledger-dashboard__total">
<td>{{this.labels.total}}</td>
<td
class="hledger-dashboard__amount"
>{{group.total.display}}</td>
<td></td>
</tr>
</tbody>
</table>
</div>
{{/each}}
<p class="hledger-dashboard__note">{{this.labels.note}}</p>
{{else if this.isTransactions}}
{{#if this.report.transactions.length}}
{{#each this.report.transactions as |transaction|}}
<div class="hledger-dashboard__transaction">
<div class="hledger-dashboard__transaction-header">
<span
class="hledger-dashboard__transaction-date"
>{{transaction.date}}</span>
<span
class="hledger-dashboard__transaction-description"
>{{transaction.description}}</span>
</div>
<table class="hledger-dashboard__table">
<tbody>
{{#each transaction.postings as |posting|}}
<tr>
<td
class="hledger-dashboard__account"
title={{posting.account}}
>
{{#if posting.account_segments}}
<HledgerAccountPath
@segments={{posting.account_segments}}
/>
{{else}}
{{posting.account}}
{{/if}}
</td>
<td class="hledger-dashboard__amount">{{amountText
posting.amounts
}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
{{/each}}
{{else}}
<p class="hledger-dashboard__empty">{{this.labels.noData}}</p>
{{/if}}
{{else}}
{{#each this.report.sections as |section|}}
<div class="hledger-dashboard__section">
{{#if section.title}}
<h3
class="hledger-dashboard__section-title"
>{{section.title}}</h3>
{{/if}}
{{#if (and section.title (isEmpty section.rows))}}
<p class="hledger-dashboard__empty">{{this.labels.noData}}</p>
{{else}}
<table class="hledger-dashboard__table">
<tbody>
{{#each section.rows as |row|}}
<tr>
<HledgerAccountCell
@row={{row}}
@boldRoot={{row.root}}
/>
<td class="hledger-dashboard__amount">{{amountText
row.amounts
}}</td>
</tr>
{{/each}}
{{#if section.total}}
<tr class="hledger-dashboard__total">
<td>{{this.labels.total}}</td>
<td class="hledger-dashboard__amount">{{amountText
section.total
}}</td>
</tr>
{{/if}}
</tbody>
</table>
{{/if}}
</div>
{{/each}}
{{#if this.report.net}}
<div class="hledger-dashboard__net">
<span>{{this.labels.net}}</span>
<span class="hledger-dashboard__amount">{{amountText
this.report.net
}}</span>
</div>
{{/if}}
{{/if}}
{{/if}}
</DConditionalLoadingSpinner>
</div>
</template>
}