mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-07-05 12:31:39 +02:00
changes
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Add account group' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'account_group_add' %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Edit account group' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'account_group_edit' pk=account_group.id %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,74 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate 'Accounts' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container px-md-3 py-3 column-gap-5">
|
||||
<div class="tw-text-3xl fw-bold font-monospace tw-w-full mb-3">
|
||||
{% spaceless %}
|
||||
<div>{% translate 'Account Groups' %}<span>
|
||||
<a class="text-decoration-none tw-text-2xl p-1 category-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Add" %}"
|
||||
hx-get="{% url 'account_group_add' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="">
|
||||
<i class="fa-solid fa-circle-plus fa-fw"></i></a>
|
||||
</span></div>
|
||||
{% endspaceless %}
|
||||
</div>
|
||||
|
||||
<div class="border p-3 rounded-3">
|
||||
<table class="table table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for account_group in account_groups %}
|
||||
<tr class="account_group">
|
||||
<td class="col-auto">
|
||||
<a class="text-decoration-none tw-text-gray-400 p-1 tag-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'account_group_edit' pk=account_group.id %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="install toast">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="text-danger text-decoration-none p-1 tag-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'account_group_delete' pk=account_group.id %}"
|
||||
hx-trigger='delete_confirmed'
|
||||
_="install toast
|
||||
on click
|
||||
if event.ctrlKey trigger delete_confirmed
|
||||
else
|
||||
call Swal.fire({title: '{% translate 'Are you sure?' %}',
|
||||
text: '{% blocktranslate %}You won\'t be able to revert this!{% endblocktranslate %}',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
cancelButtonText: '{% blocktranslate %}Cancel{% endblocktranslate %}',
|
||||
confirmButtonText: '{% blocktranslate %}Yes, delete it!{% endblocktranslate %}',
|
||||
customClass: {
|
||||
confirmButton: 'btn btn-primary me-3',
|
||||
cancelButton: 'btn btn-danger'
|
||||
},
|
||||
buttonsStyling: false})
|
||||
if result.isConfirmed trigger delete_confirmed"><i class="fa-solid fa-trash fa-fw"></i></a></td>
|
||||
<td class="col">{{ account_group.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,72 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load currency_display %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Account Reconciliation' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'account_reconciliation' %}">
|
||||
{% csrf_token %}
|
||||
{{ form.management_form }}
|
||||
<div class="accordion accordion-flush" id="balanceAccordionFlush">
|
||||
{% for form in form.forms %}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#flush-collapse-{{ forloop.counter0 }}" aria-expanded="false"
|
||||
aria-controls="flush-collapseOne">
|
||||
{{ form.account_name }}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="flush-collapse-{{ forloop.counter0 }}" class="accordion-collapse collapse">
|
||||
<div class="accordion-body">
|
||||
<div class="mb-3">
|
||||
<div class="form-label">
|
||||
{% translate 'Current balance' %}
|
||||
</div>
|
||||
<div data-amount="{{ form.current_balance }}"
|
||||
data-decimal-places="{{ form.currency_decimal_places }}"
|
||||
id="amount-{{ forloop.counter0 }}">
|
||||
{% currency_display amount=form.current_balance prefix=form.currency_prefix suffix=form.currency_suffix decimal_places=form.currency_decimal_places %}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{% crispy form %}
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-label">
|
||||
{% translate 'Difference' %}
|
||||
</div>
|
||||
<div _="on input from #id_form-{{ forloop.counter0 }}-new_balance
|
||||
set original_amount to parseLocaleNumber('{{ form.current_balance }}')
|
||||
then call parseLocaleNumber(#id_form-{{ forloop.counter0 }}-new_balance.value)
|
||||
then set new_amount to result
|
||||
then set diff to new_amount - original_amount
|
||||
then set prefix to '{{ form.currency_prefix }}'
|
||||
then set suffix to '{{ form.currency_suffix }}'
|
||||
then set decimal_places to {{ form.currency_decimal_places }}
|
||||
then set format_new_amount to
|
||||
Intl.NumberFormat(
|
||||
undefined,
|
||||
{
|
||||
minimumFractionDigits: decimal_places,
|
||||
maximumFractionDigits: decimal_places,
|
||||
roundingMode: 'trunc'}
|
||||
).format(diff)
|
||||
then set formatted_string to `${prefix}${format_new_amount}${suffix}`
|
||||
then put formatted_string into me if diff else
|
||||
put '' into me">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div>
|
||||
<input type="submit" name="submit" value="{% translate 'Reconcile balances' %}" class="btn btn-outline-primary w-100" id="submit-id-submit">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Add account' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'account_add' %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Edit account' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'account_edit' pk=account.id %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,85 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate 'Accounts' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container px-md-3 py-3 column-gap-5">
|
||||
<div class="tw-text-3xl fw-bold font-monospace tw-w-full mb-3">
|
||||
{% spaceless %}
|
||||
<div>{% translate 'Accounts' %}<span>
|
||||
<a class="text-decoration-none tw-text-2xl p-1 category-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Add" %}"
|
||||
hx-get="{% url 'account_add' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="">
|
||||
<i class="fa-solid fa-circle-plus fa-fw"></i></a>
|
||||
</span></div>
|
||||
{% endspaceless %}
|
||||
</div>
|
||||
|
||||
<div class="border p-3 rounded-3">
|
||||
<table class="table table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
<th scope="col" class="col">{% translate 'Currency' %}</th>
|
||||
<th scope="col" class="col">{% translate 'Exchange Currency' %}</th>
|
||||
<th scope="col" class="col-auto">{% translate 'Is Asset' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for account in accounts %}
|
||||
<tr class="currency">
|
||||
<td class="col-auto">
|
||||
<a class="text-decoration-none tw-text-gray-400 p-1 tag-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'account_edit' pk=account.id %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="on click send action_clicked to .tag-action in the closest parent .tag end
|
||||
on action_clicked call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
install toast">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="text-danger text-decoration-none p-1 tag-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'account_delete' pk=account.id %}"
|
||||
hx-trigger='delete_confirmed'
|
||||
_="on click send action_clicked to .tag-action in the closest parent .tag end
|
||||
on action_clicked call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
install toast
|
||||
on click
|
||||
if event.ctrlKey trigger delete_confirmed
|
||||
else
|
||||
call Swal.fire({title: '{% translate 'Are you sure?' %}',
|
||||
text: '{% blocktranslate %}You won\'t be able to revert this!{% endblocktranslate %}',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
cancelButtonText: '{% blocktranslate %}Cancel{% endblocktranslate %}',
|
||||
confirmButtonText: '{% blocktranslate %}Yes, delete it!{% endblocktranslate %}',
|
||||
customClass: {
|
||||
confirmButton: 'btn btn-primary me-3',
|
||||
cancelButton: 'btn btn-danger'
|
||||
},
|
||||
buttonsStyling: false})
|
||||
if result.isConfirmed trigger delete_confirmed"><i class="fa-solid fa-trash fa-fw"></i></a></td>
|
||||
<td class="col">{{ account.name }}</td>
|
||||
<td class="col-auto">{{ account.currency }} ({{ account.currency.code }})</td>
|
||||
<td class="col-auto">{% if account.exchange_currency %}{{ account.exchange_currency }} (
|
||||
{{ account.exchange_currency.code }}){% else %}-{% endif %}</td>
|
||||
<td class="col-auto text-center">{% if account.is_asset %}<i class="fa-solid fa-circle text-success"></i>{% endif %}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,12 @@
|
||||
{% extends "admin/base_site.html" %}
|
||||
|
||||
{% block extrahead %}
|
||||
{{ block.super }}
|
||||
<script>
|
||||
let tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
if (!tz) {
|
||||
tz = "UTC"
|
||||
}
|
||||
document.cookie = "mytz=" + tz + ";path=/";
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Add category' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'category_add' %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Edit category' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'category_edit' category_id=category.id %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,78 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate 'Categories' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container px-md-3 py-3 column-gap-5">
|
||||
<div class="tw-text-3xl fw-bold font-monospace tw-w-full mb-3">
|
||||
{% spaceless %}
|
||||
<div>{% translate 'Categories' %}<span>
|
||||
<a class="text-decoration-none tw-text-2xl p-1 category-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Add" %}"
|
||||
hx-get="{% url 'category_add' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="">
|
||||
<i class="fa-solid fa-circle-plus fa-fw"></i></a>
|
||||
</span></div>
|
||||
{% endspaceless %}
|
||||
</div>
|
||||
|
||||
<div class="border p-3 rounded-3">
|
||||
<table class="table table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col-auto">{% translate 'Muted' %}</th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for category in categories %}
|
||||
<tr class="category">
|
||||
<td class="col-auto text-center">
|
||||
<a class="text-decoration-none tw-text-gray-400 p-1 category-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'category_edit' category_id=category.id %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="install toast">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="text-danger text-decoration-none p-1 category-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'category_delete' category_id=category.id %}"
|
||||
hx-trigger='delete_confirmed'
|
||||
_="install toast
|
||||
on click
|
||||
if event.ctrlKey trigger delete_confirmed
|
||||
else
|
||||
call Swal.fire({title: '{% translate 'Are you sure?' %}',
|
||||
text: '{% blocktranslate %}You won\'t be able to revert this!{% endblocktranslate %}',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
cancelButtonText: '{% blocktranslate %}Cancel{% endblocktranslate %}',
|
||||
confirmButtonText: '{% blocktranslate %}Yes, delete it!{% endblocktranslate %}',
|
||||
customClass: {
|
||||
confirmButton: 'btn btn-primary me-3',
|
||||
cancelButton: 'btn btn-danger'
|
||||
},
|
||||
buttonsStyling: false})
|
||||
if result.isConfirmed trigger delete_confirmed"><i class="fa-solid fa-trash fa-fw"></i></a></td>
|
||||
<td class="col-auto text-center">
|
||||
{% if category.mute %}<i class="fa-solid fa-circle text-success"></i>{% endif %}
|
||||
</td>
|
||||
<td class="col">{{ category.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load toast_bg %}
|
||||
{% if messages %}
|
||||
<div class="toast-container position-fixed top-0 start-50 translate-middle-x p-3">
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||
{% for message in messages %}
|
||||
<div class="toast align-items-center text-bg-{{ message.tags | toast_bg }} border-0"
|
||||
role="alert"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Add currency' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'currency_add' %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Edit currency' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'currency_edit' pk=currency.id %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,80 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate 'Currencies' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container px-md-3 py-3 column-gap-5">
|
||||
<div class="tw-text-3xl fw-bold font-monospace tw-w-full mb-3">
|
||||
{% spaceless %}
|
||||
<div>{% translate 'Currencies' %}<span>
|
||||
<a class="text-decoration-none tw-text-2xl p-1 category-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Add" %}"
|
||||
hx-get="{% url 'currency_add' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="">
|
||||
<i class="fa-solid fa-circle-plus fa-fw"></i></a>
|
||||
</span></div>
|
||||
{% endspaceless %}
|
||||
</div>
|
||||
|
||||
<div class="border p-3 rounded-3">
|
||||
<table class="table table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col-auto">{% translate 'Code' %}</th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for currency in currencies %}
|
||||
<tr class="currency">
|
||||
<td class="col-auto">
|
||||
<a class="text-decoration-none tw-text-gray-400 p-1 tag-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'currency_edit' pk=currency.id %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="on click send action_clicked to .tag-action in the closest parent .tag end
|
||||
on action_clicked call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
install toast">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="text-danger text-decoration-none p-1 tag-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'currency_delete' pk=currency.id %}"
|
||||
hx-trigger='delete_confirmed'
|
||||
_="on click send action_clicked to .tag-action in the closest parent .tag end
|
||||
on action_clicked call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
install toast
|
||||
on click
|
||||
if event.ctrlKey trigger delete_confirmed
|
||||
else
|
||||
call Swal.fire({title: '{% translate 'Are you sure?' %}',
|
||||
text: '{% blocktranslate %}You won\'t be able to revert this!{% endblocktranslate %}',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
cancelButtonText: '{% blocktranslate %}Cancel{% endblocktranslate %}',
|
||||
confirmButtonText: '{% blocktranslate %}Yes, delete it!{% endblocktranslate %}',
|
||||
customClass: {
|
||||
confirmButton: 'btn btn-primary me-3',
|
||||
cancelButton: 'btn btn-danger'
|
||||
},
|
||||
buttonsStyling: false})
|
||||
if result.isConfirmed trigger delete_confirmed"><i class="fa-solid fa-trash fa-fw"></i></a></td>
|
||||
<td class="col-auto">{{ currency.code }}</td>
|
||||
<td class="col">{{ currency.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -3,7 +3,7 @@
|
||||
<h5 class="offcanvas-title">{% block title %}{% endblock %}</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div id="generic-offcanvas-body" class="offcanvas-body">
|
||||
<div id="generic-offcanvas-body" class="offcanvas-body"
|
||||
_="install init_tom_select">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
{% javascript_pack 'select' %}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{% spaceless %}
|
||||
<span class="tw-text-xs text-white-50 mx-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{{ content }}"
|
||||
_="install toast">
|
||||
<i class="fa-solid fa-circle-question fa-fw"></i>
|
||||
</span>
|
||||
{% endspaceless %}
|
||||
@@ -0,0 +1,59 @@
|
||||
{% load i18n %}
|
||||
{% load active_link %}
|
||||
<nav class="navbar navbar-expand-lg border-bottom bg-body-tertiary" hx-boost="true">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand fw-bold text-primary font-base" href="#">WYGIWYH</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0 nav-underline" hx-push-url="true">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle {% active_link views='monthly_overview' %}"
|
||||
href="#"
|
||||
role="button"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
{% translate 'Overview' %}
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item {% active_link views='monthly_overview' %}" href="{% url 'monthly_index' %}">{%translate 'Monthly' %}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% active_link views='net_worth' %}"
|
||||
href="{% url 'net_worth' %}">
|
||||
{% translate 'Net Worth' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle {% active_link views='tags_list||categories_list||currencies_list' %}"
|
||||
href="#" role="button"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
{% translate 'Management' %}
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><h6 class="dropdown-header">{% trans 'Transactions' %}</h6></li>
|
||||
<li><a class="dropdown-item {% active_link views='categories_list' %}" href="{% url 'categories_list' %}">{% translate 'Categories' %}</a></li>
|
||||
<li><a class="dropdown-item {% active_link views='tags_list' %}" href="{% url 'tags_list' %}">{% translate 'Tags' %}</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><h6 class="dropdown-header">{% trans 'Accounts' %}</h6></li>
|
||||
<li><a class="dropdown-item {% active_link views='accounts_list' %}" href="{% url 'accounts_list' %}">{% translate 'Accounts' %}</a></li>
|
||||
<li><a class="dropdown-item {% active_link views='account_groups_list' %}" href="{% url 'account_groups_list' %}">{% translate 'Account Groups' %}</a></li>
|
||||
<li><a class="dropdown-item {% active_link views='currencies_list' %}" href="{% url 'currencies_list' %}">{% translate 'Currencies' %}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% spaceless %}
|
||||
<a class="mx-3 tw-text-2xl" hx-get="{% url 'toggle_amount_visibility' %}">
|
||||
{% if user.settings.hide_amounts %}
|
||||
<i class="fa-solid fa-eye-slash fa-fw"></i><div id="settings-hide-amounts" class="d-inline tw-invisible"></div>
|
||||
{% else %}
|
||||
<i class="fa-solid fa-eye fa-fw"></i>
|
||||
{% endif %}</a>
|
||||
{% endspaceless %}
|
||||
<a class="btn btn-outline-light btn-sm" href="{% url 'logout' %}"><i class="fa-solid fa-door-open me-2"></i>{% translate 'Logout' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -1,10 +1,16 @@
|
||||
<div id="generic-offcanvas" class="offcanvas offcanvas-end offcanvas-size-xl" data-bs-backdrop="static"
|
||||
<div id="generic-offcanvas" class="offcanvas offcanvas-end offcanvas-size-xl"
|
||||
data-bs-backdrop="static"
|
||||
tabindex="-1"
|
||||
_="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end
|
||||
on hide_offcanvas call bootstrap.Offcanvas.getOrCreateInstance(me).hide() end
|
||||
on htmx:beforeOnLoad[detail.boosted] call bootstrap.Offcanvas.getOrCreateInstance(me).hide() then log event
|
||||
on hidden.bs.offcanvas set my innerHTML to '' end">
|
||||
</div>
|
||||
<div id="generic-offcanvas-left" class="offcanvas offcanvas-start offcanvas-size-xl" data-bs-backdrop="static"
|
||||
<div id="generic-offcanvas-left" class="offcanvas offcanvas-start offcanvas-size-xl"
|
||||
data-bs-backdrop="static"
|
||||
tabindex="-1"
|
||||
_="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end
|
||||
on hide_offcanvas call bootstrap.Offcanvas.getOrCreateInstance(me).hide() end
|
||||
on htmx:beforeOnLoad[detail.boosted] call bootstrap.Offcanvas.getOrCreateInstance(me).hide() then log event
|
||||
on hidden.bs.offcanvas set my innerHTML to '' end">
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
{% load webpack_loader %}
|
||||
|
||||
{% javascript_pack 'bootstrap' %}
|
||||
{% javascript_pack 'sweetalert2' %}
|
||||
{% javascript_pack 'htmx' %}
|
||||
{% javascript_pack 'jquery' %}
|
||||
{% javascript_pack 'bootstrap' attrs="defer" %}
|
||||
{% javascript_pack 'sweetalert2' attrs="defer" %}
|
||||
{% javascript_pack 'select' attrs="defer" %}
|
||||
|
||||
{% include 'includes/scripts/hyperscript/init_tom_select.html' %}
|
||||
{% include 'includes/scripts/hyperscript/hide_amount.html' %}
|
||||
{% include 'includes/scripts/hyperscript/toast.html' %}
|
||||
{% include 'includes/scripts/hyperscript/htmx_error_handler.html' %}
|
||||
|
||||
{% javascript_pack 'htmx' attrs="defer" %}
|
||||
|
||||
<script>
|
||||
let tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
if (!tz) {
|
||||
tz = "UTC"
|
||||
}
|
||||
document.cookie = "mytz=" + tz + ";path=/";
|
||||
</script>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script type="text/hyperscript">
|
||||
behavior hide_amounts
|
||||
on load or htmx:afterSwap if I include #settings-hide-amounts
|
||||
set elements to <.amount/> in me
|
||||
for el in elements
|
||||
set el.textContent to '•••••••••••'
|
||||
end
|
||||
end
|
||||
|
||||
on load or htmx:afterSwap if I do not include #settings-hide-amounts
|
||||
set elements to <.amount/> in me
|
||||
for el in elements
|
||||
set el.textContent to el.dataset.originalValue
|
||||
end
|
||||
end
|
||||
|
||||
on click[target matches .amount] if I include #settings-hide-amounts
|
||||
if event.target do not matches .revealed then set event.target.textContent to event.target.dataset.originalValue
|
||||
else set event.target.textContent to '•••••••••••' end
|
||||
then toggle .revealed on event.target
|
||||
end
|
||||
end
|
||||
</script>
|
||||
@@ -0,0 +1,8 @@
|
||||
<script type="text/hyperscript">
|
||||
behavior htmx_error_handler
|
||||
on htmx:responseError or htmx:afterRequest[detail.failed] remove .invisible .visually-hidden .swing-in-top-fwd from
|
||||
#loading-error then log event
|
||||
on htmx:afterRequest[detail.successful] add .invisible .visually-hidden .swing-in-top-fwd to #loading-error then
|
||||
log 'oi'
|
||||
end
|
||||
</script>
|
||||
@@ -1,24 +0,0 @@
|
||||
<script type="text/hyperscript">
|
||||
behavior initTomSelect
|
||||
init
|
||||
set selectmultiple to .selectmultiple in me
|
||||
set select to .select in me
|
||||
set selectcsv to .csvselect in me
|
||||
for x in selectmultiple
|
||||
js(it)
|
||||
new TomSelect(it, tomselect_multiple)
|
||||
end
|
||||
end
|
||||
for x in select
|
||||
js(it)
|
||||
new TomSelect(it, tomselect_single)
|
||||
end
|
||||
end
|
||||
for x in selectcsv
|
||||
js(it)
|
||||
new TomSelect(it, tomselect_single)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
</script>
|
||||
@@ -0,0 +1,12 @@
|
||||
<script type="text/hyperscript">
|
||||
behavior init_tom_select
|
||||
init
|
||||
set selects to <select/> in me
|
||||
for x in selects
|
||||
js(it)
|
||||
TomSelect(it)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
</script>
|
||||
@@ -0,0 +1,6 @@
|
||||
<script type="text/hyperscript">
|
||||
behavior toast
|
||||
on mouseenter call bootstrap.Tooltip.getOrCreateInstance(me).show() end
|
||||
on mouseleave or click call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
end
|
||||
</script>
|
||||
@@ -1,3 +1,3 @@
|
||||
<div id="toasts" hx-get="{% url 'toasts' %}"
|
||||
hx-trigger="load, toast from:window">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,26 +1,45 @@
|
||||
{% load i18n %}
|
||||
{% load title %}
|
||||
{% load webpack_loader %}
|
||||
{% load tz_detect %}
|
||||
<!doctype html>
|
||||
<html lang="en" data-bs-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}WYGIWYH{% endblock %}</title>
|
||||
<title>
|
||||
{% filter site_title %}
|
||||
{% block title %}
|
||||
{% endblock title %}
|
||||
{% endfilter %}
|
||||
</title>
|
||||
|
||||
{% include 'includes/styles.html' %}
|
||||
{% block extra_styles %}{% endblock %}
|
||||
|
||||
{% tz_detect %}
|
||||
|
||||
{% include 'includes/scripts.html' %}
|
||||
{% block extra_js_head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
{% block content %}{% endblock %}
|
||||
<body class="font-monospace">
|
||||
|
||||
<div _="install hide_amounts
|
||||
install htmx_error_handler
|
||||
{% block body_hyperscript %}{% endblock %}">
|
||||
{% include 'includes/navbar.html' %}
|
||||
|
||||
<div class="container invisible visually-hidden" id="loading-error">
|
||||
<div class="alert alert-danger m-3" role="alert">
|
||||
{% translate 'Something went wrong loading your data. Try reloading the page or check the console for more information.' %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'includes/offcanvas.html' %}
|
||||
{% include 'includes/toasts.html' %}
|
||||
|
||||
{% include 'includes/scripts.html' %}
|
||||
{% block extra_js %}{% endblock %}
|
||||
|
||||
{% block extra_js_body %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
{% load title %}
|
||||
{% load webpack_loader %}
|
||||
<!doctype html>
|
||||
<html lang="en" data-bs-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>
|
||||
{% filter site_title %}
|
||||
{% block title %}
|
||||
{% endblock title %}
|
||||
{% endfilter %}
|
||||
</title>
|
||||
|
||||
{% include 'includes/styles.html' %}
|
||||
{% block extra_styles %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{% include 'includes/toasts.html' %}
|
||||
|
||||
{% include 'includes/scripts.html' %}
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,38 @@
|
||||
{% load natural %}
|
||||
{% load i18n %}
|
||||
{% regroup transactions by date|customnaturaldate as transactions_by_date %}
|
||||
|
||||
<div>
|
||||
{% for x in transactions_by_date %}
|
||||
<div>
|
||||
<div class="my-3 w-100 tw-text-base border-bottom bg-body">
|
||||
<a class="text-decoration-none d-inline-block w-100"
|
||||
role="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#{{ x.grouper|slugify }}"
|
||||
aria-expanded="true"
|
||||
aria-controls="collapseExample">
|
||||
{{ x.grouper }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse show" id="{{ x.grouper|slugify }}">
|
||||
<div class="ps-3">
|
||||
{% for trans in x.list %}
|
||||
{% include 'transactions/fragments/item.html' with transaction=trans %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% empty %}
|
||||
<div class="row p-5">
|
||||
<div class="col p-5">
|
||||
<div class="text-center">
|
||||
<i class="fa-solid fa-circle-xmark tw-text-6xl"></i>
|
||||
<p class="lead mt-4 mb-0">{% translate "No transactions this month" %}</p>
|
||||
<p class="tw-text-gray-500">{% translate "Try adding one" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -0,0 +1,49 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load month_name %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate 'Pick a month' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% regroup month_year_data by year as years_list %}
|
||||
|
||||
<ul class="nav nav-pills nav-fill" id="yearTabs" role="tablist">
|
||||
{% for x in years_list %}
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link{% if x.grouper == current_year %} active{% endif %}"
|
||||
id="{{ x.grouper }}"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#{{ x.grouper }}-pane"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="{{ x.grouper }}-pane"
|
||||
aria-selected="{% if x.grouper == current_year %}true{% else %}false{% endif %}">
|
||||
{{ x.grouper }}
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="tab-content" id="yearTabsContent" hx-boost="true">
|
||||
{% for x in years_list %}
|
||||
<div class="tab-pane fade{% if x.grouper == current_year %} show active{% endif %} mt-2"
|
||||
id="{{ x.grouper }}-pane"
|
||||
role="tabpanel"
|
||||
aria-labelledby="{{ x.grouper }}"
|
||||
tabindex="0">
|
||||
<ul class="list-group list-group-flush" id="month-year-list">
|
||||
{% for month_data in x.list %}
|
||||
<li class="list-group-item hover:tw-bg-zinc-900
|
||||
{% if month_data.month == current_month and month_data.year == current_year %} disabled bg-primary{% endif %}"
|
||||
{% if month_data.month == current_month and month_data.year == current_year %}aria-disabled="true"{% endif %}>
|
||||
<a class="text-decoration-none stretched-link {% if month_data.month == current_month and month_data.year == current_year %} text-black{% endif %}"
|
||||
href="{% url "monthly_overview" month=month_data.month year=month_data.year %}">
|
||||
{{ month_data.month|month_name }}
|
||||
<span class="badge text-bg-secondary">{{ month_data.transaction_count }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,166 @@
|
||||
{% load i18n %}
|
||||
{% load currency_display %}
|
||||
<div class="row row-cols-1 g-4 mb-3">
|
||||
{# Daily Spending#}
|
||||
<div class="col">
|
||||
<div class="card tw-relative h-100 shadow">
|
||||
<div class="tw-absolute tw-h-8 tw-w-8 tw-right-2 tw-top-2 tw-bg-yellow-300 tw-text-yellow-800 text-center
|
||||
align-items-center d-flex justify-content-center rounded-2">
|
||||
<i class="fa-solid fa-calendar-day"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="tw-text-yellow-400 fw-bold">{% translate 'Daily Spending Allowance' %}{% include 'includes/help_icon.html' with content=_('This is the final total divided by the remaining days in the month') %}</h5>
|
||||
<div class="d-flex justify-content-between mt-3">
|
||||
<div class="text-end font-monospace">
|
||||
<div class="tw-text-gray-400">{% translate 'today' %}</div>
|
||||
</div>
|
||||
<div class="text-start font-monospace">
|
||||
{% for entry in totals.daily_spending_allowance %}
|
||||
<div class="amount" data-original-value="{% entry_amount entry %}"></div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# Income#}
|
||||
<div class="col">
|
||||
<div class="card tw-relative h-100 shadow">
|
||||
<div class="tw-absolute tw-h-8 tw-w-8 tw-right-2 tw-top-2 tw-bg-green-300 tw-text-green-800 text-center
|
||||
align-items-center d-flex justify-content-center rounded-2">
|
||||
<i class="fa-solid fa-arrow-right-to-bracket"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="tw-text-green-400 fw-bold">{% translate 'Income' %}</h5>
|
||||
<div class="d-flex justify-content-between mt-3">
|
||||
<div class="text-end font-monospace">
|
||||
<div class="tw-text-gray-400">current</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between text-start font-monospace">
|
||||
{% for entry in totals.paid_income %}
|
||||
<div class="amount" data-original-value="{% entry_amount entry %}"></div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-1">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="text-end font-monospace">
|
||||
<div class="tw-text-gray-400">projected</div>
|
||||
</div>
|
||||
<div class="text-start font-monospace">
|
||||
{% for entry in totals.projected_income %}
|
||||
<div class="amount" data-original-value="{% entry_amount entry %}"></div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# Expenses#}
|
||||
<div class="col">
|
||||
<div class="card tw-relative h-100 shadow">
|
||||
<div class="tw-absolute tw-h-8 tw-w-8 tw-right-2 tw-top-2 tw-bg-red-300 tw-text-red-800 text-center
|
||||
align-items-center d-flex justify-content-center rounded-2">
|
||||
<i class="fa-solid fa-arrow-right-from-bracket"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="tw-text-red-400">{% translate 'Expenses' %}</h5>
|
||||
<div class="d-flex justify-content-between mt-3">
|
||||
<div class="text-end font-monospace">
|
||||
<div class="tw-text-gray-400">current</div>
|
||||
</div>
|
||||
<div class="text-start font-monospace">
|
||||
{% for entry in totals.paid_expenses %}
|
||||
<div class="amount" data-original-value="{% entry_amount entry %}"></div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-1">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="text-end font-monospace">
|
||||
<div class="tw-text-gray-400">projected</div>
|
||||
</div>
|
||||
<div class="text-start font-monospace">
|
||||
{% for entry in totals.projected_expenses %}
|
||||
<div class="amount" data-original-value="{% entry_amount entry %}"></div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# Total#}
|
||||
<div class="col">
|
||||
<div class="card tw-relative h-100 shadow">
|
||||
<div class="tw-absolute tw-h-8 tw-w-8 tw-right-2 tw-top-2 tw-bg-blue-300 tw-text-blue-800 text-center
|
||||
align-items-center d-flex justify-content-center rounded-2">
|
||||
<i class="fa-solid fa-scale-balanced"></i>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="tw-text-blue-400">{% translate 'Total' %}</h5>
|
||||
<div class="d-flex justify-content-between mt-3">
|
||||
<div class="text-end font-monospace">
|
||||
<div class="tw-text-gray-400">current</div>
|
||||
</div>
|
||||
<div class="text-start font-monospace">
|
||||
{% for entry in totals.total_current %}
|
||||
<div class="amount" data-original-value="{% entry_amount entry %}"></div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-3">
|
||||
<div class="text-end font-monospace">
|
||||
<div class="tw-text-gray-400">projected</div>
|
||||
</div>
|
||||
<div class="text-start font-monospace">
|
||||
{% for entry in totals.total_projected %}
|
||||
<div class="amount" data-original-value="{% entry_amount entry %}"></div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-1">
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="text-start font-monospace">
|
||||
{% for entry in totals.total_final %}
|
||||
<div class="amount" data-original-value="{% entry_amount entry %}"></div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#<div class="p-2 rounded-2 shadow tw-text-sm card mt-4">#}
|
||||
{# <p class="font-monospace text-light text-uppercase text-center fw-bold m-0 tw-text-base">#}
|
||||
{# {% translate "Account Overview" %}</p>#}
|
||||
{# <hr class="my-1">#}
|
||||
{# <div>#}
|
||||
{# {% for account in account_summary %}#}
|
||||
{# <div class="row">#}
|
||||
{# <div class="col-6">#}
|
||||
{# <div class="font-monospace text-primary text-start align-self-end fw-bold m-0">{{ account.name }}</div>#}
|
||||
{# </div>#}
|
||||
{# <div class="col-6 text-end font-monospace">#}
|
||||
{# <div class="amount" data-original-value="{% currency_display amount=account.balance prefix=account.currency__prefix suffix=account.currency__suffix decimal_places=account.currency__decimal_places %}"></div>#}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
{# <div class="my-1"></div>#}
|
||||
{# {% endfor %}#}
|
||||
{# </div>#}
|
||||
{#</div>#}
|
||||
@@ -0,0 +1,125 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load i18n %}
|
||||
{% load month_name %}
|
||||
{% load static %}
|
||||
{% load webpack_loader %}
|
||||
|
||||
{% block title %}Monthly Overview :: {{ month|month_name }}/{{ year }}{% endblock %}
|
||||
|
||||
{% block body_hyperscript %}
|
||||
on keyup[code is 'KeyE' and target.nodeName is 'BODY'] from body trigger 'add_expense' end
|
||||
on keyup[code is 'KeyI' and target.nodeName is 'BODY'] from body trigger 'add_income' end
|
||||
on keyup[code is 'KeyB' and target.nodeName is 'BODY'] from body trigger 'balance' end
|
||||
on keyup[code is 'KeyT' and target.nodeName is 'BODY'] from body trigger 'add_transfer' end
|
||||
on keyup[code is 'KeyN' and target.nodeName is 'BODY'] from body trigger 'installment' end
|
||||
on keyup[code is 'ArrowLeft' and target.nodeName is 'BODY'] from body trigger 'previous_month' end
|
||||
on keyup[code is 'ArrowRight' and target.nodeName is 'BODY'] from body trigger 'next_month' end
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container px-md-3 py-3 column-gap-5">
|
||||
<div class="row mb-3 gx-xl-4 gy-3 mb-4">
|
||||
{# Date picker#}
|
||||
<div class="col-12 col-xl-4 flex-row align-items-center d-flex">
|
||||
<div class="tw-text-base h-100 align-items-center d-flex">
|
||||
<a role="button"
|
||||
class="pe-4 py-2"
|
||||
hx-boost="true"
|
||||
hx-trigger="click, previous_month from:window"
|
||||
href="{% url 'monthly_overview' month=previous_month year=previous_year %}"><i
|
||||
class="fa-solid fa-chevron-left"></i></a>
|
||||
</div>
|
||||
<div class="tw-text-3xl fw-bold font-monospace tw-w-full text-center"
|
||||
hx-get="{% url 'available_dates' %}"
|
||||
hx-target="#generic-offcanvas-left"
|
||||
hx-trigger="click, date_picker from:window"
|
||||
hx-vals='{"month": {{ month }}, "year": {{ year }}}' role="button">
|
||||
{{ month|month_name }} {{ year }}
|
||||
</div>
|
||||
<div class="tw-text-base mx-2 h-100 align-items-center d-flex">
|
||||
<a role="button"
|
||||
class="ps-3 py-2"
|
||||
hx-boost="true"
|
||||
hx-trigger="click, next_month from:window"
|
||||
href="{% url 'monthly_overview' month=next_month year=next_year %}">
|
||||
<i class="fa-solid fa-chevron-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{# Action buttons#}
|
||||
<div class="col-12 col-xl-8">
|
||||
<div class="d-grid gap-2 d-xl-flex justify-content-xl-end">
|
||||
<button class="btn btn-sm btn-outline-success"
|
||||
hx-get="{% url 'transaction_add' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
hx-trigger="click, add_income from:window"
|
||||
hx-vals='{"year": {{ year }}, "month": {{ month }}, "type": "IN"}'>
|
||||
<i class="fa-solid fa-arrow-right-to-bracket me-2"></i>
|
||||
{% translate "Income" %}
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger"
|
||||
hx-get="{% url 'transaction_add' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
hx-trigger="click, add_expense from:window"
|
||||
hx-vals='{"year": {{ year }}, "month": {{ month }}, "type": "EX"}'>
|
||||
<i class="fa-solid fa-arrow-right-from-bracket me-2"></i>
|
||||
{% translate "Expense" %}
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-warning"
|
||||
hx-get="{% url 'installments_add' %}"
|
||||
hx-trigger="click, installment from:window"
|
||||
hx-target="#generic-offcanvas">
|
||||
<i class="fa-solid fa-divide me-2"></i>
|
||||
{% translate "Installment" %}
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-info"
|
||||
hx-get="{% url 'transactions_transfer' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
hx-trigger="click, add_transfer from:window"
|
||||
hx-vals='{"year": {{ year }}, "month": {{ month }}}'>
|
||||
<i class="fa-solid fa-money-bill-transfer me-2"></i>
|
||||
{% translate "Transfer" %}
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-info"
|
||||
hx-get="{% url 'account_reconciliation' %}"
|
||||
hx-trigger="click, balance from:window"
|
||||
hx-target="#generic-offcanvas">
|
||||
<i class="fa-solid fa-scale-balanced me-2"></i>
|
||||
{% translate "Balance" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# Monthly summary#}
|
||||
<div class="row gx-xl-4 gy-3">
|
||||
<div class="col-12 col-xl-4 order-0 order-xl-2">
|
||||
<div id="summary" hx-get="{% url 'monthly_summary' month=month year=year %}" class=" sticky-sidebar"
|
||||
hx-trigger="load, transaction_updated from:window, monthly_summary_update from:window">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-8 order-2 order-xl-1">
|
||||
{# Filter transactions#}
|
||||
<div class="row mb-1">
|
||||
<div class="col-12">
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary dropdown-toggle" data-bs-toggle="dropdown"
|
||||
aria-expanded="false" data-bs-auto-close="false">
|
||||
<i class="fa-solid fa-filter fa-fw me-2"></i>{% translate 'Filter transactions' %}
|
||||
</button>
|
||||
<form hx-get="{% url 'monthly_transactions_list' month=month year=year %}" hx-trigger="change, submit, search"
|
||||
hx-target="#transactions" id="filter" hx-indicator="#transactions" class="dropdown-menu p-4
|
||||
w-lg-50 tw-w-full lg:tw-w-2/4"
|
||||
_="install init_tom_select">
|
||||
{% crispy filter.form %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="transactions"
|
||||
hx-get="{% url 'monthly_transactions_list' month=month year=year %}"
|
||||
hx-trigger="load, transaction_updated from:window" hx-include="#filter"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,133 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% load currency_display %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load i18n %}
|
||||
{% load month_name %}
|
||||
{% load static %}
|
||||
{% load webpack_loader %}
|
||||
|
||||
{% block title %}Net Worth{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container px-md-3 py-3 column-gap-5">
|
||||
<div class="row">
|
||||
<div class="col-6 row-gap-5">
|
||||
{% for currency in currency_net_worth %}
|
||||
<div class="row">
|
||||
<div class="card-body">
|
||||
<div class="card-title">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
{{ currency.name }}
|
||||
</div>
|
||||
<div class="col-6 text-end">
|
||||
<div class="amount" data-original-value="{% currency_display amount=currency.amount prefix=currency.prefix suffix=currency.suffix decimal_places=currency.decimal_places %}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="col-6">oi</div>
|
||||
</div>
|
||||
</div>
|
||||
{#<canvas id="chart" width="500" height="300"></canvas>#}
|
||||
{##}
|
||||
{#<script>#}
|
||||
{# let ctx = document.getElementById("chart").getContext("2d");#}
|
||||
{##}
|
||||
{#let chart = new Chart(ctx, {#}
|
||||
{# type: 'line',#}
|
||||
{# responsive: true,#}
|
||||
{# data: {#}
|
||||
{# labels: ["2020/01", "2020/02", "2020/03", "2020/04", "2020/05"],#}
|
||||
{# datasets: [#}
|
||||
{# {#}
|
||||
{# label: "Gross volume ($)",#}
|
||||
{# data: [0.0, 0.8457, 1, 0.5],#}
|
||||
{# yAxisID: 'y1',#}
|
||||
{# },#}
|
||||
{# {#}
|
||||
{# label: "Gross volume ($) 2",#}
|
||||
{# data: [85000, 89000, 96000, 100000],#}
|
||||
{# yAxisID: 'y2',#}
|
||||
{# }#}
|
||||
{# ]#}
|
||||
{# },#}
|
||||
{# options: {#}
|
||||
{# title: {#}
|
||||
{# text: "Gross Volume in 2020",#}
|
||||
{# display: true#}
|
||||
{# },#}
|
||||
{# responsive: true,#}
|
||||
{# interaction: {#}
|
||||
{# mode: 'index',#}
|
||||
{# intersect: false,#}
|
||||
{# scales: {#}
|
||||
{# y1: {#}
|
||||
{# ticks: {#}
|
||||
{# display: false // This hides the y-axis labels#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
{# },#}
|
||||
{# },#}
|
||||
{# scales: {#}
|
||||
{# x: {#}
|
||||
{# display: true,#}
|
||||
{# ticks: {#}
|
||||
{# display: false // This hides the y-axis labels#}
|
||||
{# }#}
|
||||
{# },#}
|
||||
{# y1: {#}
|
||||
{# display: true,#}
|
||||
{# type: 'logarithmic',#}
|
||||
{# ticks: {#}
|
||||
{# display: false // This hides the y-axis labels#}
|
||||
{# }#}
|
||||
{# },#}
|
||||
{# y2: {#}
|
||||
{# type: 'logarithmic',#}
|
||||
{# display: false,#}
|
||||
{# position: 'left',#}
|
||||
{# ticks: {#}
|
||||
{# display: false,#}
|
||||
{# },#}
|
||||
{##}
|
||||
{# // grid line settings#}
|
||||
{# grid: {#}
|
||||
{# display: false,#}
|
||||
{# drawOnChartArea: false, // only want the grid lines for one axis to show up#}
|
||||
{# }},}#}
|
||||
{#});#}
|
||||
{#new Chart(ctx, {#}
|
||||
{# type: 'line', // e.g., 'line', 'bar', etc.#}
|
||||
{# data: {#}
|
||||
{# labels: ["2020/01", "2020/02", "2020/03", "2020/04", "2020/05"],#}
|
||||
{# datasets: [#}
|
||||
{# {#}
|
||||
{# label: "Gross volume ($)",#}
|
||||
{# data: [0.0, 0.8457, 1, 0.5],#}
|
||||
{# yAxisID: 'y1',#}
|
||||
{# },#}
|
||||
{# {#}
|
||||
{# label: "Gross volume ($) 2",#}
|
||||
{# data: [85000, 89000, 96000, 100000],#}
|
||||
{# yAxisID: 'y2',#}
|
||||
{# }#}
|
||||
{# ]#}
|
||||
{# },#}
|
||||
{# options: {#}
|
||||
{# scales: {#}
|
||||
{# y: {#}
|
||||
{# ticks: {#}
|
||||
{# display: false // This hides the y-axis labels#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
{# }#}
|
||||
{#});#}
|
||||
{#</script>#}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Add tag' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'tag_add' %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Edit tag' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'tag_edit' tag_id=tag.id %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,78 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate 'Tags' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container px-md-3 py-3 column-gap-5">
|
||||
<div class="tw-text-3xl fw-bold font-monospace tw-w-full mb-3">
|
||||
{% spaceless %}
|
||||
<div>{% translate 'Tags' %}<span>
|
||||
<a class="text-decoration-none tw-text-2xl p-1 category-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Add" %}"
|
||||
hx-get="{% url 'tag_add' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="">
|
||||
<i class="fa-solid fa-circle-plus fa-fw"></i></a>
|
||||
</span></div>
|
||||
{% endspaceless %}
|
||||
</div>
|
||||
|
||||
<div class="border p-3 rounded-3">
|
||||
<table class="table table-hover table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for tag in tags %}
|
||||
<tr class="tag">
|
||||
<td class="col-auto">
|
||||
<a class="text-decoration-none tw-text-gray-400 p-1 tag-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'tag_edit' tag_id=tag.id %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="on click send action_clicked to .tag-action in the closest parent .tag end
|
||||
on action_clicked call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
install toast">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="text-danger text-decoration-none p-1 tag-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'tag_delete' tag_id=tag.id %}"
|
||||
hx-trigger='delete_confirmed'
|
||||
_="on click send action_clicked to .tag-action in the closest parent .tag end
|
||||
on action_clicked call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
install toast
|
||||
on click
|
||||
if event.ctrlKey trigger delete_confirmed
|
||||
else
|
||||
call Swal.fire({title: '{% translate 'Are you sure?' %}',
|
||||
text: '{% blocktranslate %}You won\'t be able to revert this!{% endblocktranslate %}',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
cancelButtonText: '{% blocktranslate %}Cancel{% endblocktranslate %}',
|
||||
confirmButtonText: '{% blocktranslate %}Yes, delete it!{% endblocktranslate %}',
|
||||
customClass: {
|
||||
confirmButton: 'btn btn-primary me-3',
|
||||
cancelButton: 'btn btn-danger'
|
||||
},
|
||||
buttonsStyling: false})
|
||||
if result.isConfirmed trigger delete_confirmed"><i class="fa-solid fa-trash fa-fw"></i></a></td>
|
||||
<td class="col">{{ tag.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'transaction_add' %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% csrf_token %}
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Add Installment Plan' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'installments_add' %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'transaction_edit' transaction_id=transaction.id %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% csrf_token %}
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}{% translate 'Edit transaction' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'installments_edit' pk=installment_plan.id %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -1,69 +1,103 @@
|
||||
{% load i18n %}
|
||||
{% load currency_display %}
|
||||
<div class="tw-border-s-2 tw-border-e-0 tw-border-t-0 tw-border-b-0 border-bottom
|
||||
<div class="tw-border-s-6 tw-border-e-0 tw-border-t-0 tw-border-b-0 border-bottom
|
||||
hover:tw-bg-zinc-900 p-2 {% if transaction.account.is_asset %}tw-border-dashed{% else %}tw-border-solid{% endif %}
|
||||
{% if transaction.type == "EX" %}tw-border-red-500{% else %}tw-border-green-500{% endif %} transaction tw-relative"
|
||||
hx-trigger="dblclick"
|
||||
hx-get="{% url 'transaction_edit' transaction_id=transaction.id %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="on mouseover remove .tw-invisible from the first .transaction-actions in me end
|
||||
on mouseout add .tw-invisible to the first .transaction-actions in me end">
|
||||
{% if transaction.type == "EX" %}tw-border-red-500{% else %}tw-border-green-500{% endif %} transaction tw-relative
|
||||
my-3"
|
||||
_="on mouseenter remove .tw-invisible from the first .transaction-actions in me end
|
||||
on mouseleave add .tw-invisible to the first .transaction-actions in me end">
|
||||
<div class="row font-monospace tw-text-sm align-items-center">
|
||||
<div class="col-lg-1 col-12 d-flex align-items-center tw-text-2xl lg:tw-text-xl text-lg-center">
|
||||
<div class="col-lg-1 col-12 d-flex align-items-center tw-text-2xl lg:tw-text-xl text-lg-center pay-button">
|
||||
<a class="text-decoration-none my-2 w-100"
|
||||
role="button"
|
||||
hx-get="{% url 'transaction_pay' transaction_id=transaction.id %}"
|
||||
hx-target="closest .transaction"
|
||||
hx-swap="outerHTML">
|
||||
hx-swap="outerHTML"
|
||||
_="on paid
|
||||
js
|
||||
paidSound.pause()
|
||||
paidSound.currentTime = 0
|
||||
paidSound.play()
|
||||
end
|
||||
end
|
||||
on unpaid
|
||||
js
|
||||
unpaidSound.pause()
|
||||
unpaidSound.currentTime = 0
|
||||
unpaidSound.play()
|
||||
end
|
||||
end">
|
||||
{% if transaction.is_paid %}<i class="fa-regular fa-circle-check tw-text-green-400"></i>{% else %}<i
|
||||
class="fa-regular fa-circle tw-text-red-400"></i>{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-lg-8 col-12">
|
||||
{# Date#}
|
||||
<div class="mb-1 tw-text-gray-400">
|
||||
<i class="fa-solid fa-calendar fa-fw me-1 fa-xs"></i>{{ transaction.date|date:"SHORT_DATE_FORMAT" }}</div>
|
||||
<div class="mb-1 text-white tw-text-base">{{ transaction.description }}</div>
|
||||
<div class="text-white-50 mb-2 d-flex align-items-center">
|
||||
<i class="fa-solid fa-calendar fa-fw me-1 fa-xs"></i>
|
||||
{{ transaction.date|date:"SHORT_DATE_FORMAT" }}</div>
|
||||
<div class="text-white-50 mb-1 tw-text-xs">
|
||||
{% for tag in transaction.tags.all %}
|
||||
<span><i class="fa-solid fa-hashtag tw-text-gray-400"></i>{{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="tw-text-sm mb-1 mb-lg-0 tw-text-gray-400 font-normal">
|
||||
<i class="fa-solid fa-note-sticky me-1"></i>{{ transaction.notes | linebreaksbr }}
|
||||
</div>
|
||||
<div class="tw-text-sm mb-1 mb-lg-0">
|
||||
{% if transaction.category %}
|
||||
<i class="fa-solid fa-icons text-primary"></i>
|
||||
<span class="badge rounded-0
|
||||
{% if transaction.category.mute %}text-bg-secondary{% else %}text-bg-light{% endif %}">
|
||||
{{ transaction.category.name }}
|
||||
</span>
|
||||
<div class="tw-text-gray-400 tw-text-sm">
|
||||
{# Notes#}
|
||||
{% if transaction.notes %}
|
||||
<div class="mb-1 tw-text-gray-400">
|
||||
<i class="fa-solid fa-align-left fa-fw me-1 fa-xs"></i>{{ transaction.notes | linebreaksbr }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if transaction.category %}
|
||||
<div class="mb-1">
|
||||
{% spaceless %}
|
||||
<i class="fa-solid fa-icons fa-fw me-1 fa-xs"></i>
|
||||
<span class="">{{ transaction.category.name }}</span>
|
||||
{% endspaceless %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{# Tags#}
|
||||
{% with transaction.tags.all as tags %}
|
||||
{% if tags %}
|
||||
<div class="mb-1">
|
||||
{% for tag in tags %}
|
||||
<span><i class="fa-solid fa-hashtag fa-fw fa-xs"></i>{{ tag.name }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-3 col-12 text-lg-end align-self-end">
|
||||
<div class="{% if transaction.type == "EX" %}tw-text-red-400{% else %}tw-text-green-400{% endif %}">
|
||||
{% transaction_amount transaction %}
|
||||
<div class="amount" data-original-value="{% transaction_amount transaction %}"></div>
|
||||
</div>
|
||||
{# Exchange Rate#}
|
||||
{% with exchanged=transaction.exchanged_amount %}
|
||||
{% if exchanged %}
|
||||
<div class="tw-text-gray-500 amount"
|
||||
data-original-value="{% currency_display amount=exchanged.amount prefix=exchanged.prefix suffix=exchanged.suffix decimal_places=exchanged.decimal_places %}">
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<div>{{ transaction.account.name }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="transaction-actions !tw-absolute tw-top-0 tw-right-0 tw-invisible tw-text-base d-none
|
||||
d-xl-flex">
|
||||
{# Item actions#}
|
||||
<div class="transaction-actions !tw-absolute tw-left-2/4 tw--top-1/3 tw-invisible d-none
|
||||
d-xl-flex flex-xl-row tw-text-base card">
|
||||
<a class="text-decoration-none tw-text-gray-400 p-2 transaction-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'transaction_edit' transaction_id=transaction.id %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="install toast">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="text-danger text-decoration-none p-2 transaction-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-get="{% url 'transaction_delete' transaction_id=transaction.id %}"
|
||||
hx-trigger='confirmed'
|
||||
_="on click send transaction_action_clicked to .transaction-action in the closest parent .transaction end
|
||||
on transaction_action_clicked call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
on mouseenter call bootstrap.Tooltip.getOrCreateInstance(me) end
|
||||
on mouseleave call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
hx-delete="{% url 'transaction_delete' transaction_id=transaction.id %}"
|
||||
hx-trigger='delete_confirmed'
|
||||
_="install toast
|
||||
on click
|
||||
if event.shiftKey trigger confirmed
|
||||
if event.ctrlKey trigger delete_confirmed
|
||||
else
|
||||
call Swal.fire({title: '{% translate 'Are you sure?' %}',
|
||||
text: '{% blocktranslate %}You won\'t be able to revert this!{% endblocktranslate %}',
|
||||
@@ -78,19 +112,9 @@ hover:tw-bg-zinc-900 p-2 {% if transaction.account.is_asset %}tw-border-dashed{%
|
||||
cancelButton: 'btn btn-danger'
|
||||
},
|
||||
buttonsStyling: false})
|
||||
if result.isConfirmed trigger confirmed"><i class="fa-solid fa-trash fa-fw"></i></a>
|
||||
<a class="text-decoration-none tw-text-gray-400 p-2 transaction-action"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'transaction_edit' transaction_id=transaction.id %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
_="on click send transaction_action_clicked to .transaction-action in the closest parent .transaction end
|
||||
on transaction_action_clicked call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end
|
||||
on mouseenter call bootstrap.Tooltip.getOrCreateInstance(me) end
|
||||
on mouseleave call bootstrap.Tooltip.getOrCreateInstance(me).dispose() end">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
if result.isConfirmed trigger delete_confirmed"><i class="fa-solid fa-trash fa-fw"></i></a>
|
||||
</div>
|
||||
{# Item actions dropdown fallback for mobile#}
|
||||
<div class="dropdown !tw-absolute tw-top-0 tw-right-0 xl:tw-invisible">
|
||||
<a class="btn tw-text-2xl lg:tw-text-sm lg:tw-border-none text-light" type="button"
|
||||
data-bs-toggle="dropdown"
|
||||
@@ -106,8 +130,8 @@ hover:tw-bg-zinc-900 p-2 {% if transaction.account.is_asset %}tw-border-dashed{%
|
||||
</a></li>
|
||||
<li><a class="dropdown-item"
|
||||
role="button"
|
||||
hx-get="{% url 'transaction_delete' transaction_id=transaction.id %}"
|
||||
hx-trigger='confirmed'
|
||||
hx-delete="{% url 'transaction_delete' transaction_id=transaction.id %}"
|
||||
hx-trigger='delete_confirmed'
|
||||
_="on click
|
||||
call Swal.fire({title: '{% translate 'Are you sure?' %}',
|
||||
text: '{% blocktranslate %}You won\'t be able to revert this!{% endblocktranslate %}',
|
||||
@@ -122,7 +146,7 @@ hover:tw-bg-zinc-900 p-2 {% if transaction.account.is_asset %}tw-border-dashed{%
|
||||
cancelButton: 'btn btn-danger'
|
||||
},
|
||||
buttonsStyling: false})
|
||||
if result.isConfirmed trigger confirmed">
|
||||
if result.isConfirmed trigger delete_confirmed">
|
||||
<i class="fa-solid fa-trash me-3"></i>{% translate "Delete" %}
|
||||
</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{% load i18n %}
|
||||
<div>
|
||||
{% for trans in transactions %}
|
||||
{% include 'transactions/fragments/item.html' with transaction=trans %}
|
||||
{% empty %}
|
||||
<div class="row p-5">
|
||||
<div class="col p-5">
|
||||
<div class="text-center">
|
||||
<i class="fa-solid fa-circle-xmark tw-text-6xl"></i>
|
||||
<p class="lead mt-4 mb-0">{% translate "No transactions this month" %}</p>
|
||||
<p class="tw-text-gray-500">{% translate "Try adding one" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -1,46 +0,0 @@
|
||||
{% extends 'extends/offcanvas.html' %}
|
||||
{% load month_name %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate 'Pick one' %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<ul class="nav nav-pills nav-fill" id="yearTabs" role="tablist">
|
||||
{% for date in available_years %}
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link{% if date.year == current_year %} active{% endif %}"
|
||||
id="{{ date.year }}"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#{{ date.year }}-pane"
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-controls="{{ date.year }}-pane"
|
||||
aria-selected="{% if date.year == current_year %}true{% else %}false{% endif %}">
|
||||
{{ date.year }}
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="tab-content" id="yearTabsContent">
|
||||
{% for date in available_years %}
|
||||
<div class="tab-pane fade{% if date.year == current_year %} show active{% endif %} mt-2"
|
||||
id="{{ date.year }}-pane"
|
||||
role="tabpanel"
|
||||
aria-labelledby="{{ date.year }}"
|
||||
tabindex="0">
|
||||
<ul class="list-group list-group-flush" id="month-year-list">
|
||||
{% for month in months %}
|
||||
<li class="list-group-item hover:tw-bg-zinc-900
|
||||
{% if month == current_month and date.year == current_year %} disabled bg-primary{% endif %}"
|
||||
{% if month == current_month and date.year == current_year %}aria-disabled="true"{% endif %}>
|
||||
<a class="text-decoration-none stretched-link {% if month == current_month and date.year == current_year %} text-black{% endif %}"
|
||||
href="{% url "transactions_overview" month=month year=date.year %}">
|
||||
{{ month|month_name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,135 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% load currency_display %}
|
||||
<div class="text-bg-secondary p-2 rounded-2 shadow tw-text-sm">
|
||||
<p class="font-monospace text-light text-uppercase text-center fw-bold m-0 tw-text-base">{% translate "Summary" %}</p>
|
||||
<hr class="my-1">
|
||||
<div>
|
||||
<p class="font-monospace text-uppercase text-center fw-bold m-0">{% translate "Income" %}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="font-monospace text-primary text-start align-self-end fw-bold m-0">
|
||||
{% translate "Current Income" %}</div>
|
||||
</div>
|
||||
<div class="col-6 text-end font-monospace">
|
||||
{% for entry in totals.paid_income %}
|
||||
<div>{% entry_amount entry %}</div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3"></div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="font-monospace text-primary text-start align-self-end fw-bold m-0">
|
||||
{% translate "Projected Income" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 text-end font-monospace">
|
||||
{% for entry in totals.projected_income %}
|
||||
<div>{% entry_amount entry %}</div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-1">
|
||||
<div>
|
||||
<p class="font-monospace text-uppercase text-center fw-bold m-0">{% translate "Expenses" %}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="font-monospace text-primary text-start align-self-end fw-bold m-0">
|
||||
{% translate "Current Expenses" %}</div>
|
||||
</div>
|
||||
<div class="col-6 text-end font-monospace">
|
||||
{% for entry in totals.paid_expenses %}
|
||||
<div>{% entry_amount entry %}</div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3"></div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="font-monospace text-primary text-start align-self-end fw-bold m-0">
|
||||
{% translate "Projected Expenses" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 text-end font-monospace">
|
||||
{% for entry in totals.projected_expenses %}
|
||||
<div>{% entry_amount entry %}</div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-1">
|
||||
<div>
|
||||
<p class="font-monospace text-uppercase text-center fw-bold m-0">{% translate "Totals" %}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="font-monospace text-primary text-start align-self-end fw-bold m-0">
|
||||
{% translate "Current Total" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 text-end font-monospace">
|
||||
{% for entry in totals.total_current %}
|
||||
<div>{% entry_amount entry %}</div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3"></div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="font-monospace text-primary text-start align-self-end fw-bold m-0">
|
||||
{% translate "Projected Total" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 text-end font-monospace">
|
||||
{% for entry in totals.total_projected %}
|
||||
<div>{% entry_amount entry %}</div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="my-3"></div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="font-monospace text-primary text-start align-self-end fw-bold m-0">
|
||||
{% translate "Final Total" %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 text-end font-monospace">
|
||||
{% for entry in totals.total_final %}
|
||||
<div>{% entry_amount entry %}</div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-1">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="font-monospace text-primary text-start align-self-end fw-bold m-0">{% translate "Daily Spending Allowance" %}</div>
|
||||
</div>
|
||||
<div class="col-6 text-end font-monospace">
|
||||
{% for entry in totals.daily_spending_allowance %}
|
||||
<div>{% entry_amount entry %}</div>
|
||||
{% empty %}
|
||||
<div>-</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
{% block body %}
|
||||
<form hx-post="{% url 'transactions_transfer' %}" hx-target="#generic-offcanvas" novalidate>
|
||||
{% csrf_token %}
|
||||
{% crispy form %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load month_name %}
|
||||
{% load static %}
|
||||
{% load webpack_loader %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid px-md-3 py-5 column-gap-5">
|
||||
<div class="row mb-3">
|
||||
<div class="col flex-row align-items-center d-flex">
|
||||
<div class="tw-text-base mx-2 h-100 align-items-center d-flex">
|
||||
<a role="button" href="
|
||||
{% url 'transactions_overview' month=previous_month year=previous_year %}"><i
|
||||
class="fa-solid fa-chevron-left"></i></a>
|
||||
</div>
|
||||
<div class="tw-text-3xl fw-bold font-monospace tw-w-full xl:tw-w-72 text-center"
|
||||
hx-get="{% url 'available_dates' %}"
|
||||
hx-target="#generic-offcanvas-left"
|
||||
hx-vals='{"month": {{ month }}, "year": {{ year }}}' role="button">
|
||||
{{ month|month_name }} {{ year }}
|
||||
</div>
|
||||
<div class="tw-text-base mx-2 h-100 align-items-center d-flex">
|
||||
<a role="button"
|
||||
href="{% url 'transactions_overview' month=next_month year=next_year %}">
|
||||
<i class="fa-solid fa-chevron-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row gx-xl-4 gy-3">
|
||||
<div class="col-12 col-xl-3 order-1 order-xl-0">
|
||||
<div class="row">
|
||||
<div class="col-6 p-1">
|
||||
<button class="btn btn-sm btn-outline-success w-100"
|
||||
hx-get="{% url 'transaction_add' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
hx-vals='{"year": {{ year }}, "month": {{ month }}, "type": "IN"}'>
|
||||
<i class="fa-solid fa-circle-plus me-3"></i>
|
||||
{% translate "Income" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-6 p-1">
|
||||
<button class="btn btn-sm btn-outline-danger w-100"
|
||||
hx-get="{% url 'transaction_add' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
hx-vals='{"year": {{ year }}, "month": {{ month }}, "type": "EX"}'>
|
||||
<i class="fa-solid fa-circle-plus me-3"></i>
|
||||
{% translate "Expense" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-6 p-1">
|
||||
<button class="btn btn-sm btn-outline-info w-100"
|
||||
hx-get="{% url 'transactions_transfer' %}"
|
||||
hx-target="#generic-offcanvas"
|
||||
hx-vals='{"year": {{ year }}, "month": {{ month }}}'>
|
||||
<i class="fa-solid fa-money-bill-transfer me-3"></i>
|
||||
{% translate "Transfer" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-6 order-2 order-xl-1"
|
||||
id="transactions"
|
||||
hx-get="{% url 'transactions_list' month=month year=year %}"
|
||||
hx-trigger="load, transaction_updated from:window">
|
||||
</div>
|
||||
<div class="col-12 col-xl-3 order-0 order-xl-2">
|
||||
<div id="total-expenses" hx-get="{% url 'monthly_summary' month=month year=year %}"
|
||||
hx-trigger="load, transaction_updated from:window" class="sticky-sidebar">
|
||||
</div>
|
||||
|
||||
{# <div id="total-spent" hx-get="{% url 'monthly_expenses' month=month year=year %}"#}
|
||||
{# hx-trigger="load, transaction_updated from:window" hx-vals='{"s": "p"}'>#}
|
||||
{# </div>#}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,16 +1,28 @@
|
||||
{% load crispy_forms_field %}
|
||||
|
||||
<div class="btn-group w-100 mb-3" role="group" aria-label="{{ field.label }}">
|
||||
{% for choice in field.field.choices %}
|
||||
<input type="radio"
|
||||
class="btn-check"
|
||||
name="{{ field.html_name }}"
|
||||
id="{{ field.html_name }}_{{ forloop.counter }}"
|
||||
value="{{ choice.0 }}"
|
||||
{% if choice.0 == field.value %}checked{% endif %}>
|
||||
<label class="btn {% if forloop.first %}btn-outline-success{% elif forloop.last %}btn-outline-danger{% else %}btn-outline-primary{% endif %}"
|
||||
for="{{ field.html_name }}_{{ forloop.counter }}">
|
||||
{{ choice.1 }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
<div class="form-group mb-3">
|
||||
<div class="btn-group w-100" role="group" aria-label="{{ field.label }}">
|
||||
{% for choice in field.field.choices %}
|
||||
<input type="radio"
|
||||
class="btn-check"
|
||||
name="{{ field.html_name }}"
|
||||
id="{{ field.html_name }}_{{ forloop.counter }}_tr"
|
||||
value="{{ choice.0 }}"
|
||||
{% if choice.0 == field.value %}checked{% endif %}>
|
||||
<label class="btn {% if forloop.first %}btn-outline-success{% elif forloop.last %}btn-outline-danger{% else %}btn-outline-primary{% endif %} {% if field.errors %}is-invalid{% endif %}"
|
||||
for="{{ field.html_name }}_{{ forloop.counter }}_tr">
|
||||
{{ choice.1 }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if field.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in field.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if field.help_text %}
|
||||
<small class="form-text text-muted">{{ field.help_text }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
{% load crispy_forms_field %}
|
||||
|
||||
<div class="btn-group w-100 mb-3"
|
||||
role="group"
|
||||
aria-label="{{ field.label }}"
|
||||
_="on click
|
||||
if no <input[type='checkbox']:checked/> in me
|
||||
set nextCheckbox to the next <input[type='checkbox']/> from event.target within me with wrapping
|
||||
call nextCheckbox.click()
|
||||
end">
|
||||
{% for choice in field.field.choices %}
|
||||
<input type="checkbox"
|
||||
class="btn-check"
|
||||
name="{{ field.html_name }}"
|
||||
id="{{ field.html_name }}_{{ forloop.counter }}"
|
||||
value="{{ choice.0 }}"
|
||||
{% if choice.0 in field.value %}checked{% endif %}>
|
||||
<label class="btn btn-outline-dark"
|
||||
for="{{ field.html_name }}_{{ forloop.counter }}">
|
||||
{{ choice.1 }}
|
||||
</label>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -0,0 +1,21 @@
|
||||
{% extends "layouts/base_auth.html" %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Login{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
<div class="container">
|
||||
<div class="row vh-100 d-flex justify-content-center align-items-center">
|
||||
<div class="col-md-6 col-xl-4 col-12">
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-center mb-4">Login</h2>
|
||||
{% crispy form %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user