feat: first batch of work

This commit is contained in:
Herculino Trotta
2025-11-01 03:15:44 -03:00
parent e600d87968
commit a63367a772
175 changed files with 3433 additions and 2245 deletions
+3 -1
View File
@@ -123,6 +123,7 @@ celerybeat.pid
# Environments # Environments
.env .env
.prod.env
.venv .venv
env/ env/
venv/ venv/
@@ -161,4 +162,5 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/ .idea/
node_modules/ node_modules/
postgres_data/
+1 -2
View File
@@ -314,7 +314,7 @@ CACHES = {
} }
} }
DJANGO_VITE_ASSETS_PATH = ROOT_DIR / "frontend" / "build" DJANGO_VITE_ASSETS_PATH = STATIC_ROOT
DJANGO_VITE_MANIFEST_PATH = DJANGO_VITE_ASSETS_PATH / "manifest.json" DJANGO_VITE_MANIFEST_PATH = DJANGO_VITE_ASSETS_PATH / "manifest.json"
DJANGO_VITE_DEV_MODE = DEBUG DJANGO_VITE_DEV_MODE = DEBUG
DJANGO_VITE_DEV_SERVER_PORT = 5173 DJANGO_VITE_DEV_SERVER_PORT = 5173
@@ -365,7 +365,6 @@ SOCIALACCOUNT_ADAPTER = "allauth.socialaccount.adapter.DefaultSocialAccountAdapt
# CRISPY FORMS # CRISPY FORMS
CRISPY_ALLOWED_TEMPLATE_PACKS = [ CRISPY_ALLOWED_TEMPLATE_PACKS = [
"bootstrap5",
"crispy_forms/pure_text", "crispy_forms/pure_text",
"crispy-daisyui", "crispy-daisyui",
] ]
+13 -15
View File
@@ -1,23 +1,21 @@
from apps.accounts.models import Account, AccountGroup
from apps.common.fields.forms.dynamic_select import (
DynamicModelChoiceField,
DynamicModelMultipleChoiceField,
)
from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.decimal import ArbitraryDecimalDisplayNumberInput
from apps.common.widgets.tom_select import TomSelect
from apps.currencies.models import Currency
from apps.transactions.models import TransactionCategory, TransactionTag
from crispy_bootstrap5.bootstrap5 import Switch from crispy_bootstrap5.bootstrap5 import Switch
from crispy_forms.bootstrap import FormActions from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Column, Row from crispy_forms.layout import Column, Field, Layout, Row
from django import forms from django import forms
from django.db.models import Q from django.db.models import Q
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from apps.accounts.models import Account
from apps.accounts.models import AccountGroup
from apps.common.fields.forms.dynamic_select import (
DynamicModelMultipleChoiceField,
DynamicModelChoiceField,
)
from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.tom_select import TomSelect
from apps.transactions.models import TransactionCategory, TransactionTag
from apps.common.widgets.decimal import ArbitraryDecimalDisplayNumberInput
from apps.currencies.models import Currency
class AccountGroupForm(forms.ModelForm): class AccountGroupForm(forms.ModelForm):
class Meta: class Meta:
@@ -156,8 +154,8 @@ class AccountBalanceForm(forms.Form):
self.helper.layout = Layout( self.helper.layout = Layout(
"new_balance", "new_balance",
Row( Row(
Column("category", css_class="form-group col-md-6 mb-0"), Column("category", css_class="md:col-span-6 mb-0"),
Column("tags", css_class="form-group col-md-6 mb-0"), Column("tags", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
Field("account_id"), Field("account_id"),
+1 -1
View File
@@ -11,7 +11,7 @@ def toast_bg(tags):
elif "warning" in tags: elif "warning" in tags:
return "warning" return "warning"
elif "error" in tags: elif "error" in tags:
return "danger" return "error"
elif "info" in tags: elif "info" in tags:
return "info" return "info"
+7 -6
View File
@@ -1,15 +1,14 @@
import datetime import datetime
from django.forms import widgets from apps.common.functions.format import get_format
from django.utils import formats, translation, dates
from django.utils.translation import gettext_lazy as _
from apps.common.utils.django import ( from apps.common.utils.django import (
django_to_python_datetime,
django_to_airdatepicker_datetime, django_to_airdatepicker_datetime,
django_to_airdatepicker_datetime_separated, django_to_airdatepicker_datetime_separated,
django_to_python_datetime,
) )
from apps.common.functions.format import get_format from django.forms import widgets
from django.utils import dates, formats, translation
from django.utils.translation import gettext_lazy as _
class AirDatePickerInput(widgets.DateInput): class AirDatePickerInput(widgets.DateInput):
@@ -52,6 +51,8 @@ class AirDatePickerInput(widgets.DateInput):
def build_attrs(self, base_attrs, extra_attrs=None): def build_attrs(self, base_attrs, extra_attrs=None):
attrs = super().build_attrs(base_attrs, extra_attrs) attrs = super().build_attrs(base_attrs, extra_attrs)
attrs["class"] = attrs.get("class", "") + " input"
attrs["data-now-button-txt"] = _("Today") attrs["data-now-button-txt"] = _("Today")
attrs["data-auto-close"] = str(self.auto_close).lower() attrs["data-auto-close"] = str(self.auto_close).lower()
attrs["data-clear-button"] = str(self.clear_button).lower() attrs["data-clear-button"] = str(self.clear_button).lower()
+9 -10
View File
@@ -1,16 +1,15 @@
from crispy_bootstrap5.bootstrap5 import Switch
from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Row, Column
from django import forms
from django.forms import CharField
from django.utils.translation import gettext_lazy as _
from apps.common.widgets.crispy.submit import NoClassSubmit from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.datepicker import AirDateTimePickerInput from apps.common.widgets.datepicker import AirDateTimePickerInput
from apps.common.widgets.decimal import ArbitraryDecimalDisplayNumberInput from apps.common.widgets.decimal import ArbitraryDecimalDisplayNumberInput
from apps.common.widgets.tom_select import TomSelect from apps.common.widgets.tom_select import TomSelect
from apps.currencies.models import Currency, ExchangeRate, ExchangeRateService from apps.currencies.models import Currency, ExchangeRate, ExchangeRateService
from crispy_bootstrap5.bootstrap5 import Switch
from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Column, Layout, Row
from django import forms
from django.forms import CharField
from django.utils.translation import gettext_lazy as _
class CurrencyForm(forms.ModelForm): class CurrencyForm(forms.ModelForm):
@@ -132,8 +131,8 @@ class ExchangeRateServiceForm(forms.ModelForm):
Switch("singleton"), Switch("singleton"),
"api_key", "api_key",
Row( Row(
Column("interval_type", css_class="form-group col-md-6"), Column("interval_type", css_class="md:col-span-6"),
Column("fetch_interval", css_class="form-group col-md-6"), Column("fetch_interval", css_class="md:col-span-6"),
), ),
"target_currencies", "target_currencies",
"target_accounts", "target_accounts",
+20 -26
View File
@@ -1,22 +1,20 @@
from crispy_bootstrap5.bootstrap5 import Switch, BS5Accordion
from crispy_forms.bootstrap import FormActions, AccordionGroup
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Row, Column, HTML
from django import forms
from django.utils.translation import gettext_lazy as _
from apps.accounts.models import Account from apps.accounts.models import Account
from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.datepicker import AirDatePickerInput
from apps.common.widgets.decimal import ArbitraryDecimalDisplayNumberInput
from apps.common.widgets.tom_select import TomSelect
from apps.dca.models import DCAStrategy, DCAEntry
from apps.common.widgets.tom_select import TransactionSelect
from apps.transactions.models import Transaction, TransactionTag, TransactionCategory
from apps.common.fields.forms.dynamic_select import ( from apps.common.fields.forms.dynamic_select import (
DynamicModelChoiceField, DynamicModelChoiceField,
DynamicModelMultipleChoiceField, DynamicModelMultipleChoiceField,
) )
from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.datepicker import AirDatePickerInput
from apps.common.widgets.decimal import ArbitraryDecimalDisplayNumberInput
from apps.common.widgets.tom_select import TomSelect, TransactionSelect
from apps.dca.models import DCAEntry, DCAStrategy
from apps.transactions.models import Transaction, TransactionCategory, TransactionTag
from crispy_bootstrap5.bootstrap5 import BS5Accordion, Switch
from crispy_forms.bootstrap import AccordionGroup, FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Column, Layout, Row
from django import forms
from django.utils.translation import gettext_lazy as _
class DCAStrategyForm(forms.ModelForm): class DCAStrategyForm(forms.ModelForm):
@@ -36,8 +34,8 @@ class DCAStrategyForm(forms.ModelForm):
self.helper.layout = Layout( self.helper.layout = Layout(
"name", "name",
Row( Row(
Column("payment_currency", css_class="form-group col-md-6"), Column("payment_currency", css_class="md:col-span-6"),
Column("target_currency", css_class="form-group col-md-6"), Column("target_currency", css_class="md:col-span-6"),
), ),
"notes", "notes",
) )
@@ -155,8 +153,8 @@ class DCAEntryForm(forms.ModelForm):
self.helper.layout = Layout( self.helper.layout = Layout(
"date", "date",
Row( Row(
Column("amount_paid", css_class="form-group col-md-6"), Column("amount_paid", css_class="md:col-span-6"),
Column("amount_received", css_class="form-group col-md-6"), Column("amount_received", css_class="md:col-span-6"),
), ),
"notes", "notes",
BS5Accordion( BS5Accordion(
@@ -175,11 +173,9 @@ class DCAEntryForm(forms.ModelForm):
Row( Row(
Column( Column(
"from_category", "from_category",
css_class="form-group col-md-6 mb-0", css_class="md:col-span-6 mb-0",
),
Column(
"from_tags", css_class="form-group col-md-6 mb-0"
), ),
Column("from_tags", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
), ),
@@ -195,10 +191,8 @@ class DCAEntryForm(forms.ModelForm):
css_class="form-row", css_class="form-row",
), ),
Row( Row(
Column( Column("to_category", css_class="md:col-span-6 mb-0"),
"to_category", css_class="form-group col-md-6 mb-0" Column("to_tags", css_class="md:col-span-6 mb-0"),
),
Column("to_tags", css_class="form-group col-md-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
), ),
+12 -13
View File
@@ -1,15 +1,14 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Row, Column
from django import forms
from django.utils.translation import gettext_lazy as _
from apps.common.widgets.datepicker import ( from apps.common.widgets.datepicker import (
AirDatePickerInput,
AirMonthYearPickerInput, AirMonthYearPickerInput,
AirYearPickerInput, AirYearPickerInput,
AirDatePickerInput,
) )
from apps.transactions.models import TransactionCategory
from apps.common.widgets.tom_select import TomSelect from apps.common.widgets.tom_select import TomSelect
from apps.transactions.models import TransactionCategory
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Column, Field, Layout, Row
from django import forms
from django.utils.translation import gettext_lazy as _
class SingleMonthForm(forms.Form): class SingleMonthForm(forms.Form):
@@ -59,8 +58,8 @@ class MonthRangeForm(forms.Form):
self.helper.layout = Layout( self.helper.layout = Layout(
Row( Row(
Column("month_from", css_class="form-group col-md-6"), Column("month_from", css_class="md:col-span-6"),
Column("month_to", css_class="form-group col-md-6"), Column("month_to", css_class="md:col-span-6"),
), ),
) )
@@ -82,8 +81,8 @@ class YearRangeForm(forms.Form):
self.helper.layout = Layout( self.helper.layout = Layout(
Row( Row(
Column("year_from", css_class="form-group col-md-6"), Column("year_from", css_class="md:col-span-6"),
Column("year_to", css_class="form-group col-md-6"), Column("year_to", css_class="md:col-span-6"),
), ),
) )
@@ -105,8 +104,8 @@ class DateRangeForm(forms.Form):
self.helper.layout = Layout( self.helper.layout = Layout(
Row( Row(
Column("date_from", css_class="form-group col-md-6"), Column("date_from", css_class="md:col-span-6"),
Column("date_to", css_class="form-group col-md-6"), Column("date_to", css_class="md:col-span-6"),
css_class="mb-0", css_class="mb-0",
), ),
) )
+27 -26
View File
@@ -1,20 +1,21 @@
from crispy_bootstrap5.bootstrap5 import Switch, BS5Accordion from apps.common.fields.forms.dynamic_select import DynamicModelChoiceField
from crispy_forms.bootstrap import FormActions, AccordionGroup from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.tom_select import TomSelect, TransactionSelect
from apps.rules.models import (
TransactionRule,
TransactionRuleAction,
UpdateOrCreateTransactionRuleAction,
)
from apps.transactions.forms import BulkEditTransactionForm
from apps.transactions.models import Transaction
from crispy_bootstrap5.bootstrap5 import BS5Accordion, Switch
from crispy_forms.bootstrap import AccordionGroup, FormActions
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Row, Column, HTML from crispy_forms.layout import HTML, Column, Field, Layout, Row
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.tom_select import TomSelect, TransactionSelect
from apps.rules.models import TransactionRule, UpdateOrCreateTransactionRuleAction
from apps.rules.models import TransactionRuleAction
from apps.common.fields.forms.dynamic_select import DynamicModelChoiceField
from apps.transactions.forms import BulkEditTransactionForm
from apps.transactions.models import Transaction
class TransactionRuleForm(forms.ModelForm): class TransactionRuleForm(forms.ModelForm):
class Meta: class Meta:
@@ -221,7 +222,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_type_operator"), Field("search_type_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_type", rows=1), Field("search_type", rows=1),
@@ -231,7 +232,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_is_paid_operator"), Field("search_is_paid_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_is_paid", rows=1), Field("search_is_paid", rows=1),
@@ -241,7 +242,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_mute_operator"), Field("search_mute_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_mute", rows=1), Field("search_mute", rows=1),
@@ -251,7 +252,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_account_operator"), Field("search_account_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_account", rows=1), Field("search_account", rows=1),
@@ -261,7 +262,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_entities_operator"), Field("search_entities_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_entities", rows=1), Field("search_entities", rows=1),
@@ -271,7 +272,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_date_operator"), Field("search_date_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_date", rows=1), Field("search_date", rows=1),
@@ -281,7 +282,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_reference_date_operator"), Field("search_reference_date_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_reference_date", rows=1), Field("search_reference_date", rows=1),
@@ -291,7 +292,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_description_operator"), Field("search_description_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_description", rows=1), Field("search_description", rows=1),
@@ -301,7 +302,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_amount_operator"), Field("search_amount_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_amount", rows=1), Field("search_amount", rows=1),
@@ -311,7 +312,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_category_operator"), Field("search_category_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_category", rows=1), Field("search_category", rows=1),
@@ -321,7 +322,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_tags_operator"), Field("search_tags_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_tags", rows=1), Field("search_tags", rows=1),
@@ -331,7 +332,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_notes_operator"), Field("search_notes_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_notes", rows=1), Field("search_notes", rows=1),
@@ -341,7 +342,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_internal_note_operator"), Field("search_internal_note_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_internal_note", rows=1), Field("search_internal_note", rows=1),
@@ -351,7 +352,7 @@ class UpdateOrCreateTransactionRuleActionForm(forms.ModelForm):
Row( Row(
Column( Column(
Field("search_internal_id_operator"), Field("search_internal_id_operator"),
css_class="form-group col-md-4", css_class="md:col-span-4",
), ),
Column( Column(
Field("search_internal_id", rows=1), Field("search_internal_id", rows=1),
+11 -12
View File
@@ -1,11 +1,4 @@
import django_filters import django_filters
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Row, Column
from django import forms
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from django_filters import Filter
from apps.accounts.models import Account from apps.accounts.models import Account
from apps.common.fields.month_year import MonthYearFormField from apps.common.fields.month_year import MonthYearFormField
from apps.common.widgets.datepicker import AirDatePickerInput from apps.common.widgets.datepicker import AirDatePickerInput
@@ -15,9 +8,15 @@ from apps.currencies.models import Currency
from apps.transactions.models import ( from apps.transactions.models import (
Transaction, Transaction,
TransactionCategory, TransactionCategory,
TransactionTag,
TransactionEntity, TransactionEntity,
TransactionTag,
) )
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Column, Field, Layout, Row
from django import forms
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from django_filters import Filter
SITUACAO_CHOICES = ( SITUACAO_CHOICES = (
("1", _("Paid")), ("1", _("Paid")),
@@ -159,13 +158,13 @@ class TransactionsFilter(django_filters.FilterSet):
Field("description"), Field("description"),
Row(Column("date_start"), Column("date_end")), Row(Column("date_start"), Column("date_end")),
Row( Row(
Column("reference_date_start", css_class="form-group col-md-6 mb-0"), Column("reference_date_start", css_class="md:col-span-6 mb-0"),
Column("reference_date_end", css_class="form-group col-md-6 mb-0"), Column("reference_date_end", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
Row( Row(
Column("from_amount", css_class="form-group col-md-6 mb-0"), Column("from_amount", css_class="md:col-span-6 mb-0"),
Column("to_amount", css_class="form-group col-md-6 mb-0"), Column("to_amount", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
Field("account", size=1), Field("account", size=1),
+76 -93
View File
@@ -1,20 +1,5 @@
from copy import deepcopy from copy import deepcopy
from crispy_bootstrap5.bootstrap5 import Switch, BS5Accordion
from crispy_forms.bootstrap import FormActions, AccordionGroup, AppendedText
from crispy_forms.helper import FormHelper
from crispy_forms.layout import (
Layout,
Row,
Column,
Field,
Div,
HTML,
)
from django import forms
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from apps.accounts.models import Account from apps.accounts.models import Account
from apps.common.fields.forms.dynamic_select import ( from apps.common.fields.forms.dynamic_select import (
DynamicModelChoiceField, DynamicModelChoiceField,
@@ -26,14 +11,28 @@ from apps.common.widgets.decimal import ArbitraryDecimalDisplayNumberInput
from apps.common.widgets.tom_select import TomSelect from apps.common.widgets.tom_select import TomSelect
from apps.rules.signals import transaction_created, transaction_updated from apps.rules.signals import transaction_created, transaction_updated
from apps.transactions.models import ( from apps.transactions.models import (
InstallmentPlan,
QuickTransaction,
RecurringTransaction,
Transaction, Transaction,
TransactionCategory, TransactionCategory,
TransactionTag,
InstallmentPlan,
RecurringTransaction,
TransactionEntity, TransactionEntity,
QuickTransaction, TransactionTag,
) )
from crispy_bootstrap5.bootstrap5 import BS5Accordion, Switch
from crispy_forms.bootstrap import AccordionGroup, AppendedText, FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import (
HTML,
Column,
Div,
Field,
Layout,
Row,
)
from django import forms
from django.db.models import Q
from django.utils.translation import gettext_lazy as _
class TransactionForm(forms.ModelForm): class TransactionForm(forms.ModelForm):
@@ -134,20 +133,20 @@ class TransactionForm(forms.ModelForm):
), ),
Field("is_paid", template="transactions/widgets/paid_toggle_button.html"), Field("is_paid", template="transactions/widgets/paid_toggle_button.html"),
Row( Row(
Column("account", css_class="form-group col-md-6 mb-0"), Column("account", css_class="md:col-span-6 mb-0"),
Column("entities", css_class="form-group col-md-6 mb-0"), Column("entities", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
Row( Row(
Column(Field("date"), css_class="form-group col-md-6 mb-0"), Column(Field("date"), css_class="md:col-span-6 mb-0"),
Column(Field("reference_date"), css_class="form-group col-md-6 mb-0"), Column(Field("reference_date"), css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"description", "description",
Field("amount", inputmode="decimal"), Field("amount", inputmode="decimal"),
Row( Row(
Column("category", css_class="form-group col-md-6 mb-0"), Column("category", css_class="md:col-span-6 mb-0"),
Column("tags", css_class="form-group col-md-6 mb-0"), Column("tags", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"notes", "notes",
@@ -164,8 +163,8 @@ class TransactionForm(forms.ModelForm):
Field("is_paid", template="transactions/widgets/paid_toggle_button.html"), Field("is_paid", template="transactions/widgets/paid_toggle_button.html"),
"account", "account",
Row( Row(
Column(Field("date"), css_class="form-group col-md-6 mb-0"), Column(Field("date"), css_class="md:col-span-6 mb-0"),
Column(Field("reference_date"), css_class="form-group col-md-6 mb-0"), Column(Field("reference_date"), css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"description", "description",
@@ -175,8 +174,8 @@ class TransactionForm(forms.ModelForm):
_("More"), _("More"),
"entities", "entities",
Row( Row(
Column("category", css_class="form-group col-md-6 mb-0"), Column("category", css_class="md:col-span-6 mb-0"),
Column("tags", css_class="form-group col-md-6 mb-0"), Column("tags", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"notes", "notes",
@@ -350,15 +349,15 @@ class QuickTransactionForm(forms.ModelForm):
"name", "name",
HTML("<hr />"), HTML("<hr />"),
Row( Row(
Column("account", css_class="form-group col-md-6 mb-0"), Column("account", css_class="md:col-span-6 mb-0"),
Column("entities", css_class="form-group col-md-6 mb-0"), Column("entities", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"description", "description",
Field("amount", inputmode="decimal"), Field("amount", inputmode="decimal"),
Row( Row(
Column("category", css_class="form-group col-md-6 mb-0"), Column("category", css_class="md:col-span-6 mb-0"),
Column("tags", css_class="form-group col-md-6 mb-0"), Column("tags", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"notes", "notes",
@@ -481,20 +480,20 @@ class BulkEditTransactionForm(forms.Form):
template="transactions/widgets/unselectable_paid_toggle_button.html", template="transactions/widgets/unselectable_paid_toggle_button.html",
), ),
Row( Row(
Column("account", css_class="form-group col-md-6 mb-0"), Column("account", css_class="md:col-span-6 mb-0"),
Column("entities", css_class="form-group col-md-6 mb-0"), Column("entities", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
Row( Row(
Column(Field("date"), css_class="form-group col-md-6 mb-0"), Column(Field("date"), css_class="md:col-span-6 mb-0"),
Column(Field("reference_date"), css_class="form-group col-md-6 mb-0"), Column(Field("reference_date"), css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"description", "description",
Field("amount", inputmode="decimal"), Field("amount", inputmode="decimal"),
Row( Row(
Column("category", css_class="form-group col-md-6 mb-0"), Column("category", css_class="md:col-span-6 mb-0"),
Column("tags", css_class="form-group col-md-6 mb-0"), Column("tags", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"notes", "notes",
@@ -600,10 +599,10 @@ class TransferForm(forms.Form):
self.helper.layout = Layout( self.helper.layout = Layout(
Row( Row(
Column(Field("date"), css_class="form-group col-md-6 mb-0"), Column(Field("date"), css_class="md:col-span-6 mb-0"),
Column( Column(
Field("reference_date"), Field("reference_date"),
css_class="form-group col-md-6 mb-0", css_class="md:col-span-6 mb-0",
), ),
css_class="form-row", css_class="form-row",
), ),
@@ -612,45 +611,29 @@ class TransferForm(forms.Form):
Switch("mute"), Switch("mute"),
Row( Row(
Column( Column(
Row( "from_account",
Column( css_class="md:col-span-6",
"from_account",
css_class="form-group col-md-6 mb-0",
),
Column(
Field("from_amount"),
css_class="form-group col-md-6 mb-0",
),
css_class="form-row",
),
Row(
Column("from_category", css_class="form-group col-md-6 mb-0"),
Column("from_tags", css_class="form-group col-md-6 mb-0"),
css_class="form-row",
),
), ),
css_class="p-1 mx-1 my-3 border rounded-3", Column(
Field("from_amount"),
css_class="md:col-span-6",
),
Column("from_category", css_class="md:col-span-6"),
Column("from_tags", css_class="md:col-span-6"),
css_class="bg-base-200 rounded-box p-4 border-base-content/60 border mb-3",
), ),
Row( Row(
Column( Column(
Row( "to_account",
Column( css_class="md:col-span-6 mb-0",
"to_account",
css_class="form-group col-md-6 mb-0",
),
Column(
Field("to_amount"),
css_class="form-group col-md-6 mb-0",
),
css_class="form-row",
),
Row(
Column("to_category", css_class="form-group col-md-6 mb-0"),
Column("to_tags", css_class="form-group col-md-6 mb-0"),
css_class="form-row",
),
), ),
css_class="p-1 mx-1 my-3 border rounded-3", Column(
Field("to_amount"),
css_class="md:col-span-6 mb-0",
),
Column("to_category", css_class="md:col-span-6 mb-0"),
Column("to_tags", css_class="md:col-span-6 mb-0"),
css_class="bg-base-200 rounded-box p-4 border-base-content/60 border mb-3",
), ),
FormActions( FormActions(
NoClassSubmit( NoClassSubmit(
@@ -841,8 +824,8 @@ class InstallmentPlanForm(forms.ModelForm):
template="transactions/widgets/income_expense_toggle_buttons.html", template="transactions/widgets/income_expense_toggle_buttons.html",
), ),
Row( Row(
Column("account", css_class="form-group col-md-6 mb-0"), Column("account", css_class="md:col-span-6 mb-0"),
Column("entities", css_class="form-group col-md-6 mb-0"), Column("entities", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"description", "description",
@@ -850,20 +833,20 @@ class InstallmentPlanForm(forms.ModelForm):
"notes", "notes",
Switch("add_notes_to_transaction"), Switch("add_notes_to_transaction"),
Row( Row(
Column("number_of_installments", css_class="form-group col-md-6 mb-0"), Column("number_of_installments", css_class="md:col-span-6 mb-0"),
Column("installment_start", css_class="form-group col-md-6 mb-0"), Column("installment_start", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
Row( Row(
Column("start_date", css_class="form-group col-md-4 mb-0"), Column("start_date", css_class="md:col-span-4 mb-0"),
Column("reference_date", css_class="form-group col-md-4 mb-0"), Column("reference_date", css_class="md:col-span-4 mb-0"),
Column("recurrence", css_class="form-group col-md-4 mb-0"), Column("recurrence", css_class="md:col-span-4 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"installment_amount", "installment_amount",
Row( Row(
Column("category", css_class="form-group col-md-6 mb-0"), Column("category", css_class="md:col-span-6 mb-0"),
Column("tags", css_class="form-group col-md-6 mb-0"), Column("tags", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
) )
@@ -1103,29 +1086,29 @@ class RecurringTransactionForm(forms.ModelForm):
template="transactions/widgets/income_expense_toggle_buttons.html", template="transactions/widgets/income_expense_toggle_buttons.html",
), ),
Row( Row(
Column("account", css_class="form-group col-md-6 mb-0"), Column("account", css_class="md:col-span-6 mb-0"),
Column("entities", css_class="form-group col-md-6 mb-0"), Column("entities", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"description", "description",
Switch("add_description_to_transaction"), Switch("add_description_to_transaction"),
"amount", "amount",
Row( Row(
Column("category", css_class="form-group col-md-6 mb-0"), Column("category", css_class="md:col-span-6 mb-0"),
Column("tags", css_class="form-group col-md-6 mb-0"), Column("tags", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"notes", "notes",
Switch("add_notes_to_transaction"), Switch("add_notes_to_transaction"),
Row( Row(
Column("start_date", css_class="form-group col-md-6 mb-0"), Column("start_date", css_class="md:col-span-6 mb-0"),
Column("reference_date", css_class="form-group col-md-6 mb-0"), Column("reference_date", css_class="md:col-span-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
Row( Row(
Column("recurrence_interval", css_class="form-group col-md-4 mb-0"), Column("recurrence_interval", css_class="md:col-span-4 mb-0"),
Column("recurrence_type", css_class="form-group col-md-4 mb-0"), Column("recurrence_type", css_class="md:col-span-4 mb-0"),
Column("end_date", css_class="form-group col-md-4 mb-0"), Column("end_date", css_class="md:col-span-4 mb-0"),
css_class="form-row", css_class="form-row",
), ),
AppendedText("keep_at_most", _("future transactions")), AppendedText("keep_at_most", _("future transactions")),
+28 -17
View File
@@ -2,29 +2,29 @@ import decimal
import logging import logging
from copy import deepcopy from copy import deepcopy
from apps.common.fields.month_year import MonthYearModelField
from apps.common.functions.decimals import truncate_decimal
from apps.common.middleware.thread_local import get_current_user
from apps.common.models import (
OwnedObject,
OwnedObjectManager,
SharedObject,
SharedObjectManager,
)
from apps.common.templatetags.decimal import drop_trailing_zeros, localize_number
from apps.currencies.utils.convert import convert
from apps.transactions.validators import validate_decimal_places, validate_non_negative
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django.conf import settings from django.conf import settings
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
from django.db import models, transaction from django.db import models, transaction
from django.db.models import Q from django.db.models import Q
from django.dispatch import Signal from django.dispatch import Signal
from django.forms import ValidationError
from django.template.defaultfilters import date from django.template.defaultfilters import date
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from apps.common.fields.month_year import MonthYearModelField
from apps.common.functions.decimals import truncate_decimal
from apps.common.templatetags.decimal import localize_number, drop_trailing_zeros
from apps.currencies.utils.convert import convert
from apps.transactions.validators import validate_decimal_places, validate_non_negative
from apps.common.middleware.thread_local import get_current_user
from apps.common.models import (
SharedObject,
SharedObjectManager,
OwnedObject,
OwnedObjectManager,
)
logger = logging.getLogger() logger = logging.getLogger()
@@ -381,21 +381,32 @@ class Transaction(OwnedObject):
db_table = "transactions" db_table = "transactions"
default_manager_name = "objects" default_manager_name = "objects"
def clean_fields(self, *args, **kwargs): def clean(self):
super().clean()
# Only process amount and reference_date if account exists
# If account is missing, Django's required field validation will handle it
try:
account = self.account
except Transaction.account.RelatedObjectDoesNotExist:
# Account doesn't exist, skip processing that depends on it
# Django will add the required field error
return
# Validate and normalize amount
if isinstance(self.amount, (str, int, float)): if isinstance(self.amount, (str, int, float)):
self.amount = decimal.Decimal(str(self.amount)) self.amount = decimal.Decimal(str(self.amount))
self.amount = truncate_decimal( self.amount = truncate_decimal(
value=self.amount, decimal_places=self.account.currency.decimal_places value=self.amount, decimal_places=account.currency.decimal_places
) )
# Normalize reference_date
if self.reference_date: if self.reference_date:
self.reference_date = self.reference_date.replace(day=1) self.reference_date = self.reference_date.replace(day=1)
elif not self.reference_date and self.date: elif not self.reference_date and self.date:
self.reference_date = self.date.replace(day=1) self.reference_date = self.date.replace(day=1)
super().clean_fields(*args, **kwargs)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
# This is not recommended as it will run twice on some cases like form and API saves. # This is not recommended as it will run twice on some cases like form and API saves.
# We only do this here because we forgot to independently call it on multiple places. # We only do this here because we forgot to independently call it on multiple places.
+20 -12
View File
@@ -1,35 +1,43 @@
from apps.common.middleware.thread_local import get_current_user
from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.users.models import UserSettings
from crispy_forms.bootstrap import ( from crispy_forms.bootstrap import (
FormActions, FormActions,
) )
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Row, Column, Field, Div, HTML from crispy_forms.layout import HTML, Column, Div, Field, Layout, Row, Submit
from django import forms from django import forms
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.forms import ( from django.contrib.auth.forms import (
UsernameField,
AuthenticationForm, AuthenticationForm,
UserCreationForm, UserCreationForm,
UsernameField,
) )
from django.db import transaction from django.db import transaction
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.users.models import UserSettings
from apps.common.middleware.thread_local import get_current_user
class LoginForm(AuthenticationForm): class LoginForm(AuthenticationForm):
username = UsernameField( username = UsernameField(
label=_("E-mail"), label=_("E-mail"),
widget=forms.EmailInput( widget=forms.EmailInput(
attrs={"class": "form-control", "placeholder": "E-mail", "name": "email"} attrs={
"class": "input",
"placeholder": _("E-mail"),
"name": "email",
"autocomplete": "email",
}
), ),
) )
password = forms.CharField( password = forms.CharField(
label=_("Password"), label=_("Password"),
strip=False, strip=False,
widget=forms.PasswordInput( widget=forms.PasswordInput(
attrs={"class": "form-control", "placeholder": "Senha"} attrs={
"class": "input",
"placeholder": _("Password"),
"autocomplete": "current-password",
}
), ),
) )
@@ -191,8 +199,8 @@ class UserUpdateForm(forms.ModelForm):
# Define the layout using Crispy Forms, including the new fields # Define the layout using Crispy Forms, including the new fields
self.helper.layout = Layout( self.helper.layout = Layout(
Row( Row(
Column("first_name", css_class="form-group col-md-6"), Column("first_name", css_class="md:col-span-6"),
Column("last_name", css_class="form-group col-md-6"), Column("last_name", css_class="md:col-span-6"),
css_class="row", css_class="row",
), ),
Field("email"), Field("email"),
@@ -354,8 +362,8 @@ class UserAddForm(UserCreationForm):
self.helper.layout = Layout( self.helper.layout = Layout(
Field("email"), Field("email"),
Row( Row(
Column("first_name", css_class="form-group col-md-6"), Column("first_name", css_class="md:col-span-6"),
Column("last_name", css_class="form-group col-md-6"), Column("last_name", css_class="md:col-span-6"),
css_class="row", css_class="row",
), ),
# UserCreationForm provides 'password1' and 'password2' fields # UserCreationForm provides 'password1' and 'password2' fields
@@ -1,9 +1,9 @@
{% load i18n %} {% load i18n %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container px-md-3 py-3 column-gap-5">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:mb-3"> <div class="text-3xl font-bold font-mono w-full mb-3">
{% spaceless %} {% spaceless %}
<div>{% translate 'Account Groups' %}<span> <div>{% translate 'Account Groups' %}<span>
<a class="tw:no-underline tw:text-2xl tw:p-1 category-action" <a class="no-underline text-2xl p-1 category-action"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}" data-bs-title="{% translate "Add" %}"
@@ -14,30 +14,30 @@
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-body tw:overflow-x-auto"> <div class="card-body overflow-x-auto">
{% if account_groups %} {% if account_groups %}
<c-config.search></c-config.search> <c-config.search></c-config.search>
<table class="tw:table tw:table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col">{% translate 'Name' %}</th> <th scope="col">{% translate 'Name' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for account_group in account_groups %} {% for account_group in account_groups %}
<tr class="account_group"> <tr class="account_group">
<td class="tw:w-auto"> <td class="w-auto">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}" data-bs-title="{% translate "Edit" %}"
hx-get="{% url 'account_group_edit' pk=account_group.id %}" hx-get="{% url 'account_group_edit' pk=account_group.id %}"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -49,7 +49,7 @@
data-confirm-text="{% translate "Yes, delete it!" %}" data-confirm-text="{% translate "Yes, delete it!" %}"
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
{% if not account_group.owner %} {% if not account_group.owner %}
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-warning" <a class="btn btn-secondary btn-sm join-item text-warning"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Take ownership" %}" data-bs-title="{% translate "Take ownership" %}"
@@ -57,7 +57,7 @@
<i class="fa-solid fa-crown fa-fw"></i></a> <i class="fa-solid fa-crown fa-fw"></i></a>
{% endif %} {% endif %}
{% if user == account_group.owner %} {% if user == account_group.owner %}
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-primary" <a class="btn btn-secondary btn-sm join-item text-primary"
role="button" role="button"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-swap="innerHTML" hx-swap="innerHTML"
@@ -9,60 +9,55 @@
<form hx-post="{% url 'account_reconciliation' %}"> <form hx-post="{% url 'account_reconciliation' %}">
{% csrf_token %} {% csrf_token %}
{{ form.management_form }} {{ form.management_form }}
<div class="tw:space-y-2" id="balanceAccordionFlush"> <div class="space-y-2" id="balanceAccordionFlush">
{% for form in form.forms %} {% for form in form.forms %}
<div class="tw:collapse tw:collapse-arrow tw:bg-base-200"> <details class="collapse collapse-arrow bg-base-100 border-base-300 border overflow-visible show">
<input type="radio" name="my-accordion-flush" id="accordion-{{ forloop.counter0 }}" /> <summary class="collapse-title font-medium text-sm">
<label for="accordion-{{ forloop.counter0 }}" class="tw:collapse-title tw:text-base tw:font-medium"> {% if form.account_group %}<span class="badge badge-primary me-2">{{ form.account_group.name }}</span>{% endif %}{{ form.account_name }}
{% if form.account_group %}<span class="tw:badge tw:badge-primary tw:me-2">{{ form.account_group.name }}</span>{% endif %}{{ form.account_name }} </summary>
</label> <div class="collapse-content">
<div class="tw:collapse-content"> <div class="fieldset">
<div class="tw:mb-3"> <span class="fieldset-legend">{% translate 'Current balance' %}</span>
<div class="tw:label">
<span class="tw:label-text">{% translate 'Current balance' %}</span>
</div>
<div data-amount="{{ form.current_balance|floatformat:"-40u" }}" <div data-amount="{{ form.current_balance|floatformat:"-40u" }}"
data-decimal-places="{{ form.currency_decimal_places }}" data-decimal-places="{{ form.currency_decimal_places }}"
id="amount-{{ forloop.counter0 }}"> id="amount-{{ forloop.counter0 }}" class="text-base">
{% currency_display amount=form.current_balance prefix=form.currency_prefix suffix=form.currency_suffix decimal_places=form.currency_decimal_places %} {% currency_display amount=form.current_balance prefix=form.currency_prefix suffix=form.currency_suffix decimal_places=form.currency_decimal_places %}
</div> </div>
</div> </div>
<div> <div>
{% crispy form %} {% crispy form %}
</div> </div>
<div class="tw:mb-3"> <div class="fieldset">
<div class="tw:label"> <span class="fieldset-legend">{% translate 'Difference' %}</span>
<span class="tw:label-text">{% translate 'Difference' %}</span> <div class="text-base"
</div> _="on input from #id_form-{{ forloop.counter0 }}-new_balance
<div _="on input from #id_form-{{ forloop.counter0 }}-new_balance set original_amount to parseFloat('{{ form.current_balance|floatformat:"-40u" }}')
set original_amount to parseFloat('{{ form.current_balance|floatformat:"-40u" }}') then set prefix to '{{ form.currency_prefix }}'
then set prefix to '{{ form.currency_prefix }}' then set suffix to '{{ form.currency_suffix }}'
then set suffix to '{{ form.currency_suffix }}' then set decimal_places to {{ form.currency_decimal_places }}
then set decimal_places to {{ form.currency_decimal_places }} then call parseLocaleNumber(#id_form-{{ forloop.counter0 }}-new_balance.value)
then call parseLocaleNumber(#id_form-{{ forloop.counter0 }}-new_balance.value) then set new_amount to result
then set new_amount to result then set diff to (Math.round((new_amount - original_amount) * Math.pow(10, decimal_places))) / Math.pow(10, decimal_places)
then set diff to (Math.round((new_amount - original_amount) * Math.pow(10, decimal_places))) / Math.pow(10, decimal_places) then set format_new_amount to
then log diff Intl.NumberFormat(
then set format_new_amount to undefined,
Intl.NumberFormat( {
undefined, minimumFractionDigits: decimal_places,
{ maximumFractionDigits: decimal_places,
minimumFractionDigits: decimal_places, roundingMode: 'trunc'
maximumFractionDigits: decimal_places, }
roundingMode: 'trunc' ).format(diff)
} then set formatted_string to `${prefix}${format_new_amount}${suffix}`
).format(diff) then put formatted_string into me if diff else
then set formatted_string to `${prefix}${format_new_amount}${suffix}` put '-' into me">-</div>
then put formatted_string into me if diff else
put '-' into me">-</div>
</div> </div>
</div> </div>
</div> </details>
{% endfor %} {% endfor %}
</div> </div>
<div class="tw:mt-3"> <div class="mt-3">
<div> <div>
<input type="submit" name="submit" value="{% translate 'Reconcile balances' %}" class="tw:btn tw:btn-outline tw:btn-primary tw:w-full" id="submit-id-submit"> <input type="submit" name="submit" value="{% translate 'Reconcile balances' %}" class="btn btn-primary w-full" id="submit-id-submit">
</div> </div>
</div> </div>
</form> </form>
+16 -16
View File
@@ -1,9 +1,9 @@
{% load i18n %} {% load i18n %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container px-md-3 py-3 column-gap-5">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:mb-3"> <div class="text-3xl font-bold font-mono w-full mb-3">
{% spaceless %} {% spaceless %}
<div>{% translate 'Accounts' %}<span> <div>{% translate 'Accounts' %}<span>
<a class="tw:no-underline tw:text-2xl tw:p-1 category-action" <a class="no-underline text-2xl p-1 category-action"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}" data-bs-title="{% translate "Add" %}"
@@ -14,14 +14,14 @@
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-body tw:overflow-x-auto"> <div class="card-body overflow-x-auto">
{% if accounts %} {% if accounts %}
<c-config.search></c-config.search> <c-config.search></c-config.search>
<table class="tw:table tw:table-hover tw:whitespace-nowrap"> <table class="table table-hover whitespace-nowrap">
<thead> <thead>
<tr> <tr>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col">{% translate 'Name' %}</th> <th scope="col">{% translate 'Name' %}</th>
<th scope="col">{% translate 'Group' %}</th> <th scope="col">{% translate 'Group' %}</th>
<th scope="col">{% translate 'Currency' %}</th> <th scope="col">{% translate 'Currency' %}</th>
@@ -33,16 +33,16 @@
<tbody> <tbody>
{% for account in accounts %} {% for account in accounts %}
<tr class="account"> <tr class="account">
<td class="tw:w-auto"> <td class="w-auto">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}" data-bs-title="{% translate "Edit" %}"
hx-get="{% url 'account_edit' pk=account.id %}" hx-get="{% url 'account_edit' pk=account.id %}"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -54,7 +54,7 @@
data-confirm-text="{% translate "Yes, delete it!" %}" data-confirm-text="{% translate "Yes, delete it!" %}"
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
{% if not account.owner %} {% if not account.owner %}
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-primary" <a class="btn btn-secondary btn-sm join-item text-primary"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Take ownership" %}" data-bs-title="{% translate "Take ownership" %}"
@@ -62,7 +62,7 @@
<i class="fa-solid fa-crown fa-fw"></i></a> <i class="fa-solid fa-crown fa-fw"></i></a>
{% endif %} {% endif %}
{% if user == account.owner %} {% if user == account.owner %}
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-primary" <a class="btn btn-secondary btn-sm join-item text-primary"
role="button" role="button"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-swap="innerHTML" hx-swap="innerHTML"
@@ -71,7 +71,7 @@
hx-get="{% url 'account_share_settings' pk=account.id %}"> hx-get="{% url 'account_share_settings' pk=account.id %}">
<i class="fa-solid fa-share fa-fw"></i></a> <i class="fa-solid fa-share fa-fw"></i></a>
{% endif %} {% endif %}
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
hx-get="{% url 'account_toggle_untracked' pk=account.id %}" hx-get="{% url 'account_toggle_untracked' pk=account.id %}"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
@@ -88,8 +88,8 @@
<td>{{ account.group.name }}</td> <td>{{ account.group.name }}</td>
<td>{{ account.currency }}</td> <td>{{ account.currency }}</td>
<td>{% if account.exchange_currency %}{{ account.exchange_currency }}{% else %}-{% endif %}</td> <td>{% if account.exchange_currency %}{{ account.exchange_currency }}{% else %}-{% endif %}</td>
<td>{% if account.is_asset %}<i class="fa-solid fa-solid fa-check tw:text-success"></i>{% endif %}</td> <td>{% if account.is_asset %}<i class="fa-solid fa-solid fa-check text-success"></i>{% endif %}</td>
<td>{% if account.is_archived %}<i class="fa-solid fa-solid fa-check tw:text-success"></i>{% endif %}</td> <td>{% if account.is_archived %}<i class="fa-solid fa-solid fa-check text-success"></i>{% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
+23 -23
View File
@@ -2,67 +2,67 @@
{% load i18n %} {% load i18n %}
<div> <div>
<div class="tw:hidden tw:lg:grid tw:lg:grid-cols-7 tw:gap-4 tw:lg:gap-0 bg-base-300"> <div class="hidden lg:grid lg:grid-cols-7 gap-4 lg:gap-0 bg-base-300">
<div class="tw:border-l tw:border-t tw:border-b tw:border-base-100 tw:p-2 tw:text-center"> <div class="border-l border-t border-b border-base-100 p-2 text-center">
{% translate 'MON' %} {% translate 'MON' %}
</div> </div>
<div class="tw:border-t tw:border-b tw:border-base-300 tw:p-2 tw:text-center"> <div class="border-t border-b border-base-300 p-2 text-center">
{% translate 'TUE' %} {% translate 'TUE' %}
</div> </div>
<div class="tw:border-t tw:border-b tw:border-base-300 tw:p-2 tw:text-center"> <div class="border-t border-b border-base-300 p-2 text-center">
{% translate 'WED' %} {% translate 'WED' %}
</div> </div>
<div class="tw:border-t tw:border-b tw:border-base-300 tw:p-2 tw:text-center"> <div class="border-t border-b border-base-300 p-2 text-center">
{% translate 'THU' %} {% translate 'THU' %}
</div> </div>
<div class="tw:border-t tw:border-b tw:border-base-300 tw:p-2 tw:text-center"> <div class="border-t border-b border-base-300 p-2 text-center">
{% translate 'FRI' %} {% translate 'FRI' %}
</div> </div>
<div class="tw:border-t tw:border-b tw:border-base-300 tw:p-2 tw:text-center"> <div class="border-t border-b border-base-300 p-2 text-center">
{% translate 'SAT' %} {% translate 'SAT' %}
</div> </div>
<div class="tw:border-r tw:border-t tw:border-b tw:border-base-300 tw:p-2 tw:text-center"> <div class="border-r border-t border-b border-base-300 p-2 text-center">
{% translate 'SUN' %} {% translate 'SUN' %}
</div> </div>
</div> </div>
<div class="tw:grid tw:grid-cols-1 tw:grid-rows-1 tw:lg:grid-cols-7 tw:lg:grid-rows-6 tw:gap-4 tw:lg:gap-0"> <div class="grid grid-cols-1 grid-rows-1 lg:grid-cols-7 lg:grid-rows-6 gap-4 lg:gap-0">
{% for date in dates %} {% for date in dates %}
{% if date %} {% if date %}
<div class="tw:card tw:bg-base-200 tw:h-full tw:hover:bg-base-300! tw:rounded-none tw:card-border tw:border-base-content/50 {% if not date.transactions %}tw:hidden! tw:lg:flex!{% endif %}{% if today == date.date %} tw:card-border tw:border-2 tw:border-primary{% endif %}" role="button" <div class="card bg-base-200 h-full hover:bg-base-300! border-[1px] border-base-content/30 rounded-none card-border {% if not date.transactions %}hidden! lg:flex!{% endif %}{% if today == date.date %} card-border border-2 border-primary{% endif %} cursor-pointer" role="button"
hx-get="{% url 'calendar_transactions_list' day=date.date.day month=date.date.month year=date.date.year %}" hx-get="{% url 'calendar_transactions_list' day=date.date.day month=date.date.month year=date.date.year %}"
hx-target="#persistent-generic-offcanvas-left"> hx-target="#persistent-generic-offcanvas-left">
<div class="tw:card-header tw:border-0 tw:bg-transparent tw:text-end tw:flex tw:justify-between tw:p-2 tw:w-full"> <div class="card-header border-0 bg-transparent text-end flex justify-between p-2 w-full">
<div class="tw:lg:hidden tw:text-start tw:w-full">{{ date.date|date:"l"|lower }}</div> <div class="lg:hidden text-start w-full">{{ date.date|date:"l"|lower }}</div>
<div class="tw:text-end tw:w-full">{{ date.day }}</div> <div class="text-end w-full">{{ date.day }}</div>
</div> </div>
<div class="tw:card-body tw:p-2"> <div class="card-body p-2 flex flex-row flex-wrap gap-1">
{% for transaction in date.transactions %} {% for transaction in date.transactions %}
{% if transaction.is_paid %} {% if transaction.is_paid %}
{% if transaction.type == "IN" and not transaction.account.is_asset %} {% if transaction.type == "IN" and not transaction.account.is_asset %}
<i class="fa-solid fa-circle-check tw:text-green-400" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i> <i class="fa-solid fa-circle-check text-success" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i>
{% elif transaction.type == "IN" and transaction.account.is_asset %} {% elif transaction.type == "IN" and transaction.account.is_asset %}
<i class="fa-solid fa-circle-check tw:text-green-300" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i> <i class="fa-solid fa-circle-check text-success/80" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i>
{% elif transaction.type == "EX" and not transaction.account.is_asset %} {% elif transaction.type == "EX" and not transaction.account.is_asset %}
<i class="fa-solid fa-circle-check tw:text-red-400" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i> <i class="fa-solid fa-circle-check text-error" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i>
{% elif transaction.type == "EX" and transaction.account.is_asset %} {% elif transaction.type == "EX" and transaction.account.is_asset %}
<i class="fa-solid fa-circle-check tw:text-red-300" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i> <i class="fa-solid fa-circle-check text-error/80" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i>
{% endif %} {% endif %}
{% else %} {% else %}
{% if transaction.type == "IN" and not transaction.account.is_asset %} {% if transaction.type == "IN" and not transaction.account.is_asset %}
<i class="fa-regular fa-circle tw:text-green-400" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i> <i class="fa-regular fa-circle text-success" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i>
{% elif transaction.type == "IN" and transaction.account.is_asset %} {% elif transaction.type == "IN" and transaction.account.is_asset %}
<i class="fa-regular fa-circle tw:text-green-300" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i> <i class="fa-regular fa-circle text-success/80" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Income' %}{% endif %}"></i>
{% elif transaction.type == "EX" and not transaction.account.is_asset %} {% elif transaction.type == "EX" and not transaction.account.is_asset %}
<i class="fa-regular fa-circle tw:text-red-400" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i> <i class="fa-regular fa-circle text-error" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i>
{% elif transaction.type == "EX" and transaction.account.is_asset %} {% elif transaction.type == "EX" and transaction.account.is_asset %}
<i class="fa-regular fa-circle tw:text-red-300" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i> <i class="fa-regular fa-circle text-error/80" data-bs-toggle="tooltip" data-bs-title="{% if transaction.description %}{{ transaction.description }}{% else %}{% trans 'Expense' %}{% endif %}"></i>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
{% else %} {% else %}
<div class="tw:hidden! tw:lg:block! tw:card tw:bg-base-100 tw:h-full tw:rounded-none"></div> <div class="hidden! lg:block! card bg-base-100 h-full rounded-none"></div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</div> </div>
+12 -20
View File
@@ -12,45 +12,37 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:gap-x-5"> <div class="container px-md-3 py-3 gap-x-5">
<div class="tw:flex tw:flex-wrap tw:mb-4 tw:gap-x-xl-4 tw:gap-y-3"> <div class="flex flex-wrap mb-4 gap-x-xl-4 gap-y-3">
{# Date picker#} {# Date picker#}
<div class="tw:w-full tw:xl:w-4/12 tw:flex-row tw:items-center tw:flex"> <div class="w-full xl:w-4/12 flex-row items-center flex">
<div class="tw:text-base tw:h-full tw:items-center tw:flex"> <div class="text-base h-full items-center flex btn btn-ghost">
<a role="button" <a role="button"
class="tw:pe-4 tw:py-2"
hx-boost="true" hx-boost="true"
hx-trigger="click, previous_month from:window" hx-trigger="click, previous_month from:window"
href="{% url 'calendar' month=previous_month year=previous_year %}"><i href="{% url 'calendar' month=previous_month year=previous_year %}">
class="fa-solid fa-chevron-left"></i></a> <i class="fa-solid fa-chevron-left"></i>
</a>
</div> </div>
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:text-center" <div class="text-3xl font-bold font-mono w-full text-center btn btn-ghost"
hx-get="{% url 'month_year_picker' %}" hx-get="{% url 'month_year_picker' %}"
hx-target="#generic-offcanvas-left" hx-target="#generic-offcanvas-left"
hx-trigger="click, date_picker from:window" hx-trigger="click, date_picker from:window"
hx-vals='{"month": {{ month }}, "year": {{ year }}, "for": "calendar", "field": "date"}' role="button"> hx-vals='{"month": {{ month }}, "year": {{ year }}, "for": "calendar", "field": "date"}' role="button">
{{ month|month_name }} {{ year }} {{ month|month_name }} {{ year }}
</div> </div>
<div class="tw:text-base tw:mx-2 tw:h-full tw:items-center tw:flex"> <div class="text-base h-full items-center flex btn btn-ghost">
<a role="button" <a role="button"
class="tw:ps-3 tw:py-2"
hx-boost="true" hx-boost="true"
hx-trigger="click, next_month from:window" hx-trigger="click, next_month from:window"
href="{% url 'calendar' month=next_month year=next_year %}"> href="{% url 'calendar' month=next_month year=next_year %}">
<i class="fa-solid fa-chevron-right"></i> <i class="fa-solid fa-chevron-right"></i>
</a> </a>
</div> </div>
</div> </div>
{# Action buttons#}
<div class="tw:w-full tw:xl:w-8/12">
{# <c-ui.quick-transactions-buttons#}
{# :year="year"#}
{# :month="month"#}
{# ></c-ui.quick-transactions-buttons>#}
</div>
</div> </div>
<div class="tw:flex tw:flex-wrap"> <div class="flex flex-wrap">
<div class="show-loading tw:w-full" hx-get="{% url 'calendar_list' month=month year=year %}" <div class="show-loading w-full" hx-get="{% url 'calendar_list' month=month year=year %}"
hx-trigger="load, updated from:window, selective_update from:window, every 10m"></div> hx-trigger="load, updated from:window, selective_update from:window, every 10m"></div>
</div> </div>
</div> </div>
+9 -9
View File
@@ -1,9 +1,9 @@
{% load i18n %} {% load i18n %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container px-md-3 py-3 column-gap-5">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:mb-3"> <div class="text-3xl font-bold font-mono w-full mb-3">
{% spaceless %} {% spaceless %}
<div>{% translate 'Categories' %}<span> <div>{% translate 'Categories' %}<span>
<a class="tw:no-underline tw:text-2xl tw:p-1 category-action" <a class="no-underline text-2xl p-1 category-action"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}" data-bs-title="{% translate "Add" %}"
@@ -14,14 +14,14 @@
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-header tw:bg-base-200 tw:p-4"> <div class="card-header bg-base-200 p-4">
<div role="tablist" class="tw:tabs tw:tabs-lifted"> <div role="tablist" class="tabs tabs-lifted">
<button class="tw:tab tw:tab-active" data-bs-toggle="tab" type="button" role="tab" aria-selected="true" hx-get="{% url 'categories_table_active' %}" hx-trigger="load, click" hx-target="#categories-table">{% translate 'Active' %}</button> <button class="tab tab-active" data-bs-toggle="tab" type="button" role="tab" aria-selected="true" hx-get="{% url 'categories_table_active' %}" hx-trigger="load, click" hx-target="#categories-table">{% translate 'Active' %}</button>
<button class="tw:tab" hx-get="{% url 'categories_table_archived' %}" hx-target="#categories-table" data-bs-toggle="tab" type="button" role="tab" aria-selected="false">{% translate 'Archived' %}</button> <button class="tab" hx-get="{% url 'categories_table_archived' %}" hx-target="#categories-table" data-bs-toggle="tab" type="button" role="tab" aria-selected="false">{% translate 'Archived' %}</button>
</div> </div>
</div> </div>
<div class="tw:card-body"> <div class="card-body">
<div id="categories-table"></div> <div id="categories-table"></div>
</div> </div>
</div> </div>
+10 -10
View File
@@ -7,12 +7,12 @@
hx-swap="outerHTML"> hx-swap="outerHTML">
{% endif %} {% endif %}
{% if categories %} {% if categories %}
<div class="tw:overflow-x-auto"> <div class="overflow-x-auto">
<c-config.search></c-config.search> <c-config.search></c-config.search>
<table class="tw:table tw:table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col">{% translate 'Name' %}</th> <th scope="col">{% translate 'Name' %}</th>
<th scope="col">{% translate 'Muted' %}</th> <th scope="col">{% translate 'Muted' %}</th>
</tr> </tr>
@@ -20,9 +20,9 @@
<tbody> <tbody>
{% for category in categories %} {% for category in categories %}
<tr class="category"> <tr class="category">
<td class="tw:w-auto tw:text-center"> <td class="w-auto text-center">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
hx-swap="innerHTML" hx-swap="innerHTML"
@@ -30,7 +30,7 @@
hx-get="{% url 'category_edit' category_id=category.id %}" hx-get="{% url 'category_edit' category_id=category.id %}"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -43,7 +43,7 @@
data-confirm-text="{% translate "Yes, delete it!" %}" data-confirm-text="{% translate "Yes, delete it!" %}"
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
{% if not category.owner %} {% if not category.owner %}
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-primary" <a class="btn btn-secondary btn-sm join-item text-primary"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Take ownership" %}" data-bs-title="{% translate "Take ownership" %}"
@@ -51,7 +51,7 @@
<i class="fa-solid fa-crown fa-fw"></i></a> <i class="fa-solid fa-crown fa-fw"></i></a>
{% endif %} {% endif %}
{% if user == category.owner %} {% if user == category.owner %}
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-primary" <a class="btn btn-secondary btn-sm join-item text-primary"
role="button" role="button"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-swap="innerHTML" hx-swap="innerHTML"
@@ -64,7 +64,7 @@
</td> </td>
<td>{{ category.name }}</td> <td>{{ category.name }}</td>
<td> <td>
{% if category.mute %}<i class="fa-solid fa-check tw:text-success"></i>{% endif %} {% if category.mute %}<i class="fa-solid fa-check text-success"></i>{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@@ -7,24 +7,24 @@
{% block body %} {% block body %}
{% regroup month_year_data by year as years_list %} {% regroup month_year_data by year as years_list %}
<div role="tablist" class="tw:tabs tw:tabs-bordered tw:w-full" id="yearTabs"> <div role="tablist" class="tabs tabs-border w-full" id="yearTabs">
{% for x in years_list %} {% for x in years_list %}
<input type="radio" <input type="radio"
name="year_tabs" name="year_tabs"
role="tab" role="tab"
class="tw:tab" class="tab"
aria-label="{{ x.grouper }}" aria-label="{{ x.grouper }}"
id="tab-{{ x.grouper }}" id="tab-{{ x.grouper }}"
{% if x.grouper == current_year %}checked="checked"{% endif %} /> {% if x.grouper == current_year %}checked="checked"{% endif %} />
<div role="tabpanel" class="tw:tab-content tw:p-4" id="{{ x.grouper }}-pane"> <div role="tabpanel" class="tab-content" id="{{ x.grouper }}-pane">
<ul class="tw:menu tw:bg-base-100 tw:w-full" id="month-year-list" hx-boost="true"> <ul class="menu bg-base-100 w-full" id="month-year-list" hx-boost="true">
{% for month_data in x.list %} {% for month_data in x.list %}
<li {% if month_data.month == current_month and month_data.year == current_year %}class="tw:disabled"{% endif %}> <li {% if month_data.month == current_month and month_data.year == current_year %}class="disabled"{% endif %}>
<a class="{% if month_data.month == current_month and month_data.year == current_year %}tw:active{% endif %}" <a class="{% if month_data.month == current_month and month_data.year == current_year %}menu-active{% endif %}"
href={{ month_data.url }} href={{ month_data.url }}
{% if month_data.month == current_month and month_data.year == current_year %}aria-disabled="true"{% endif %}> {% if month_data.month == current_month and month_data.year == current_year %}aria-disabled="true"{% endif %}>
<span class="tw:flex-1">{{ month_data.month|month_name }}</span> <span class="flex-1">{{ month_data.month|month_name }}</span>
<span class="tw:badge tw:badge-primary">{{ month_data.transaction_count }}</span> <span class="badge badge-primary">{{ month_data.transaction_count }}</span>
</a> </a>
</li> </li>
{% endfor %} {% endfor %}
@@ -32,8 +32,8 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<hr class="tw:border-base-300"> <hr class="hr my-4">
<div class="tw:w-full tw:text-end"> <div class="w-full text-end">
<a class="tw:btn tw:btn-outline tw:btn-primary tw:btn-sm" href="{{ today_url }}" role="button" hx-boost="true">{% trans 'Today' %}</a> <a class="btn btn-outline btn-primary btn-sm" href="{{ today_url }}" role="button" hx-boost="true">{% trans 'Today' %}</a>
</div> </div>
{% endblock %} {% endblock %}
+5 -5
View File
@@ -2,12 +2,12 @@
{% load toast_bg %} {% load toast_bg %}
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<div class="tw:alert tw:alert-{{ message.tags | toast_bg }}" <div class="toasty alert alert-{{ message.tags | toast_bg }}"
role="alert" role="alert"
aria-live="assertive" aria-live="assertive"
aria-atomic="true"> aria-atomic="true">
<div class="tw:flex tw:items-center tw:justify-between tw:w-full"> <div class="flex items-center justify-between w-full">
<div class="tw:flex tw:items-center tw:gap-2"> <div class="flex items-center gap-2">
<i class="{{ message.tags | toast_icon }} fa-fw"></i> <i class="{{ message.tags | toast_icon }} fa-fw"></i>
<div> <div>
<strong>{{ message.tags | toast_title }}</strong> <strong>{{ message.tags | toast_title }}</strong>
@@ -15,8 +15,8 @@
</div> </div>
</div> </div>
<button type="button" <button type="button"
class="tw:btn tw:btn-ghost tw:btn-sm tw:btn-circle" class="btn btn-ghost btn-sm btn-circle"
data-bs-dismiss="toast" _="on click remove closest .toasty"
aria-label={% translate 'Close' %}> aria-label={% translate 'Close' %}>
<i class="fa-solid fa-xmark"></i> <i class="fa-solid fa-xmark"></i>
</button> </button>
+21 -2
View File
@@ -1,3 +1,22 @@
{#This is here so we can add dynamic Tailwind classes that will be required via JS/hyperscript but Tailwind has no knowledge of#} {#This is here so we can add dynamic Tailwind classes that will be required via JS/hyperscript but Tailwind has no knowledge of#}
<div class="tw:lg:w-[15vw]"></div> <div class="lg:w-[15vw]"></div>
<div class="tw:lg:ml-[16vw]"></div> <div class="lg:ml-[16vw]"></div>
<div class="grid-cols-12"></div>
<div class="md:col-span-1"></div>
<div class="md:col-span-2"></div>
<div class="md:col-span-3"></div>
<div class="md:col-span-4"></div>
<div class="md:col-span-5"></div>
<div class="md:col-span-6"></div>
<div class="md:col-span-7"></div>
<div class="md:col-span-8"></div>
<div class="md:col-span-9"></div>
<div class="md:col-span-10"></div>
<div class="md:col-span-11"></div>
<div class="md:col-span-12"></div>
<div class="alert-error"></div>
<div class="alert-info"></div>
<div class="alert-success"></div>
<div class="alert-warning"></div>
<div class="textarea"></div>
<div class="border-base-content/60"></div>
+2 -2
View File
@@ -1,9 +1,9 @@
{% load currency_display %} {% load currency_display %}
{% if not divless %} {% if not divless %}
<div class="{% if text_end %}tw:text-end{% elif text_start %}tw:text-start{% endif %}"> <div class="{% if text_end %}text-end{% elif text_start %}text-start{% endif %}">
{% endif %} {% endif %}
<span class="amount{% if color == 'grey' or color == "gray" %} tw:text-base-content/70{% elif color == 'green' %} tw:dark:text-green-400 tw:text-green-600 {% elif color == 'red' %} tw:dark:text-red-400 tw:text-red-600{% endif %} {{ custom_class }}" <span class="amount{% if color == 'grey' or color == "gray" %} text-base-content/70{% elif color == 'green' %} text-success {% elif color == 'red' %} text-error{% endif %} {{ custom_class }}"
data-original-value="{% currency_display amount=amount prefix=prefix suffix=suffix decimal_places=decimal_places %}" data-original-value="{% currency_display amount=amount prefix=prefix suffix=suffix decimal_places=decimal_places %}"
data-amount="{{ amount|floatformat:"-40u" }}"> data-amount="{{ amount|floatformat:"-40u" }}">
</span><span>{{ slot }}</span> </span><span>{{ slot }}</span>
+5 -5
View File
@@ -1,12 +1,12 @@
{% load i18n%} {% load i18n%}
<div class="tw:fab"> <div class="fab">
<div tabindex="0" role="button" class="tw:btn tw:btn-lg tw:btn-circle tw:btn-primary"> <div tabindex="0" role="button" class="btn btn-lg btn-circle btn-primary">
<i class="fa-solid fa-plus tw:text-2xl fa-fw"></i> <i class="fa-solid fa-plus text-2xl fa-fw"></i>
</div> </div>
<div class="tw:fab-close"> <div class="fab-close">
{% trans 'Close' %} <span class="tw:btn tw:btn-circle tw:btn-lg tw:btn-error"><i class="fa-solid fa-xmark tw:text-2xl fa-fw"></i></span> {% trans 'Close' %} <span class="btn btn-circle btn-lg btn-error"><i class="fa-solid fa-xmark text-2xl fa-fw"></i></span>
</div> </div>
{{ slot }} {{ slot }}
@@ -3,4 +3,4 @@
hx-target="{{ hx_target }}" hx-target="{{ hx_target }}"
hx-vals='{{ hx_vals }}'> hx-vals='{{ hx_vals }}'>
{{ title }} {{ title }}
<button class="tw:btn tw:btn-lg tw:btn-circle tw:btn-{{color}}"><i class="{{ icon }} fa-fw"></i></button></div> <button class="btn btn-lg btn-circle btn-{{color}}"><i class="{{ icon }} fa-fw"></i></button></div>
@@ -1,12 +1,12 @@
<li class="tw:lg:hidden tw:lg:group-hover:block"> <li class="lg:hidden lg:group-hover:block">
<div class="tw:flex tw:items-center" data-bs-toggle="collapse" href="#{{ title|slugify }}" role="button" <div class="flex items-center" data-bs-toggle="collapse" href="#{{ title|slugify }}" role="button"
aria-expanded="false" aria-controls="{{ title|slugify }}"> aria-expanded="false" aria-controls="{{ title|slugify }}">
<span <span
class="tw:text-base-content/60 tw:text-sm tw:font-bold tw:uppercase tw:lg:hidden tw:lg:group-hover:inline tw:me-2">{{ title }}</span> class="text-base-content/60 text-sm font-bold uppercase lg:hidden lg:group-hover:inline me-2">{{ title }}</span>
<hr class="tw:flex-grow"/> <hr class="flex-grow"/>
<i class="fas fa-chevron-down tw:text-base-content/60 tw:lg:before:hidden tw:lg:group-hover:before:inline tw:ml-2 tw:lg:ml-0 tw:lg:group-hover:ml-2"></i> <i class="fas fa-chevron-down text-base-content/60 lg:before:hidden lg:group-hover:before:inline ml-2 lg:ml-0 lg:group-hover:ml-2"></i>
</div> </div>
</li> </li>
<div class="tw:collapse tw:lg:hidden tw:lg:group-hover:block" id="{{ title|slugify }}"> <div class="collapse lg:hidden lg:group-hover:block" id="{{ title|slugify }}">
{{ slot }} {{ slot }}
</div> </div>
@@ -1,6 +1,6 @@
<li> <li>
<div class="tw:flex tw:items-center"> <div class="flex items-center">
<span class="sidebar-menu-header tw:text-base-content/60 tw:text-sm tw:font-bold tw:uppercase tw:mr-3">{{ title }}</span> <span class="sidebar-menu-header text-base-content/60 text-sm font-bold uppercase mr-3">{{ title }}</span>
<hr class="tw:text-base-content/60 tw:flex-grow"/> <hr class="hr flex-grow"/>
</div> </div>
</li> </li>
@@ -1,7 +1,7 @@
{% load active_link %} {% load active_link %}
<li> <li>
<a href="{% url url %}" <a href="{% url url %}"
class="tw:lg:text-sm tw:flex tw:items-center tw:no-underline tw:p-2 tw:rounded-3xl sidebar-item {% active_link views=active css_class="sidebar-active" %}" class="lg:text-sm flex items-center no-underline p-2 rounded-box sidebar-item {% active_link views=active css_class="sidebar-active" %}"
{% if tooltip %} {% if tooltip %}
data-bs-placement="right" data-bs-placement="right"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
@@ -9,6 +9,6 @@
{% endif %}> {% endif %}>
<i class="{{ icon }} fa-fw"></i> <i class="{{ icon }} fa-fw"></i>
<span <span
class="tw:ms-3 tw:font-medium tw:lg:group-hover:truncate tw:lg:group-focus:truncate tw:lg:group-hover:text-ellipsis tw:lg:group-focus:text-ellipsis">{{ title }}</span> class="ms-3 font-medium lg:group-hover:truncate lg:group-focus:truncate lg:group-hover:text-ellipsis lg:group-focus:text-ellipsis">{{ title }}</span>
</a> </a>
</li> </li>
@@ -2,7 +2,7 @@
<li> <li>
<a href="{{ url }}" <a href="{{ url }}"
hx-boost="false" hx-boost="false"
class="tw:lg:text-sm tw:flex tw:items-center tw:no-underline tw:p-2 tw:rounded-3xl sidebar-item {% active_link views=active css_class="sidebar-active" %}" class="lg:text-sm flex items-center no-underline p-2 rounded-3xl sidebar-item {% active_link views=active css_class="sidebar-active" %}"
{% if tooltip %} {% if tooltip %}
data-bs-placement="right" data-bs-placement="right"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
@@ -11,6 +11,6 @@
<i class="{{ icon }} fa-fw"></i> <i class="{{ icon }} fa-fw"></i>
<span <span
class="tw:ms-3 tw:font-medium tw:lg:group-hover:truncate tw:lg:group-focus:truncate tw:lg:group-hover:text-ellipsis tw:lg:group-focus:text-ellipsis">{{ title }}</span> class="ms-3 font-medium lg:group-hover:truncate lg:group-focus:truncate lg:group-hover:text-ellipsis lg:group-focus:text-ellipsis">{{ title }}</span>
</a> </a>
</li> </li>
+3 -3
View File
@@ -1,8 +1,8 @@
{% load i18n %} {% load i18n %}
<div id="search" class="tw:mb-3"> <div id="search" class="mb-3">
<label class="tw:w-full"> <label class="w-full">
<input type="search" <input type="search"
class="tw:input tw:input-bordered tw:w-full" class="input input-bordered w-full"
placeholder="{% translate 'Search' %}" placeholder="{% translate 'Search' %}"
_="on input or search _="on input or search
show < tbody>tr /> in <table/> show < tbody>tr /> in <table/>
+6 -6
View File
@@ -1,9 +1,9 @@
<div class="tw:grid tw:grid-cols-1 {% if not remove_padding %}tw:p-5{% endif %}"> <div class="grid grid-cols-1 {% if not remove_padding %}p-5{% endif %}">
<div class="{% if not remove_padding %}tw:p-5{% endif %}"> <div class="{% if not remove_padding %}p-5{% endif %}">
<div class="tw:text-center"> <div class="text-center">
<i class="{% if icon %}{{ icon }}{% else %}fa-solid fa-circle-xmark{% endif %} tw:text-6xl"></i> <i class="{% if icon %}{{ icon }}{% else %}fa-solid fa-circle-xmark{% endif %} text-6xl"></i>
<p class="tw:text-lg tw:mt-4 tw:mb-0">{{ title }}</p> <p class="text-lg mt-4 mb-0">{{ title }}</p>
<p class="tw:text-gray-500">{{ subtitle }}</p> <p class="text-gray-500">{{ subtitle }}</p>
</div> </div>
</div> </div>
</div> </div>
+73 -73
View File
@@ -1,22 +1,22 @@
{% load markdown %} {% load markdown %}
{% load i18n %} {% load i18n %}
<div <div
class="transaction {% if transaction.type == "EX" %}expense{% else %}income{% endif %} tw:group/transaction tw:relative tw:hover:z-10"> class="transaction {% if transaction.type == "EX" %}expense{% else %}income{% endif %} group/transaction relative hover:z-10">
<div class="tw:flex tw:my-1"> <div class="flex my-1">
{% if not disable_selection or not dummy %} {% if not disable_selection or not dummy %}
<label class="tw:px-3 tw:flex! tw:items-center tw:justify-center"> <label class="px-3 flex! items-center justify-center">
<input class="tw:checkbox" type="checkbox" name="transactions" value="{{ transaction.id }}" <input class="checkbox" type="checkbox" name="transactions" value="{{ transaction.id }}"
id="check-{{ transaction.id }}" aria-label="{% translate 'Select' %}" hx-preserve> id="check-{{ transaction.id }}" aria-label="{% translate 'Select' %}" hx-preserve>
</label> </label>
{% endif %} {% endif %}
<div class="tw:border-s-4 tw:border-e-0 tw:border-t-0 <div class="border-s-4 border-e-0 border-t-0
tw:hover:bg-neutral-500/20 tw:p-2 {% if transaction.account.is_asset %}tw:border-l-dashed{% else %}tw:border-solid{% endif %} hover:bg-base-200 p-2 {% if transaction.account.is_asset %}border-dashed{% else %}border-solid{% endif %}
{% if transaction.type == "EX" %}tw:border-l-red-500{% else %}tw:border-l-green-500{% endif %} tw:relative {% if transaction.type == "EX" %}border-s-error{% else %}border-s-success{% endif %} relative
tw:w-full transaction-item tw:bg-base-200 tw:rounded"> w-full transaction-item bg-base-100 rounded">
<div class="tw:flex tw:flex-wrap tw:font-mono tw:text-sm tw:items-center"> <div class="flex flex-wrap font-mono text-sm items-center">
<div class="tw:lg:w-auto tw:w-full tw:flex tw:items-center tw:text-2xl tw:lg:text-xl tw:lg:text-center tw:text-center tw:p-0 tw:cursor-pointer"> <div class="lg:w-auto w-full flex items-center text-2xl lg:text-xl lg:text-center text-center p-0 cursor-pointer">
{% if not transaction.deleted %} {% if not transaction.deleted %}
<a class="tw:no-underline tw:p-3 tw:text-gray-500!" <a class="no-underline p-3 text-gray-500!"
title="{% if transaction.is_paid %}{% trans 'Paid' %}{% else %}{% trans 'Projected' %}{% endif %}" title="{% if transaction.is_paid %}{% trans 'Paid' %}{% else %}{% trans 'Projected' %}{% endif %}"
role="button" role="button"
{% if not dummy %} {% if not dummy %}
@@ -27,7 +27,7 @@
class="fa-regular fa-circle"></i>{% endif %} class="fa-regular fa-circle"></i>{% endif %}
</a> </a>
{% else %} {% else %}
<div class="tw:no-underline tw:p-3 tw:text-gray-500!" <div class="no-underline p-3 text-gray-500!"
title="{% if transaction.is_paid %}{% trans 'Paid' %}{% else %}{% trans 'Projected' %}{% endif %}"> title="{% if transaction.is_paid %}{% trans 'Paid' %}{% else %}{% trans 'Projected' %}{% endif %}">
{% if transaction.is_paid %}<i class="fa-regular fa-circle-check"></i>{% else %}<i {% if transaction.is_paid %}<i class="fa-regular fa-circle-check"></i>{% else %}<i
class="fa-regular fa-circle"></i>{% endif %} class="fa-regular fa-circle"></i>{% endif %}
@@ -35,79 +35,79 @@
{% endif %} {% endif %}
</div> </div>
<div <div
class="tw:lg:flex-1 tw:w-full {% if transaction.account.is_untracked_by or transaction.category.mute or transaction.mute %}tw:brightness-80{% endif %}"> class="lg:flex-1 w-full {% if transaction.account.is_untracked_by or transaction.category.mute or transaction.mute %}brightness-80{% endif %}">
{# Date#} {# Date#}
<div class="tw:flex tw:flex-wrap tw:mb-2 tw:lg:mb-1 tw:text-base-content/70"> <div class="flex flex-wrap mb-2 lg:mb-1 text-base-content/70">
<div class="tw:w-auto tw:pe-1"><i class="fa-solid fa-calendar fa-fw tw:mr-1 fa-xs"></i></div> <div class="w-auto pe-1"><i class="fa-solid fa-calendar fa-fw mr-1 fa-xs"></i></div>
<div <div
class="tw:flex-1 tw:ps-0">{{ transaction.date|date:"SHORT_DATE_FORMAT" }} • {{ transaction.reference_date|date:"b/Y" }}</div> class="flex-1 ps-0">{{ transaction.date|date:"SHORT_DATE_FORMAT" }} • {{ transaction.reference_date|date:"b/Y" }}</div>
</div> </div>
{# Description#} {# Description#}
<div class="tw:mb-2 tw:lg:mb-1 tw:text-base-content tw:text-base"> <div class="mb-2 lg:mb-1 text-base-content text-base">
{% spaceless %} {% spaceless %}
<span class="{% if transaction.description %}tw:mr-2{% endif %}">{{ transaction.description }}</span> <span class="{% if transaction.description %}mr-2{% endif %}">{{ transaction.description }}</span>
{% if transaction.installment_plan and transaction.installment_id %} {% if transaction.installment_plan and transaction.installment_id %}
<span <span
class="tw:badge tw:badge-neutral tw:mx-1">{{ transaction.installment_id }}/{{ transaction.installment_plan.installment_total_number }}</span> class="badge badge-neutral mx-1">{{ transaction.installment_id }}/{{ transaction.installment_plan.installment_total_number }}</span>
{% endif %} {% endif %}
{% if transaction.recurring_transaction %} {% if transaction.recurring_transaction %}
<span class="tw:text-primary tw:text-xs tw:mx-1"><i class="fa-solid fa-arrows-rotate fa-fw"></i></span> <span class="text-primary text-xs mx-1"><i class="fa-solid fa-arrows-rotate fa-fw"></i></span>
{% endif %} {% endif %}
{% if transaction.dca_expense_entries.all or transaction.dca_income_entries.all %} {% if transaction.dca_expense_entries.all or transaction.dca_income_entries.all %}
<span class="tw:badge tw:badge-neutral tw:mx-1">{% trans 'DCA' %}</span> <span class="badge badge-neutral mx-1">{% trans 'DCA' %}</span>
{% endif %} {% endif %}
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:text-base-content/70 tw:text-sm"> <div class="text-base-content/70 text-sm">
{# Entities #} {# Entities #}
{% comment %} First, check for the highest priority: a valid 'overriden_entities' list. {% endcomment %} {% comment %} First, check for the highest priority: a valid 'overriden_entities' list. {% endcomment %}
{% if overriden_entities %} {% if overriden_entities %}
<div class="tw:flex tw:flex-wrap tw:mb-2 tw:lg:mb-1 tw:text-base-content/70"> <div class="flex flex-wrap mb-2 lg:mb-1 text-base-content/70">
<div class="tw:w-auto tw:pe-1"><i class="fa-solid fa-user-group fa-fw tw:mr-1 fa-xs"></i></div> <div class="w-auto pe-1"><i class="fa-solid fa-user-group fa-fw mr-1 fa-xs"></i></div>
<div class="tw:flex-1 tw:ps-0">{{ overriden_entities|join:", " }}</div> <div class="flex-1 ps-0">{{ overriden_entities|join:", " }}</div>
</div> </div>
{% comment %} If no override, fall back to transaction entities, but ONLY if the transaction has an ID. {% endcomment %} {% comment %} If no override, fall back to transaction entities, but ONLY if the transaction has an ID. {% endcomment %}
{% elif transaction.id and transaction.entities.all %} {% elif transaction.id and transaction.entities.all %}
<div class="tw:flex tw:flex-wrap tw:mb-2 tw:lg:mb-1 tw:text-base-content/70"> <div class="flex flex-wrap mb-2 lg:mb-1 text-base-content/70">
<div class="tw:w-auto tw:pe-1"><i class="fa-solid fa-user-group fa-fw tw:mr-1 fa-xs"></i></div> <div class="w-auto pe-1"><i class="fa-solid fa-user-group fa-fw mr-1 fa-xs"></i></div>
<div class="tw:flex-1 tw:ps-0">{{ transaction.entities.all|join:", " }}</div> <div class="flex-1 ps-0">{{ transaction.entities.all|join:", " }}</div>
</div> </div>
{% endif %} {% endif %}
{# Notes#} {# Notes#}
{% if transaction.notes %} {% if transaction.notes %}
<div class="tw:flex tw:flex-wrap tw:mb-2 tw:lg:mb-1 tw:text-base-content/70"> <div class="flex flex-wrap mb-2 lg:mb-1 text-base-content/70">
<div class="tw:w-auto tw:pe-1"><i class="fa-solid fa-align-left fa-fw tw:mr-1 fa-xs"></i></div> <div class="w-auto pe-1"><i class="fa-solid fa-align-left fa-fw mr-1 fa-xs"></i></div>
<div class="tw:flex-1 tw:ps-0">{{ transaction.notes | limited_markdown | linebreaksbr }}</div> <div class="flex-1 ps-0">{{ transaction.notes | limited_markdown | linebreaksbr }}</div>
</div> </div>
{% endif %} {% endif %}
{# Category#} {# Category#}
{% if transaction.category %} {% if transaction.category %}
<div class="tw:flex tw:flex-wrap tw:mb-2 tw:lg:mb-1 tw:text-base-content/70"> <div class="flex flex-wrap mb-2 lg:mb-1 text-base-content/70">
<div class="tw:w-auto tw:pe-1"><i class="fa-solid fa-icons fa-fw tw:mr-1 fa-xs"></i></div> <div class="w-auto pe-1"><i class="fa-solid fa-icons fa-fw mr-1 fa-xs"></i></div>
<div class="tw:flex-1 tw:ps-0">{{ transaction.category.name }}</div> <div class="flex-1 ps-0">{{ transaction.category.name }}</div>
</div> </div>
{% endif %} {% endif %}
{# Tags#} {# Tags#}
{% comment %} First, check for the highest priority: a valid 'overriden_tags' list. {% endcomment %} {% comment %} First, check for the highest priority: a valid 'overriden_tags' list. {% endcomment %}
{% if overriden_tags %} {% if overriden_tags %}
<div class="tw:flex tw:flex-wrap tw:mb-2 tw:lg:mb-1 tw:text-base-content/70"> <div class="flex flex-wrap mb-2 lg:mb-1 text-base-content/70">
<div class="tw:w-auto tw:pe-1"><i class="fa-solid fa-hashtag fa-fw tw:mr-1 fa-xs"></i></div> <div class="w-auto pe-1"><i class="fa-solid fa-hashtag fa-fw mr-1 fa-xs"></i></div>
<div class="tw:flex-1 tw:ps-0">{{ overriden_tags|join:", " }}</div> <div class="flex-1 ps-0">{{ overriden_tags|join:", " }}</div>
</div> </div>
{% comment %} If no override, fall back to transaction tags, but ONLY if the transaction has an ID. {% endcomment %} {% comment %} If no override, fall back to transaction tags, but ONLY if the transaction has an ID. {% endcomment %}
{% elif transaction.id and transaction.tags.all %} {% elif transaction.id and transaction.tags.all %}
<div class="tw:flex tw:flex-wrap tw:mb-2 tw:lg:mb-1 tw:text-base-content/70"> <div class="flex flex-wrap mb-2 lg:mb-1 text-base-content/70">
<div class="tw:w-auto tw:pe-1"><i class="fa-solid fa-hashtag fa-fw tw:mr-1 fa-xs"></i></div> <div class="w-auto pe-1"><i class="fa-solid fa-hashtag fa-fw mr-1 fa-xs"></i></div>
<div class="tw:flex-1 tw:ps-0">{{ transaction.tags.all|join:", " }}</div> <div class="flex-1 ps-0">{{ transaction.tags.all|join:", " }}</div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div <div
class="tw:lg:w-auto tw:w-full tw:lg:text-right tw:self-end {% if transaction.account.is_untracked_by or transaction.category.mute or transaction.mute %}tw:brightness-80{% endif %}"> class="lg:w-auto w-full lg:text-right self-end {% if transaction.account.is_untracked_by or transaction.category.mute or transaction.mute %}brightness-80{% endif %}">
<div class="main-amount tw:mb-2 tw:lg:mb-0"> <div class="main-amount mb-2 lg:mb-0">
<c-amount.display <c-amount.display
:amount="transaction.amount" :amount="transaction.amount"
:prefix="transaction.account.currency.prefix" :prefix="transaction.account.currency.prefix"
@@ -119,7 +119,7 @@
{% if not dummy %} {% if not dummy %}
{% with exchanged=transaction.exchanged_amount %} {% with exchanged=transaction.exchanged_amount %}
{% if exchanged %} {% if exchanged %}
<div class="exchanged-amount tw:mb-2 tw:lg:mb-0"> <div class="exchanged-amount mb-2 lg:mb-0">
<c-amount.display <c-amount.display
:amount="exchanged.amount" :amount="exchanged.amount"
:prefix="exchanged.prefix" :prefix="exchanged.prefix"
@@ -138,18 +138,18 @@
<div> <div>
{# Item actions#} {# Item actions#}
<div <div
class="transaction-actions tw:absolute! tw:left-1/2 tw:top-0 tw:-translate-x-1/2 tw:-translate-y-1/2 tw:invisible tw:group-hover/transaction:visible tw:flex tw:flex-row tw:card tw:bg-base-300"> class="transaction-actions absolute! left-1/2 top-0 -translate-x-1/2 -translate-y-1/2 invisible group-hover/transaction:visible! flex flex-row card bg-base-300">
<div class="tw:card-body tw:p-1 tw:shadow-lg tw:flex tw:flex-row tw:gap-1"> <div class="card-body p-1 shadow-lg flex flex-row gap-1">
{% if not transaction.deleted %} {% if not transaction.deleted %}
<div class="tw:tooltip" data-tip="{% translate "Edit" %}"> <div class="tooltip" data-tip="{% translate "Edit" %}">
<a class="tw:btn btn-neutral tw:btn-sm transaction-action" <a class="btn btn-neutral btn-sm transaction-action"
role="button" role="button"
hx-get="{% url 'transaction_edit' transaction_id=transaction.id %}" hx-get="{% url 'transaction_edit' transaction_id=transaction.id %}"
hx-target="#generic-offcanvas" hx-swap="innerHTML"> hx-target="#generic-offcanvas" hx-swap="innerHTML">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
</div> </div>
<div class="tw:tooltip" data-tip="{% translate "Delete" %}"> <div class="tooltip" data-tip="{% translate "Delete" %}">
<a class="tw:btn btn-neutral tw:btn-sm transaction-action" <a class="btn btn-neutral btn-sm transaction-action"
role="button" role="button"
hx-delete="{% url 'transaction_delete' transaction_id=transaction.id %}" hx-delete="{% url 'transaction_delete' transaction_id=transaction.id %}"
hx-trigger='confirmed' hx-trigger='confirmed'
@@ -157,33 +157,33 @@
data-title="{% translate "Are you sure?" %}" data-title="{% translate "Are you sure?" %}"
data-text="{% translate "You won't be able to revert this!" %}" data-text="{% translate "You won't be able to revert this!" %}"
data-confirm-text="{% translate "Yes, delete it!" %}" data-confirm-text="{% translate "Yes, delete it!" %}"
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw tw:text-red-500"></i> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw text-red-500"></i>
</a> </a>
</div> </div>
<div class="tw:dropdown tw:dropdown-end"> <div class="dropdown dropdown-end">
<button type="button" tabindex="0" role="button" class="tw:btn btn-neutral tw:btn-sm transaction-action"> <button type="button" tabindex="0" role="button" class="btn btn-neutral btn-sm transaction-action">
<i class="fa-solid fa-ellipsis fa-fw"></i> <i class="fa-solid fa-ellipsis fa-fw"></i>
</button> </button>
<ul tabindex="0" class="tw:dropdown-content tw:menu tw:p-2 tw:shadow tw:bg-base-200 tw:rounded-box tw:w-72 z-[1]"> <ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-200 rounded-box w-72 z-[1]">
{% if transaction.account.is_untracked_by %} {% if transaction.account.is_untracked_by %}
<li> <li>
<a class="tw:disabled tw:flex tw:items-center" aria-disabled="true"> <a class="disabled flex items-center" aria-disabled="true">
<i class="fa-solid fa-eye fa-fw tw:mr-2"></i> <i class="fa-solid fa-eye fa-fw mr-2"></i>
<div> <div>
{% translate 'Show on summaries' %} {% translate 'Show on summaries' %}
<div <div
class="tw:block tw:text-gray-500 tw:text-xs tw:font-medium">{% translate 'Controlled by account' %}</div> class="block text-gray-500 text-xs font-medium">{% translate 'Controlled by account' %}</div>
</div> </div>
</a> </a>
</li> </li>
{% elif transaction.category.mute %} {% elif transaction.category.mute %}
<li> <li>
<a class="tw:disabled tw:flex tw:items-center" aria-disabled="true"> <a class="disabled flex items-center" aria-disabled="true">
<i class="fa-solid fa-eye fa-fw tw:mr-2"></i> <i class="fa-solid fa-eye fa-fw mr-2"></i>
<div> <div>
{% translate 'Show on summaries' %} {% translate 'Show on summaries' %}
<div <div
class="tw:block tw:text-gray-500 tw:text-xs tw:font-medium">{% translate 'Controlled by category' %}</div> class="block text-gray-500 text-xs font-medium">{% translate 'Controlled by category' %}</div>
</div> </div>
</a> </a>
</li> </li>
@@ -191,43 +191,43 @@
<li><a href="#" <li><a href="#"
hx-get="{% url 'transaction_mute' transaction_id=transaction.id %}" hx-get="{% url 'transaction_mute' transaction_id=transaction.id %}"
hx-target="closest .transaction" hx-swap="outerHTML"><i hx-target="closest .transaction" hx-swap="outerHTML"><i
class="fa-solid fa-eye fa-fw tw:mr-2"></i>{% translate 'Show on summaries' %}</a></li> class="fa-solid fa-eye fa-fw mr-2"></i>{% translate 'Show on summaries' %}</a></li>
{% else %} {% else %}
<li><a href="#" <li><a href="#"
hx-get="{% url 'transaction_mute' transaction_id=transaction.id %}" hx-get="{% url 'transaction_mute' transaction_id=transaction.id %}"
hx-target="closest .transaction" hx-swap="outerHTML"><i hx-target="closest .transaction" hx-swap="outerHTML"><i
class="fa-solid fa-eye-slash fa-fw tw:mr-2"></i>{% translate 'Hide from summaries' %}</a></li> class="fa-solid fa-eye-slash fa-fw mr-2"></i>{% translate 'Hide from summaries' %}</a></li>
{% endif %} {% endif %}
<li><a href="#" <li><a href="#"
hx-get="{% url 'quick_transaction_add_as_quick_transaction' transaction_id=transaction.id %}"><i hx-get="{% url 'quick_transaction_add_as_quick_transaction' transaction_id=transaction.id %}"><i
class="fa-solid fa-person-running fa-fw tw:mr-2"></i>{% translate 'Add as quick transaction' %}</a> class="fa-solid fa-person-running fa-fw mr-2"></i>{% translate 'Add as quick transaction' %}</a>
</li> </li>
<hr class="tw:my-1 tw:text-base-content/60"> <hr class="my-1 hr">
<li><a href="#" <li><a href="#"
hx-get="{% url 'transaction_change_month' transaction_id=transaction.id change_type='previous' %}"><i hx-get="{% url 'transaction_change_month' transaction_id=transaction.id change_type='previous' %}"><i
class="fa-solid fa-calendar-minus fa-fw tw:mr-2"></i>{% translate 'Move to previous month' %}</a> class="fa-solid fa-calendar-minus fa-fw mr-2"></i>{% translate 'Move to previous month' %}</a>
</li> </li>
<li><a href="#" <li><a href="#"
hx-get="{% url 'transaction_change_month' transaction_id=transaction.id change_type='next' %}"><i hx-get="{% url 'transaction_change_month' transaction_id=transaction.id change_type='next' %}"><i
class="fa-solid fa-calendar-plus fa-fw tw:mr-2"></i>{% translate 'Move to next month' %}</a></li> class="fa-solid fa-calendar-plus fa-fw mr-2"></i>{% translate 'Move to next month' %}</a></li>
<li><a href="#" <li><a href="#"
hx-get="{% url 'transaction_move_to_today' transaction_id=transaction.id %}"><i hx-get="{% url 'transaction_move_to_today' transaction_id=transaction.id %}"><i
class="fa-solid fa-calendar-day fa-fw tw:mr-2"></i>{% translate 'Move to today' %}</a></li> class="fa-solid fa-calendar-day fa-fw mr-2"></i>{% translate 'Move to today' %}</a></li>
<hr class="tw:my-1 tw:text-base-content/60"> <hr class="my-1 hr">
<li><a href="#" <li><a href="#"
hx-get="{% url 'transaction_clone' transaction_id=transaction.id %}"><i hx-get="{% url 'transaction_clone' transaction_id=transaction.id %}"><i
class="fa-solid fa-clone fa-fw tw:mr-2"></i>{% translate 'Duplicate' %}</a></li> class="fa-solid fa-clone fa-fw mr-2"></i>{% translate 'Duplicate' %}</a></li>
</ul> </ul>
</div> </div>
{% else %} {% else %}
<div class="tw:tooltip" data-tip="{% translate "Restore" %}"> <div class="tooltip" data-tip="{% translate "Restore" %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm transaction-action" <a class="btn btn-secondary btn-sm transaction-action"
role="button" role="button"
hx-get="{% url 'transaction_undelete' transaction_id=transaction.id %}"><i hx-get="{% url 'transaction_undelete' transaction_id=transaction.id %}"><i
class="fa-solid fa-trash-arrow-up"></i></a> class="fa-solid fa-trash-arrow-up"></i></a>
</div> </div>
<div class="tw:tooltip" data-tip="{% translate "Delete" %}"> <div class="tooltip" data-tip="{% translate "Delete" %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm transaction-action" <a class="btn btn-secondary btn-sm transaction-action"
role="button" role="button"
hx-delete="{% url 'transaction_delete' transaction_id=transaction.id %}" hx-delete="{% url 'transaction_delete' transaction_id=transaction.id %}"
hx-trigger='confirmed' hx-trigger='confirmed'
@@ -235,7 +235,7 @@
data-title="{% translate "Are you sure?" %}" data-title="{% translate "Are you sure?" %}"
data-text="{% translate "You won't be able to revert this!" %}" data-text="{% translate "You won't be able to revert this!" %}"
data-confirm-text="{% translate "Yes, delete it!" %}" data-confirm-text="{% translate "Yes, delete it!" %}"
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw tw:text-red-500"></i> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw text-red-500"></i>
</a> </a>
</div> </div>
{% endif %} {% endif %}
@@ -1,38 +1,38 @@
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:mb-2 transaction-item"> <div class="card bg-base-100 shadow-xl mb-2 transaction-item">
<div class="tw:card-body tw:p-2 tw:flex tw:items-center tw:gap-3" data-bs-toggle="collapse" data-bs-target="#{{ transaction.id }}" role="button" aria-expanded="false" aria-controls="{{ transaction.id }}"> <div class="card-body p-2 flex items-center gap-3" data-bs-toggle="collapse" data-bs-target="#{{ transaction.id }}" role="button" aria-expanded="false" aria-controls="{{ transaction.id }}">
<!-- Main visible content --> <!-- Main visible content -->
<div class="tw:flex tw:flex-col tw:lg:flex-row tw:lg:items-center tw:w-full tw:gap-3"> <div class="flex flex-col lg:flex-row lg:items-center w-full gap-3">
<!-- Type indicator --> <!-- Type indicator -->
<div class="tw:w-8"> <div class="w-8">
{% if transaction.type == 'IN' %} {% if transaction.type == 'IN' %}
<span class="tw:badge tw:badge-success"></span> <span class="badge badge-success"></span>
{% else %} {% else %}
<span class="tw:badge tw:badge-error"></span> <span class="badge badge-error"></span>
{% endif %} {% endif %}
</div> </div>
<!-- Payment status --> <!-- Payment status -->
<div class="tw:w-8"> <div class="w-8">
{% if transaction.is_paid %} {% if transaction.is_paid %}
<span class="tw:badge tw:badge-success"></span> <span class="badge badge-success"></span>
{% else %} {% else %}
<span class="tw:badge tw:badge-warning"></span> <span class="badge badge-warning"></span>
{% endif %} {% endif %}
</div> </div>
<!-- Description --> <!-- Description -->
<div class="tw:flex-grow"> <div class="flex-grow">
<span class="tw:font-medium">{{ transaction.description }}</span> <span class="font-medium">{{ transaction.description }}</span>
</div> </div>
<!-- Amount --> <!-- Amount -->
<div class="tw:text-right tw:whitespace-nowrap"> <div class="text-right whitespace-nowrap">
<span class="{% if transaction.type == 'IN' %}tw:text-green-400{% else %}tw:text-red-400{% endif %}"> <span class="{% if transaction.type == 'IN' %}text-green-400{% else %}text-red-400{% endif %}">
{{ transaction.amount }} {{ transaction.amount }}
</span> </span>
{% if transaction.exchanged_amount %} {% if transaction.exchanged_amount %}
<br> <br>
<small class="tw:text-base-content/60"> <small class="text-base-content/60">
{{ transaction.exchanged_amount.prefix }}{{ transaction.exchanged_amount.amount }}{{ transaction.exchanged_amount.suffix }} {{ transaction.exchanged_amount.prefix }}{{ transaction.exchanged_amount.amount }}{{ transaction.exchanged_amount.suffix }}
</small> </small>
{% endif %} {% endif %}
@@ -41,50 +41,50 @@
</div> </div>
<!-- Expandable details --> <!-- Expandable details -->
<div class="tw:collapse" id="{{ transaction.id }}"> <div class="collapse" id="{{ transaction.id }}">
<div class="tw:card-body tw:p-3 transaction-details"> <div class="card-body p-3 transaction-details">
<div class="tw:grid tw:grid-cols-1 tw:md:grid-cols-2"> <div class="grid grid-cols-1 md:grid-cols-2">
<div> <div>
<dl class="tw:grid tw:grid-cols-3"> <dl class="grid grid-cols-3">
<dt class="tw:col-span-1">Date</dt> <dt class="col-span-1">Date</dt>
<dd class="tw:col-span-2">{{ transaction.date|date:"Y-m-d" }}</dd> <dd class="col-span-2">{{ transaction.date|date:"Y-m-d" }}</dd>
<dt class="tw:col-span-1">Reference Date</dt> <dt class="col-span-1">Reference Date</dt>
<dd class="tw:col-span-2">{{ transaction.reference_date|date:"Y-m" }}</dd> <dd class="col-span-2">{{ transaction.reference_date|date:"Y-m" }}</dd>
<dt class="tw:col-span-1">Account</dt> <dt class="col-span-1">Account</dt>
<dd class="tw:col-span-2">{{ transaction.account.name }}</dd> <dd class="col-span-2">{{ transaction.account.name }}</dd>
<dt class="tw:col-span-1">Category</dt> <dt class="col-span-1">Category</dt>
<dd class="tw:col-span-2">{{ transaction.category|default:"-" }}</dd> <dd class="col-span-2">{{ transaction.category|default:"-" }}</dd>
</dl> </dl>
</div> </div>
<div> <div>
<dl class="tw:grid tw:grid-cols-3"> <dl class="grid grid-cols-3">
{% if transaction.tags.exists %} {% if transaction.tags.exists %}
<dt class="tw:col-span-1">Tags</dt> <dt class="col-span-1">Tags</dt>
<dd class="tw:col-span-2"> <dd class="col-span-2">
{% for tag in transaction.tags.all %} {% for tag in transaction.tags.all %}
<span class="tw:badge tw:badge-secondary">{{ tag.name }}</span> <span class="badge badge-secondary">{{ tag.name }}</span>
{% endfor %} {% endfor %}
</dd> </dd>
{% endif %} {% endif %}
{% if transaction.installment_plan %} {% if transaction.installment_plan %}
<dt class="tw:col-span-1">Installment</dt> <dt class="col-span-1">Installment</dt>
<dd class="tw:col-span-2"> <dd class="col-span-2">
{{ transaction.installment_id }} of {{ transaction.installment_plan.total_installments }} {{ transaction.installment_id }} of {{ transaction.installment_plan.total_installments }}
</dd> </dd>
{% endif %} {% endif %}
{% if transaction.recurring_transaction %} {% if transaction.recurring_transaction %}
<dt class="tw:col-span-1">Recurring</dt> <dt class="col-span-1">Recurring</dt>
<dd class="tw:col-span-2">Yes</dd> <dd class="col-span-2">Yes</dd>
{% endif %} {% endif %}
{% if transaction.notes %} {% if transaction.notes %}
<dt class="tw:col-span-1">Notes</dt> <dt class="col-span-1">Notes</dt>
<dd class="tw:col-span-2">{{ transaction.notes }}</dd> <dd class="col-span-2">{{ transaction.notes }}</dd>
{% endif %} {% endif %}
</dl> </dl>
</div> </div>
+54 -54
View File
@@ -1,22 +1,22 @@
{% load tools %} {% load tools %}
{% load i18n %} {% load i18n %}
<div class="col tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-md card-border border-base-300">
<div class="tw:card-body"> <div class="card-body">
{% if account.account.group %} {% if account.account.group %}
<div class="tw:text-sm tw:mb-2"> <div class="text-sm mb-2">
<span class="tw:badge tw:badge-primary">{{ account.account.group }}</span> <span class="badge badge-primary">{{ account.account.group }}</span>
</div> </div>
{% endif %} {% endif %}
<h5 class="tw:card-title"> <h5 class="card-title">
{{ account.account.name }} {{ account.account.name }}
</h5> </h5>
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'projected income' %}</div> <div class="text-gray-400">{% translate 'projected income' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
{% if account.income_projected != 0 %} {% if account.income_projected != 0 %}
<div class="tw:text-end tw:font-mono tw:text-green-400"> <div class="text-end font-mono text-green-400">
<c-amount.display <c-amount.display
:amount="account.income_projected" :amount="account.income_projected"
:prefix="account.currency.prefix" :prefix="account.currency.prefix"
@@ -24,11 +24,11 @@
:decimal_places="account.currency.decimal_places"></c-amount.display> :decimal_places="account.currency.decimal_places"></c-amount.display>
</div> </div>
{% else %} {% else %}
<div class="tw:text-end tw:font-mono">-</div> <div class="text-end font-mono">-</div>
{% endif %} {% endif %}
</div> </div>
{% if account.exchanged and account.exchanged.income_projected %} {% if account.exchanged and account.exchanged.income_projected %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="account.exchanged.income_projected" :amount="account.exchanged.income_projected"
:prefix="account.exchanged.currency.prefix" :prefix="account.exchanged.currency.prefix"
@@ -36,14 +36,14 @@
:decimal_places="account.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="account.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'projected expenses' %}</div> <div class="text-gray-400">{% translate 'projected expenses' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
<div> <div>
{% if account.expense_projected != 0 %} {% if account.expense_projected != 0 %}
<div class="tw:text-end tw:font-mono tw:text-red-400"> <div class="text-end font-mono text-red-400">
<c-amount.display <c-amount.display
:amount="account.expense_projected" :amount="account.expense_projected"
:prefix="account.currency.prefix" :prefix="account.currency.prefix"
@@ -51,12 +51,12 @@
:decimal_places="account.currency.decimal_places"></c-amount.display> :decimal_places="account.currency.decimal_places"></c-amount.display>
</div> </div>
{% else %} {% else %}
<div class="tw:text-end tw:font-mono">-</div> <div class="text-end font-mono">-</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% if account.exchanged and account.exchanged.expense_projected %} {% if account.exchanged and account.exchanged.expense_projected %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="account.exchanged.expense_projected" :amount="account.exchanged.expense_projected"
:prefix="account.exchanged.currency.prefix" :prefix="account.exchanged.currency.prefix"
@@ -64,13 +64,13 @@
:decimal_places="account.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="account.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'projected total' %}</div> <div class="text-gray-400">{% translate 'projected total' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
<div <div
class="tw:text-end tw:font-mono"> class="text-end font-mono">
<c-amount.display <c-amount.display
:amount="account.total_projected" :amount="account.total_projected"
:prefix="account.currency.prefix" :prefix="account.currency.prefix"
@@ -80,7 +80,7 @@
</div> </div>
</div> </div>
{% if account.exchanged.total_projected and account.exchanged.total_projected %} {% if account.exchanged.total_projected and account.exchanged.total_projected %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="account.exchanged.total_projected" :amount="account.exchanged.total_projected"
:prefix="account.exchanged.currency.prefix" :prefix="account.exchanged.currency.prefix"
@@ -88,14 +88,14 @@
:decimal_places="account.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="account.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<hr class="tw:my-3"> <hr class="my-3">
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'current income' %}</div> <div class="text-gray-400">{% translate 'current income' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
{% if account.income_current != 0 %} {% if account.income_current != 0 %}
<div class="tw:text-end tw:font-mono tw:text-green-400"> <div class="text-end font-mono text-green-400">
<c-amount.display <c-amount.display
:amount="account.income_current" :amount="account.income_current"
:prefix="account.currency.prefix" :prefix="account.currency.prefix"
@@ -103,11 +103,11 @@
:decimal_places="account.currency.decimal_places"></c-amount.display> :decimal_places="account.currency.decimal_places"></c-amount.display>
</div> </div>
{% else %} {% else %}
<div class="tw:text-end tw:font-mono">-</div> <div class="text-end font-mono">-</div>
{% endif %} {% endif %}
</div> </div>
{% if account.exchanged and account.exchanged.income_current %} {% if account.exchanged and account.exchanged.income_current %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="account.exchanged.income_current" :amount="account.exchanged.income_current"
:prefix="account.exchanged.currency.prefix" :prefix="account.exchanged.currency.prefix"
@@ -115,13 +115,13 @@
:decimal_places="account.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="account.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'current expenses' %}</div> <div class="text-gray-400">{% translate 'current expenses' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
{% if account.expense_current != 0 %} {% if account.expense_current != 0 %}
<div class="tw:text-end tw:font-mono tw:text-red-400"> <div class="text-end font-mono text-red-400">
<c-amount.display <c-amount.display
:amount="account.expense_current" :amount="account.expense_current"
:prefix="account.currency.prefix" :prefix="account.currency.prefix"
@@ -129,11 +129,11 @@
:decimal_places="account.currency.decimal_places"></c-amount.display> :decimal_places="account.currency.decimal_places"></c-amount.display>
</div> </div>
{% else %} {% else %}
<div class="tw:text-end tw:font-mono">-</div> <div class="text-end font-mono">-</div>
{% endif %} {% endif %}
</div> </div>
{% if account.exchanged and account.exchanged.expense_current %} {% if account.exchanged and account.exchanged.expense_current %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="account.exchanged.expense_current" :amount="account.exchanged.expense_current"
:prefix="account.exchanged.currency.prefix" :prefix="account.exchanged.currency.prefix"
@@ -141,12 +141,12 @@
:decimal_places="account.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="account.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'current total' %}</div> <div class="text-gray-400">{% translate 'current total' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<c-amount.display <c-amount.display
:amount="account.total_current" :amount="account.total_current"
:prefix="account.currency.prefix" :prefix="account.currency.prefix"
@@ -156,7 +156,7 @@
</div> </div>
</div> </div>
{% if account.exchanged and account.exchanged.total_current %} {% if account.exchanged and account.exchanged.total_current %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="account.exchanged.total_current" :amount="account.exchanged.total_current"
:prefix="account.exchanged.currency.prefix" :prefix="account.exchanged.currency.prefix"
@@ -165,13 +165,13 @@
</div> </div>
{% endif %} {% endif %}
<div> <div>
<hr class="tw:my-3"> <hr class="my-3">
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'final total' %}</div> <div class="text-gray-400">{% translate 'final total' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<c-amount.display <c-amount.display
:amount="account.total_final" :amount="account.total_final"
:prefix="account.currency.prefix" :prefix="account.currency.prefix"
@@ -181,7 +181,7 @@
</div> </div>
</div> </div>
{% if account.exchanged and account.exchanged.total_final %} {% if account.exchanged and account.exchanged.total_final %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="account.exchanged.total_final" :amount="account.exchanged.total_final"
:prefix="account.exchanged.currency.prefix" :prefix="account.exchanged.currency.prefix"
@@ -191,7 +191,7 @@
{% endif %} {% endif %}
</div> </div>
{% with p=percentages|get_dict_item:account_id %} {% with p=percentages|get_dict_item:account_id %}
<div class="tw:my-3"> <div class="my-3">
<c-ui.percentage-distribution :percentage="p"></c-ui.percentage-distribution> <c-ui.percentage-distribution :percentage="p"></c-ui.percentage-distribution>
</div> </div>
{% endwith %} {% endwith %}
+2 -2
View File
@@ -1,5 +1,5 @@
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:relative tw:h-full"> <div class="card bg-base-100 shadow-xl relative h-full">
<div class="tw:card-body"> <div class="card-body">
{{ slot }} {{ slot }}
</div> </div>
</div> </div>
+52 -52
View File
@@ -1,17 +1,17 @@
{% load tools %} {% load tools %}
{% load i18n %} {% load i18n %}
<div class="col tw:card tw:bg-base-100 tw:shadow-xl"> <div class="col card bg-base-100 shadow card-border">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title"> <h5 class="card-title">
{{ currency.currency.name }} {{ currency.currency.name }}
</h5> </h5>
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'projected income' %}</div> <div class="text-gray-400">{% translate 'projected income' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
{% if currency.income_projected != 0 %} {% if currency.income_projected != 0 %}
<div class="tw:text-end tw:font-mono tw:text-green-400"> <div class="text-end font-mono text-green-400">
<c-amount.display <c-amount.display
:amount="currency.income_projected" :amount="currency.income_projected"
:prefix="currency.currency.prefix" :prefix="currency.currency.prefix"
@@ -19,11 +19,11 @@
:decimal_places="currency.currency.decimal_places"></c-amount.display> :decimal_places="currency.currency.decimal_places"></c-amount.display>
</div> </div>
{% else %} {% else %}
<div class="tw:text-end tw:font-mono">-</div> <div class="text-end font-mono">-</div>
{% endif %} {% endif %}
</div> </div>
{% if currency.exchanged and currency.exchanged.income_projected %} {% if currency.exchanged and currency.exchanged.income_projected %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="currency.exchanged.income_projected" :amount="currency.exchanged.income_projected"
:prefix="currency.exchanged.currency.prefix" :prefix="currency.exchanged.currency.prefix"
@@ -31,14 +31,14 @@
:decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'projected expenses' %}</div> <div class="text-gray-400">{% translate 'projected expenses' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
<div> <div>
{% if currency.expense_projected != 0 %} {% if currency.expense_projected != 0 %}
<div class="tw:text-end tw:font-mono tw:text-red-400"> <div class="text-end font-mono text-red-400">
<c-amount.display <c-amount.display
:amount="currency.expense_projected" :amount="currency.expense_projected"
:prefix="currency.currency.prefix" :prefix="currency.currency.prefix"
@@ -46,12 +46,12 @@
:decimal_places="currency.currency.decimal_places"></c-amount.display> :decimal_places="currency.currency.decimal_places"></c-amount.display>
</div> </div>
{% else %} {% else %}
<div class="tw:text-end tw:font-mono">-</div> <div class="text-end font-mono">-</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% if currency.exchanged and currency.exchanged.expense_projected %} {% if currency.exchanged and currency.exchanged.expense_projected %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="currency.exchanged.expense_projected" :amount="currency.exchanged.expense_projected"
:prefix="currency.exchanged.currency.prefix" :prefix="currency.exchanged.currency.prefix"
@@ -59,12 +59,12 @@
:decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'projected total' %}</div> <div class="text-gray-400">{% translate 'projected total' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<c-amount.display <c-amount.display
:amount="currency.total_projected" :amount="currency.total_projected"
:prefix="currency.currency.prefix" :prefix="currency.currency.prefix"
@@ -74,7 +74,7 @@
</div> </div>
</div> </div>
{% if currency.exchanged.total_projected and currency.exchanged.total_projected %} {% if currency.exchanged.total_projected and currency.exchanged.total_projected %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="currency.exchanged.total_projected" :amount="currency.exchanged.total_projected"
:prefix="currency.exchanged.currency.prefix" :prefix="currency.exchanged.currency.prefix"
@@ -82,14 +82,14 @@
:decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<hr class="tw:my-3"> <hr class="my-3">
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'current income' %}</div> <div class="text-gray-400">{% translate 'current income' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
{% if currency.income_current != 0 %} {% if currency.income_current != 0 %}
<div class="tw:text-end tw:font-mono tw:text-green-400"> <div class="text-end font-mono text-green-400">
<c-amount.display <c-amount.display
:amount="currency.income_current" :amount="currency.income_current"
:prefix="currency.currency.prefix" :prefix="currency.currency.prefix"
@@ -97,11 +97,11 @@
:decimal_places="currency.currency.decimal_places"></c-amount.display> :decimal_places="currency.currency.decimal_places"></c-amount.display>
</div> </div>
{% else %} {% else %}
<div class="tw:text-end tw:font-mono">-</div> <div class="text-end font-mono">-</div>
{% endif %} {% endif %}
</div> </div>
{% if currency.exchanged and currency.exchanged.income_current %} {% if currency.exchanged and currency.exchanged.income_current %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="currency.exchanged.income_current" :amount="currency.exchanged.income_current"
:prefix="currency.exchanged.currency.prefix" :prefix="currency.exchanged.currency.prefix"
@@ -109,13 +109,13 @@
:decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'current expenses' %}</div> <div class="text-gray-400">{% translate 'current expenses' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
{% if currency.expense_current != 0 %} {% if currency.expense_current != 0 %}
<div class="tw:text-end tw:font-mono tw:text-red-400"> <div class="text-end font-mono text-red-400">
<c-amount.display <c-amount.display
:amount="currency.expense_current" :amount="currency.expense_current"
:prefix="currency.currency.prefix" :prefix="currency.currency.prefix"
@@ -123,11 +123,11 @@
:decimal_places="currency.currency.decimal_places"></c-amount.display> :decimal_places="currency.currency.decimal_places"></c-amount.display>
</div> </div>
{% else %} {% else %}
<div class="tw:text-end tw:font-mono">-</div> <div class="text-end font-mono">-</div>
{% endif %} {% endif %}
</div> </div>
{% if currency.exchanged and currency.exchanged.expense_current %} {% if currency.exchanged and currency.exchanged.expense_current %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="currency.exchanged.expense_current" :amount="currency.exchanged.expense_current"
:prefix="currency.exchanged.currency.prefix" :prefix="currency.exchanged.currency.prefix"
@@ -135,12 +135,12 @@
:decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display> :decimal_places="currency.exchanged.currency.decimal_places"></c-amount.display>
</div> </div>
{% endif %} {% endif %}
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'current total' %}</div> <div class="text-gray-400">{% translate 'current total' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<c-amount.display <c-amount.display
:amount="currency.total_current" :amount="currency.total_current"
:prefix="currency.currency.prefix" :prefix="currency.currency.prefix"
@@ -150,7 +150,7 @@
</div> </div>
</div> </div>
{% if currency.exchanged and currency.exchanged.total_current %} {% if currency.exchanged and currency.exchanged.total_current %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="currency.exchanged.total_current" :amount="currency.exchanged.total_current"
:prefix="currency.exchanged.currency.prefix" :prefix="currency.exchanged.currency.prefix"
@@ -159,13 +159,13 @@
</div> </div>
{% endif %} {% endif %}
<div> <div>
<hr class="tw:my-3"> <hr class="my-3">
<div class="tw:flex tw:justify-between tw:items-baseline tw:mt-2"> <div class="flex justify-between items-baseline mt-2">
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<div class="tw:text-gray-400">{% translate 'final total' %}</div> <div class="text-gray-400">{% translate 'final total' %}</div>
</div> </div>
<div class="dotted-line tw:flex-grow"></div> <div class="dotted-line flex-grow"></div>
<div class="tw:text-end tw:font-mono"> <div class="text-end font-mono">
<c-amount.display <c-amount.display
:amount="currency.total_final" :amount="currency.total_final"
:prefix="currency.currency.prefix" :prefix="currency.currency.prefix"
@@ -175,7 +175,7 @@
</div> </div>
</div> </div>
{% if currency.exchanged and currency.exchanged.total_final %} {% if currency.exchanged and currency.exchanged.total_final %}
<div class="tw:text-end tw:font-mono tw:text-gray-500"> <div class="text-end font-mono text-gray-500">
<c-amount.display <c-amount.display
:amount="currency.exchanged.total_final" :amount="currency.exchanged.total_final"
:prefix="currency.exchanged.currency.prefix" :prefix="currency.exchanged.currency.prefix"
@@ -185,7 +185,7 @@
{% endif %} {% endif %}
</div> </div>
{% with p=percentages|get_dict_item:currency_id %} {% with p=percentages|get_dict_item:currency_id %}
<div class="tw:my-3"> <div class="my-3">
<c-ui.percentage-distribution :percentage="p"></c-ui.percentage-distribution> <c-ui.percentage-distribution :percentage="p"></c-ui.percentage-distribution>
</div> </div>
{% endwith %} {% endwith %}
@@ -1,53 +1,53 @@
{% load i18n %} {% load i18n %}
<div class="tw:sticky tw:bottom-4 tw:left-0 tw:right-0 tw:z-50 tw:hidden tw:mx-auto tw:w-fit" id="actions-bar" <div class="sticky bottom-4 left-0 right-0 z-50 hidden mx-auto w-fit" id="actions-bar"
_="on change from #transactions-list or htmx:afterSettle from window _="on change from #transactions-list or htmx:afterSettle from window
if #actions-bar then if #actions-bar then
if no <input[type='checkbox']:checked/> in #transactions-list if no <input[type='checkbox']:checked/> in #transactions-list
if #actions-bar if #actions-bar
add .slide-in-bottom-reverse then settle add .slide-in-bottom-reverse then settle
then add .tw:hidden to #actions-bar then add .hidden to #actions-bar
then remove .slide-in-bottom-reverse then remove .slide-in-bottom-reverse
end end
else else
if #actions-bar if #actions-bar
remove .tw:hidden from #actions-bar remove .hidden from #actions-bar
then trigger selected_transactions_updated then trigger selected_transactions_updated
end end
end end
end end
end"> end">
<div class="tw:card tw:bg-base-100 tw:shadow slide-in-bottom"> <div class="card bg-base-100 shadow slide-in-bottom">
<div class="tw:card-body tw:p-2 tw:flex tw:justify-between tw:items-center tw:gap-3"> <div class="card-body p-2 flex justify-between items-center gap-3">
{% spaceless %} {% spaceless %}
<div class="tw:dropdown"> <div class="dropdown">
<button tabindex="0" role="button" class="tw:btn tw:btn-secondary tw:btn-sm" type="button"> <button tabindex="0" role="button" class="btn btn-secondary btn-sm" type="button">
<i class="fa-regular fa-square-check fa-fw"></i> <i class="fa-regular fa-square-check fa-fw"></i>
<i class="fa-solid fa-chevron-down fa-xs"></i> <i class="fa-solid fa-chevron-down fa-xs"></i>
</button> </button>
<ul tabindex="0" class="tw:dropdown-content tw:menu tw:bg-base-100 tw:rounded-box tw:z-[1] tw:w-52 tw:p-2 tw:shadow"> <ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow">
<li> <li>
<a class="tw:cursor-pointer" <a class="cursor-pointer"
_="on click set <#transactions-list input[type='checkbox']/>'s checked to true then call me.blur() then trigger change"> _="on click set <#transactions-list input[type='checkbox']/>'s checked to true then call me.blur() then trigger change">
<i class="fa-regular fa-square-check tw:text-green-400 tw:me-3"></i>{% translate 'Select All' %} <i class="fa-regular fa-square-check text-green-400 me-3"></i>{% translate 'Select All' %}
</a> </a>
</li> </li>
<li> <li>
<a class="tw:cursor-pointer" <a class="cursor-pointer"
_="on click set <#transactions-list input[type='checkbox']/>'s checked to false then call me.blur() then trigger change"> _="on click set <#transactions-list input[type='checkbox']/>'s checked to false then call me.blur() then trigger change">
<i class="fa-regular fa-square tw:text-red-400 tw:me-3"></i>{% translate 'Unselect All' %} <i class="fa-regular fa-square text-red-400 me-3"></i>{% translate 'Unselect All' %}
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="tw:divider tw:divider-horizontal tw:m-0"></div> <div class="divider divider-horizontal m-0"></div>
<button class="tw:btn tw:btn-secondary tw:btn-sm" <button class="btn btn-secondary btn-sm"
hx-get="{% url 'transactions_bulk_undelete' %}" hx-get="{% url 'transactions_bulk_undelete' %}"
hx-include=".transaction" hx-include=".transaction"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate 'Restore' %}"> data-bs-title="{% translate 'Restore' %}">
<i class="fa-solid fa-trash-arrow-up fa-fw"></i> <i class="fa-solid fa-trash-arrow-up fa-fw"></i>
</button> </button>
<button class="tw:btn tw:btn-secondary tw:btn-sm" <button class="btn btn-secondary btn-sm"
hx-get="{% url 'transactions_bulk_delete' %}" hx-get="{% url 'transactions_bulk_delete' %}"
hx-include=".transaction" hx-include=".transaction"
hx-trigger="confirmed" hx-trigger="confirmed"
@@ -58,10 +58,10 @@
data-text="{% translate "You won't be able to revert this!" %}" data-text="{% translate "You won't be able to revert this!" %}"
data-confirm-text="{% translate "Yes, delete them!" %}" data-confirm-text="{% translate "Yes, delete them!" %}"
_="install prompt_swal"> _="install prompt_swal">
<i class="fa-solid fa-trash tw:text-error"></i> <i class="fa-solid fa-trash text-error"></i>
</button> </button>
<div class="tw:divider tw:divider-horizontal tw:m-0"></div> <div class="divider divider-horizontal m-0"></div>
<div class="tw:dropdown tw:dropdown-end" <div class="dropdown dropdown-end"
_="on selected_transactions_updated from #actions-bar _="on selected_transactions_updated from #actions-bar
set realTotal to math.bignumber(0) set realTotal to math.bignumber(0)
set flatTotal to math.bignumber(0) set flatTotal to math.bignumber(0)
@@ -99,28 +99,28 @@
put mean.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-mean's innerText put mean.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-mean's innerText
put flatAmountValues.length.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-count's innerText put flatAmountValues.length.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-count's innerText
end"> end">
<button class="tw:btn tw:btn-secondary tw:btn-sm" _="on click <button class="btn btn-secondary btn-sm" _="on click
set original_value to #real-total-front's innerText set original_value to #real-total-front's innerText
writeText(original_value) on navigator.clipboard writeText(original_value) on navigator.clipboard
put '{% translate "copied!" %}' into #real-total-front's innerText put '{% translate "copied!" %}' into #real-total-front's innerText
wait 1s wait 1s
put original_value into #real-total-front's innerText put original_value into #real-total-front's innerText
end"> end">
<i class="fa-solid fa-plus fa-fw tw:me-md-2 tw:text-primary"></i> <i class="fa-solid fa-plus fa-fw me-md-2 text-primary"></i>
<span class="tw:hidden tw:md:inline-block" id="real-total-front">0</span> <span class="hidden md:inline-block" id="real-total-front">0</span>
</button> </button>
<button type="button" tabindex="0" role="button" class="tw:btn tw:btn-sm tw:btn-secondary"> <button type="button" tabindex="0" role="button" class="btn btn-sm btn-secondary">
<i class="fa-solid fa-chevron-down fa-xs"></i> <i class="fa-solid fa-chevron-down fa-xs"></i>
</button> </button>
<ul tabindex="0" class="tw:dropdown-content tw:menu tw:bg-base-100 tw:rounded-box tw:z-[1] tw:w-52 tw:shadow"> <ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 shadow">
<li> <li>
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Flat Total" %} {% trans "Flat Total" %}
</div> </div>
<a class="tw:px-3 tw:cursor-pointer" <a class="px-3 cursor-pointer"
id="calc-menu-flat-total" id="calc-menu-flat-total"
_="on click _="on click
set original_value to my innerText set original_value to my innerText
@@ -135,12 +135,12 @@
</div> </div>
</li> </li>
<li> <li>
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Real Total" %} {% trans "Real Total" %}
</div> </div>
<a class="tw:px-3 tw:cursor-pointer" <a class="px-3 cursor-pointer"
id="calc-menu-real-total" id="calc-menu-real-total"
_="on click _="on click
set original_value to my innerText set original_value to my innerText
@@ -155,12 +155,12 @@
</div> </div>
</li> </li>
<li> <li>
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Mean" %} {% trans "Mean" %}
</div> </div>
<a class="tw:px-3 tw:cursor-pointer" <a class="px-3 cursor-pointer"
id="calc-menu-mean" id="calc-menu-mean"
_="on click _="on click
set original_value to my innerText set original_value to my innerText
@@ -175,12 +175,12 @@
</div> </div>
</li> </li>
<li> <li>
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Max" %} {% trans "Max" %}
</div> </div>
<a class="tw:px-3 tw:cursor-pointer" <a class="px-3 cursor-pointer"
id="calc-menu-max" id="calc-menu-max"
_="on click _="on click
set original_value to my innerText set original_value to my innerText
@@ -195,12 +195,12 @@
</div> </div>
</li> </li>
<li> <li>
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Min" %} {% trans "Min" %}
</div> </div>
<a class="tw:px-3 tw:cursor-pointer" <a class="px-3 cursor-pointer"
id="calc-menu-min" id="calc-menu-min"
_="on click _="on click
set original_value to my innerText set original_value to my innerText
@@ -215,12 +215,12 @@
</div> </div>
</li> </li>
<li> <li>
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Count" %} {% trans "Count" %}
</div> </div>
<a class="tw:px-3 tw:cursor-pointer" <a class="px-3 cursor-pointer"
id="calc-menu-count" id="calc-menu-count"
_="on click _="on click
set original_value to my innerText set original_value to my innerText
+2 -2
View File
@@ -1,7 +1,7 @@
{% spaceless %} {% spaceless %}
{% load i18n %} {% load i18n %}
<div class="tw:tooltip" data-tip="{{ content }}"> <div class="tooltip" data-tip="{{ content }}">
<span class="tw:text-xs tw:text-base-content/50 tw:mx-3"> <span class="text-xs text-base-content/50 mx-3">
<i class="{% if not icon %}fa-solid fa-circle-question{% else %}{{ icon }}{% endif %} fa-fw"></i> <i class="{% if not icon %}fa-solid fa-circle-question{% else %}{{ icon }}{% endif %} fa-fw"></i>
</span> </span>
</div> </div>
+5 -5
View File
@@ -1,9 +1,9 @@
<div class="tw:card tw:relative tw:h-full tw:shadow tw:bg-base-300"> <div class="card card-border border-2 border-base-300 relative h-full shadow bg-base-100">
<div class="tw:absolute tw:h-8 tw:w-8 tw:right-2 tw:top-2 tw:bg-{{ color }}-300 tw:text-{{ color }}-800 tw:text-center tw:flex tw:items-center tw:justify-center tw:rounded-lg"> <div class="absolute h-8 w-8 right-2 top-2 bg-{{ color }}-300 text-{{ color }}-800 text-center flex items-center justify-center rounded-lg">
{% if icon %}<i class="{{ icon }}"></i>{% else %}<span class="tw:font-bold">{{ title.0 }}</span>{% endif %} {% if icon %}<i class="{{ icon }}"></i>{% else %}<span class="font-bold">{{ title.0 }}</span>{% endif %}
</div> </div>
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:text-{{ color }}-400 tw:font-bold tw:mr-[50px] {{ title_css_classes }}" {{ attrs }}>{{ title }}{% if help_text %}<c-ui.help-icon :content="help_text" icon=""></c-ui.help-icon>{% endif %}</h5> <h5 class="font-bold mr-[50px] text-xl {{ title_css_classes }}" {{ attrs }}>{{ title }}{% if help_text %}<c-ui.help-icon :content="help_text" icon=""></c-ui.help-icon>{% endif %}</h5>
{{ slot }} {{ slot }}
</div> </div>
</div> </div>
@@ -1,35 +1,57 @@
{% load i18n %} {% load i18n %}
<div class="tw:flex tw:flex-col"> <div class="flex w-full h-4 rounded-full bg-white"
<div class="tw:flex tw:relative" role="progressbar" aria-label="{% trans 'Projected Income' %} ({{ percentage.percentages.income_projected|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.income_projected|floatformat:"2u" }}%"> role="group"
<div class="tw:h-6 tw:bg-green-300 tw:bg-striped" aria-label="{% trans 'Income and Expense Percentages' %}">
data-bs-toggle="tooltip"
data-bs-placement="top" <!-- Segment 1: Projected Income -->
title="{% trans 'Projected Income' %} ({{ percentage.percentages.income_projected|floatformat:2 }}%)" <div class="flex items-center justify-center h-4 bg-success/60 tooltip tooltip-bottom tooltip-success
style="width: 100%"> {% if percentage.percentages.income_projected > 0 %}rounded-s-full{% endif %}
{% if percentage.percentages.income_projected > 0 and percentage.percentages.income_current <= 0 and percentage.percentages.expense_projected <= 0 and percentage.percentages.expense_current <= 0 %}rounded-e-full{% endif %}"
style="width: {{ percentage.percentages.income_projected|floatformat:'2u' }}%"
data-tip="{% trans 'Projected Income' %} ({{ percentage.percentages.income_projected|floatformat:2 }}%)"
role="progressbar"
aria-label="{% trans 'Projected Income' %} ({{ percentage.percentages.income_projected|floatformat:2 }}%)"
aria-valuenow="{{ percentage.percentages.income_projected|floatformat:0 }}"
aria-valuemin="0"
aria-valuemax="100">
</div> </div>
</div>
<div class="tw:flex tw:relative" role="progressbar" aria-label="{% trans 'Current Income' %} ({{ percentage.percentages.income_current|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.income_current|floatformat:"2u" }}%"> <!-- Segment 2: Current Income -->
<div class="tw:h-6 tw:bg-green-400" <div class="flex items-center justify-center h-4 bg-success tooltip tooltip-bottom tooltip-success
data-bs-toggle="tooltip" {% if percentage.percentages.income_projected <= 0 and percentage.percentages.income_current > 0 %}rounded-s-full{% endif %}
data-bs-placement="top" {% if percentage.percentages.income_current > 0 and percentage.percentages.expense_projected <= 0 and percentage.percentages.expense_current <= 0 %}rounded-e-full{% endif %}"
title="{% trans 'Current Income' %} ({{ p.percentages.income_current|floatformat:2 }}%)" style="width: {{ percentage.percentages.income_current|floatformat:'2u' }}%"
style="width: 100%"> data-tip="{% trans 'Current Income' %} ({{ percentage.percentages.income_current|floatformat:2 }}%)"
role="progressbar"
aria-label="{% trans 'Current Income' %} ({{ percentage.percentages.income_current|floatformat:2 }}%)"
aria-valuenow="{{ percentage.percentages.income_current|floatformat:0 }}"
aria-valuemin="0"
aria-valuemax="100">
</div> </div>
</div>
<div class="tw:flex tw:relative" role="progressbar" aria-label="{% trans 'Projected Expenses' %} ({{ percentage.percentages.expense_projected|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.expense_projected|floatformat:"2u" }}%"> <!-- Segment 3: Projected Expenses -->
<div class="tw:h-6 tw:bg-red-300 tw:bg-striped" <div class="flex items-center justify-center h-4 bg-error/60 tooltip tooltip-bottom tooltip-error
data-bs-toggle="tooltip" {% if percentage.percentages.income_projected <= 0 and percentage.percentages.income_current <= 0 and percentage.percentages.expense_projected > 0 %}rounded-s-full{% endif %}
data-bs-placement="top" {% if percentage.percentages.expense_projected > 0 and percentage.percentages.expense_current <= 0 %}rounded-e-full{% endif %}"
title="{% trans 'Projected Expenses' %} ({{ percentage.percentages.expense_projected|floatformat:2 }}%)" style="width: {{ percentage.percentages.expense_projected|floatformat:'2u' }}%"
style="width: 100%"> data-tip="{% trans 'Projected Expenses' %} ({{ percentage.percentages.expense_projected|floatformat:2 }}%)"
role="progressbar"
aria-label="{% trans 'Projected Expenses' %} ({{ percentage.percentages.expense_projected|floatformat:2 }}%)"
aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}"
aria-valuemin="0"
aria-valuemax="100">
</div> </div>
</div>
<div class="tw:flex tw:relative" role="progressbar" aria-label="{% trans 'Current Expenses' %} ({{ percentage.percentages.expense_current|floatformat:2 }}%)" aria-valuenow="{{ percentage.percentages.expense_projected|floatformat:0 }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage.percentages.expense_current|floatformat:"2u" }}%"> <!-- Segment 4: Current Expenses -->
<div class="tw:h-6 tw:bg-red-400" <div class="flex items-center justify-center h-4 bg-error tooltip tooltip-bottom tooltip-error
data-bs-toggle="tooltip" {% if percentage.percentages.income_projected <= 0 and percentage.percentages.income_current <= 0 and percentage.percentages.expense_projected <= 0 and percentage.percentages.expense_current > 0 %}rounded-s-full{% endif %}
data-bs-placement="top" {% if percentage.percentages.expense_current > 0 %}rounded-e-full{% endif %}"
title="{% trans 'Current Expenses' %} ({{ percentage.percentages.expense_current|floatformat:2 }}%)" style="width: {{ percentage.percentages.expense_current|floatformat:'2u' }}%"
style="width: 100%"> data-tip="{% trans 'Current Expenses' %} ({{ percentage.percentages.expense_current|floatformat:2 }}%)"
role="progressbar"
aria-label="{% trans 'Current Expenses' %} ({{ percentage.percentages.expense_current|floatformat:2 }}%)"
aria-valuenow="{{ percentage.percentages.expense_current|floatformat:0 }}"
aria-valuemin="0"
aria-valuemax="100">
</div> </div>
</div> </div>
</div>
@@ -1,49 +1,49 @@
{% load i18n %} {% load i18n %}
<div class="tw:grid tw:gap-2 tw:grid-cols-1 tw:xl:flex tw:xl:justify-end"> <div class="grid gap-2 grid-cols-1 xl:flex xl:justify-end">
<div class="tw:grid tw:gap-2 tw:xl:flex tw:flex-wrap tw:xl:justify-center"> <div class="grid gap-2 xl:flex flex-wrap xl:justify-center">
<button class="tw:btn tw:btn-sm tw:btn-outline tw:btn-success" <button class="btn btn-sm btn-outline btn-success"
hx-get="{% url 'transaction_add' %}" hx-get="{% url 'transaction_add' %}"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-trigger="click, add_income from:window" hx-trigger="click, add_income from:window"
hx-vals='{"year": {{ year }}, {% if month %}"month": {{ month }},{% endif %} "type": "IN"}'> hx-vals='{"year": {{ year }}, {% if month %}"month": {{ month }},{% endif %} "type": "IN"}'>
<i class="fa-solid fa-arrow-right-to-bracket tw:me-2"></i> <i class="fa-solid fa-arrow-right-to-bracket me-2"></i>
{% translate "Income" %} {% translate "Income" %}
</button> </button>
<button class="tw:btn tw:btn-sm tw:btn-outline tw:btn-error" <button class="btn btn-sm btn-outline btn-error"
hx-get="{% url 'transaction_add' %}" hx-get="{% url 'transaction_add' %}"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-trigger="click, add_expense from:window" hx-trigger="click, add_expense from:window"
hx-vals='{"year": {{ year }}, {% if month %}"month": {{ month }},{% endif %} "type": "EX"}'> hx-vals='{"year": {{ year }}, {% if month %}"month": {{ month }},{% endif %} "type": "EX"}'>
<i class="fa-solid fa-arrow-right-from-bracket tw:me-2"></i> <i class="fa-solid fa-arrow-right-from-bracket me-2"></i>
{% translate "Expense" %} {% translate "Expense" %}
</button> </button>
<button class="tw:btn tw:btn-sm tw:btn-outline tw:btn-warning" <button class="btn btn-sm btn-outline btn-warning"
hx-get="{% url 'installment_plan_add' %}" hx-get="{% url 'installment_plan_add' %}"
hx-trigger="click, installment from:window" hx-trigger="click, installment from:window"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-divide tw:me-2"></i> <i class="fa-solid fa-divide me-2"></i>
{% translate "Installment" %} {% translate "Installment" %}
</button> </button>
<button class="tw:btn tw:btn-sm tw:btn-outline tw:btn-warning" <button class="btn btn-sm btn-outline btn-warning"
hx-get="{% url 'recurring_transaction_add' %}" hx-get="{% url 'recurring_transaction_add' %}"
hx-trigger="click, balance from:window" hx-trigger="click, balance from:window"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-repeat tw:me-2"></i> <i class="fa-solid fa-repeat me-2"></i>
{% translate "Recurring" %} {% translate "Recurring" %}
</button> </button>
<button class="tw:btn tw:btn-sm tw:btn-outline tw:btn-info" <button class="btn btn-sm btn-outline btn-info"
hx-get="{% url 'transactions_transfer' %}" hx-get="{% url 'transactions_transfer' %}"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-trigger="click, add_transfer from:window" hx-trigger="click, add_transfer from:window"
hx-vals='{"year": {{ year }} {% if month %}, "month": {{ month }}{% endif %}}'> hx-vals='{"year": {{ year }} {% if month %}, "month": {{ month }}{% endif %}}'>
<i class="fa-solid fa-money-bill-transfer tw:me-2"></i> <i class="fa-solid fa-money-bill-transfer me-2"></i>
{% translate "Transfer" %} {% translate "Transfer" %}
</button> </button>
<button class="tw:btn tw:btn-sm tw:btn-outline tw:btn-info" <button class="btn btn-sm btn-outline btn-info"
hx-get="{% url 'account_reconciliation' %}" hx-get="{% url 'account_reconciliation' %}"
hx-trigger="click, balance from:window" hx-trigger="click, balance from:window"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-scale-balanced tw:me-2"></i> <i class="fa-solid fa-scale-balanced me-2"></i>
{% translate "Balance" %} {% translate "Balance" %}
</button> </button>
</div> </div>
@@ -1,50 +1,50 @@
{% load i18n %} {% load i18n %}
<div class="tw:sticky tw:bottom-4 tw:left-0 tw:right-0 tw:z-50 tw:hidden tw:mx-auto tw:w-fit" id="actions-bar" <div class="sticky bottom-4 left-0 right-0 z-50 hidden mx-auto w-fit" id="actions-bar"
_="on change from #transactions-list or htmx:afterSettle from window _="on change from #transactions-list or htmx:afterSettle from window
if #actions-bar then if #actions-bar then
if no <input[type='checkbox']:checked/> in #transactions-list if no <input[type='checkbox']:checked/> in #transactions-list
if #actions-bar if #actions-bar
add .slide-in-bottom-reverse then settle add .slide-in-bottom-reverse then settle
then add .tw:hidden to #actions-bar then add .hidden to #actions-bar
then remove .slide-in-bottom-reverse then remove .slide-in-bottom-reverse
end end
else else
if #actions-bar if #actions-bar
set #selected-count's innerHTML to length of <input[type='checkbox']:checked/> in #transactions-list set #selected-count's innerHTML to length of <input[type='checkbox']:checked/> in #transactions-list
then remove .tw:hidden from #actions-bar then remove .hidden from #actions-bar
then trigger selected_transactions_updated then trigger selected_transactions_updated
end end
end end
end end
end"> end">
<div class="tw:card tw:bg-base-300 tw:shadow slide-in-bottom tw:max-w-[90vw] tw:card-border"> <div class="card bg-base-300 shadow slide-in-bottom max-w-[90vw] card-border">
<div class="tw:card-body tw:flex-row tw:p-2 tw:flex tw:justify-between tw:items-center tw:gap-3 tw:overflow-x-auto"> <div class="card-body flex-row p-2 flex justify-between items-center gap-3 overflow-x-auto">
{% spaceless %} {% spaceless %}
<div class="tw:font-bold tw:text-md tw:ms-2" id="selected-count">0</div> <div class="font-bold text-md ms-2" id="selected-count">0</div>
<div class="tw:divider tw:divider-horizontal tw:m-0"></div> <div class="divider divider-horizontal m-0"></div>
<div class="tw:dropdown tw:dropdown-top tw:dropdown-end"> <div class="dropdown dropdown-top dropdown-end">
<button tabindex="0" role="button" class="tw:btn tw:btn-secondary tw:btn-sm" type="button"> <button tabindex="0" role="button" class="btn btn-secondary btn-sm" type="button">
<i class="fa-regular fa-square-check fa-fw"></i> <i class="fa-regular fa-square-check fa-fw"></i>
<i class="fa-solid fa-chevron-down fa-xs"></i> <i class="fa-solid fa-chevron-down fa-xs"></i>
</button> </button>
<ul tabindex="0" class="tw:dropdown-content tw:menu tw:bg-base-300 tw:rounded-box tw:z-[1] tw:w-full tw:p-2 tw:shadow tw:fixed!"> <ul tabindex="0" class="dropdown-content menu bg-base-300 rounded-box z-[1] w-full p-2 shadow fixed!">
<li> <li>
<a class="tw:cursor-pointer" <a class="cursor-pointer"
_="on click set <#transactions-list .transaction:not([style*='display: none']) input[type='checkbox']/>'s checked to true then call me.blur() then trigger change"> _="on click set <#transactions-list .transaction:not([style*='display: none']) input[type='checkbox']/>'s checked to true then call me.blur() then trigger change">
<i class="fa-regular fa-square-check tw:text-green-400 tw:me-3"></i>{% translate 'Select All' %} <i class="fa-regular fa-square-check text-green-400 me-3"></i>{% translate 'Select All' %}
</a> </a>
</li> </li>
<li> <li>
<a class="tw:cursor-pointer" <a class="cursor-pointer"
_="on click set <#transactions-list input[type='checkbox']/>'s checked to false then call me.blur() then trigger change"> _="on click set <#transactions-list input[type='checkbox']/>'s checked to false then call me.blur() then trigger change">
<i class="fa-regular fa-square tw:text-red-400 tw:me-3"></i>{% translate 'Unselect All' %} <i class="fa-regular fa-square text-red-400 me-3"></i>{% translate 'Unselect All' %}
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="tw:divider tw:divider-horizontal tw:m-0"></div> <div class="divider divider-horizontal m-0"></div>
<div class="tw:join"> <div class="join">
<button class="tw:btn tw:btn-secondary tw:join-item tw:btn-sm" <button class="btn btn-secondary join-item btn-sm"
hx-get="{% url 'transactions_bulk_edit' %}" hx-get="{% url 'transactions_bulk_edit' %}"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-include=".transaction" hx-include=".transaction"
@@ -52,37 +52,37 @@
data-bs-title="{% translate 'Edit' %}"> data-bs-title="{% translate 'Edit' %}">
<i class="fa-solid fa-pencil"></i> <i class="fa-solid fa-pencil"></i>
</button> </button>
<div class="tw:dropdown tw:dropdown-top tw:dropdown-end"> <div class="dropdown dropdown-top dropdown-end">
<button type="button" tabindex="0" role="button" class="tw:join-item tw:btn tw:btn-sm tw:btn-secondary"> <button type="button" tabindex="0" role="button" class="join-item btn btn-sm btn-secondary">
<i class="fa-solid fa-chevron-down fa-xs"></i> <i class="fa-solid fa-chevron-down fa-xs"></i>
</button> </button>
<ul tabindex="0" class="tw:dropdown-content tw:fixed! tw:menu tw:bg-base-300 tw:rounded-box tw:z-[1] tw:w-full tw:p-2 tw:shadow"> <ul tabindex="0" class="dropdown-content fixed! menu bg-base-300 rounded-box z-[1] w-full p-2 shadow">
<li> <li>
<a class="tw:cursor-pointer" <a class="cursor-pointer"
hx-get="{% url 'transactions_bulk_unpay' %}" hx-get="{% url 'transactions_bulk_unpay' %}"
hx-include=".transaction"> hx-include=".transaction">
<i class="fa-regular fa-circle tw:text-red-400 fa-fw tw:me-3"></i>{% translate 'Mark as unpaid' %} <i class="fa-regular fa-circle text-red-400 fa-fw me-3"></i>{% translate 'Mark as unpaid' %}
</a> </a>
</li> </li>
<li> <li>
<a class="tw:cursor-pointer" <a class="cursor-pointer"
hx-get="{% url 'transactions_bulk_pay' %}" hx-get="{% url 'transactions_bulk_pay' %}"
hx-include=".transaction"> hx-include=".transaction">
<i class="fa-regular fa-circle-check tw:text-green-400 fa-fw tw:me-3"></i>{% translate 'Mark as paid' %} <i class="fa-regular fa-circle-check text-green-400 fa-fw me-3"></i>{% translate 'Mark as paid' %}
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
<button class="tw:btn tw:btn-secondary tw:btn-sm" <button class="btn btn-secondary btn-sm"
hx-get="{% url 'transactions_bulk_clone' %}" hx-get="{% url 'transactions_bulk_clone' %}"
hx-include=".transaction" hx-include=".transaction"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate 'Duplicate' %}"> data-bs-title="{% translate 'Duplicate' %}">
<i class="fa-solid fa-clone fa-fw"></i> <i class="fa-solid fa-clone fa-fw"></i>
</button> </button>
<button class="tw:btn tw:btn-secondary tw:btn-sm" <button class="btn btn-secondary btn-sm"
hx-get="{% url 'transactions_bulk_delete' %}" hx-get="{% url 'transactions_bulk_delete' %}"
hx-include=".transaction" hx-include=".transaction"
hx-trigger="confirmed" hx-trigger="confirmed"
@@ -93,10 +93,10 @@
data-text="{% translate "You won't be able to revert this!" %}" data-text="{% translate "You won't be able to revert this!" %}"
data-confirm-text="{% translate "Yes, delete them!" %}" data-confirm-text="{% translate "Yes, delete them!" %}"
_="install prompt_swal"> _="install prompt_swal">
<i class="fa-solid fa-trash tw:text-error"></i> <i class="fa-solid fa-trash text-error"></i>
</button> </button>
<div class="tw:divider tw:divider-horizontal tw:m-0"></div> <div class="divider divider-horizontal m-0"></div>
<div class="tw:join" <div class="join"
_="on selected_transactions_updated from #actions-bar _="on selected_transactions_updated from #actions-bar
set realTotal to math.bignumber(0) set realTotal to math.bignumber(0)
set flatTotal to math.bignumber(0) set flatTotal to math.bignumber(0)
@@ -134,7 +134,7 @@
put mean.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-mean's innerText put mean.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-mean's innerText
put flatAmountValues.length.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-count's innerText put flatAmountValues.length.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40}) into #calc-menu-count's innerText
end"> end">
<button class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <button class="btn btn-secondary btn-sm join-item"
_="on click _="on click
set original_value to #real-total-front's innerText set original_value to #real-total-front's innerText
writeText(original_value) on navigator.clipboard writeText(original_value) on navigator.clipboard
@@ -142,16 +142,16 @@
wait 1s wait 1s
put original_value into #real-total-front's innerText put original_value into #real-total-front's innerText
end"> end">
<i class="fa-solid fa-plus fa-fw tw:me-md-2 tw:text-primary"></i> <i class="fa-solid fa-plus fa-fw me-md-2 text-primary"></i>
<span class="tw:hidden tw:md:inline-block" id="real-total-front">0</span> <span class="hidden md:inline-block" id="real-total-front">0</span>
</button> </button>
<div class="tw:dropdown tw:dropdown-end tw:dropdown-top"> <div class="dropdown dropdown-end dropdown-top">
<button type="button" tabindex="0" role="button" class="tw:join-item tw:btn tw:btn-sm tw:btn-secondary"> <button type="button" tabindex="0" role="button" class="join-item btn btn-sm btn-secondary">
<i class="fa-solid fa-chevron-down fa-xs"></i> <i class="fa-solid fa-chevron-down fa-xs"></i>
</button> </button>
<ul tabindex="0" class="tw:dropdown-content tw:menu tw:bg-base-300 tw:rounded-box tw:z-[1] tw:w-full tw:shadow tw:fixed!"> <ul tabindex="0" class="dropdown-content menu bg-base-300 rounded-box z-[1] w-full shadow fixed!">
<li class="tw:cursor-pointer" <li class="cursor-pointer"
_="on click _="on click
set original_value to #calc-menu-flat-total's innerText set original_value to #calc-menu-flat-total's innerText
writeText(original_value) on navigator.clipboard writeText(original_value) on navigator.clipboard
@@ -159,19 +159,19 @@
wait 1s wait 1s
put original_value into #calc-menu-flat-total put original_value into #calc-menu-flat-total
end"> end">
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Flat Total" %} {% trans "Flat Total" %}
</div> </div>
<div class="tw:px-3" <div class="px-3"
id="calc-menu-flat-total"> id="calc-menu-flat-total">
0 0
</div> </div>
</div> </div>
</div> </div>
</li> </li>
<li class="tw:cursor-pointer" <li class="cursor-pointer"
_="on click _="on click
set original_value to #calc-menu-real-total's innerText set original_value to #calc-menu-real-total's innerText
writeText(original_value) on navigator.clipboard writeText(original_value) on navigator.clipboard
@@ -179,19 +179,19 @@
wait 1s wait 1s
put original_value into #calc-menu-real-total put original_value into #calc-menu-real-total
end"> end">
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Real Total" %} {% trans "Real Total" %}
</div> </div>
<div class="tw:px-3" <div class="px-3"
id="calc-menu-real-total"> id="calc-menu-real-total">
0 0
</div> </div>
</div> </div>
</div> </div>
</li> </li>
<li class="tw:cursor-pointer" <li class="cursor-pointer"
_="on click _="on click
set original_value to #calc-menu-mean's innerText set original_value to #calc-menu-mean's innerText
writeText(original_value) on navigator.clipboard writeText(original_value) on navigator.clipboard
@@ -199,19 +199,19 @@
wait 1s wait 1s
put original_value into #calc-menu-mean put original_value into #calc-menu-mean
end"> end">
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Mean" %} {% trans "Mean" %}
</div> </div>
<div class="tw:px-3" <div class="px-3"
id="calc-menu-mean"> id="calc-menu-mean">
0 0
</div> </div>
</div> </div>
</div> </div>
</li> </li>
<li class="tw:cursor-pointer" <li class="cursor-pointer"
_="on click _="on click
set original_value to #calc-menu-max's innerText set original_value to #calc-menu-max's innerText
writeText(original_value) on navigator.clipboard writeText(original_value) on navigator.clipboard
@@ -219,19 +219,19 @@
wait 1s wait 1s
put original_value into #calc-menu-max put original_value into #calc-menu-max
end"> end">
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Max" %} {% trans "Max" %}
</div> </div>
<div class="tw:px-3" <div class="px-3"
id="calc-menu-max"> id="calc-menu-max">
0 0
</div> </div>
</div> </div>
</div> </div>
</li> </li>
<li class="tw:cursor-pointer" <li class="cursor-pointer"
_="on click _="on click
set original_value to #calc-menu-min's innerText set original_value to #calc-menu-min's innerText
writeText(original_value) on navigator.clipboard writeText(original_value) on navigator.clipboard
@@ -239,19 +239,19 @@
wait 1s wait 1s
put original_value into #calc-menu-min put original_value into #calc-menu-min
end"> end">
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Min" %} {% trans "Min" %}
</div> </div>
<div class="tw:px-3" <div class="px-3"
id="calc-menu-min"> id="calc-menu-min">
0 0
</div> </div>
</div> </div>
</div> </div>
</li> </li>
<li class="tw:cursor-pointer" <li class="cursor-pointer"
_="on click _="on click
set original_value to #calc-menu-count's innerText set original_value to #calc-menu-count's innerText
writeText(original_value) on navigator.clipboard writeText(original_value) on navigator.clipboard
@@ -259,12 +259,12 @@
wait 1s wait 1s
put original_value into #calc-menu-count put original_value into #calc-menu-count
end"> end">
<div class="tw:p-0"> <div class="p-0">
<div> <div>
<div class="tw:text-base-content/60 tw:text-xs tw:font-medium tw:px-3"> <div class="text-base-content/60 text-xs font-medium px-3">
{% trans "Count" %} {% trans "Count" %}
</div> </div>
<div class="tw:px-3" <div class="px-3"
id="calc-menu-count"> id="calc-menu-count">
0 0
</div> </div>
@@ -1,9 +1,9 @@
<div class="tw:collapse tw:collapse-arrow tw:bg-base-100 tw:border tw:border-base-300{% if div.css_class %} {{div.css_class}}{% endif %}"> <div class="collapse collapse-arrow bg-base-100 border border-base-300{% if div.css_class %} {{div.css_class}}{% endif %}">
<input type="radio" name="{{ div.data_parent }}" {% if div.active %}checked="checked"{% endif %} /> <input type="radio" name="{{ div.data_parent }}" {% if div.active %}checked="checked"{% endif %} />
<div class="tw:collapse-title tw:font-semibold"> <div class="collapse-title font-semibold">
{{ div.name }} {{ div.name }}
</div> </div>
<div class="tw:collapse-content"> <div class="collapse-content">
{{ fields|safe }} {{ fields|safe }}
</div> </div>
</div> </div>
+1 -1
View File
@@ -1,3 +1,3 @@
<div class="tw:join tw:join-vertical tw:w-full{% if accordion.css_class %} {{accordion.css_class}}{% endif %}" id="{{ accordion.css_id }}"> <div class="join join-vertical w-full{% if accordion.css_class %} {{accordion.css_class}}{% endif %}" id="{{ accordion.css_id }}">
{{ content|safe }} {{ content|safe }}
</div> </div>
+3 -3
View File
@@ -1,11 +1,11 @@
{% for fieldset in form.fieldsets %} {% for fieldset in form.fieldsets %}
<fieldset class="tw:fieldset fieldset-{{ forloop.counter }} {{ fieldset.classes }}"> <fieldset class="fieldset fieldset-{{ forloop.counter }} {{ fieldset.classes }}">
{% if fieldset.legend %} {% if fieldset.legend %}
<legend class="tw:fieldset-legend">{{ fieldset.legend }}</legend> <legend class="fieldset-legend">{{ fieldset.legend }}</legend>
{% endif %} {% endif %}
{% if fieldset.description %} {% if fieldset.description %}
<p class="tw:text-sm tw:text-base-content/60">{{ fieldset.description }}</p> <p class="text-sm text-base-content/60">{{ fieldset.description }}</p>
{% endif %} {% endif %}
{% for field in fieldset %} {% for field in fieldset %}
+3 -3
View File
@@ -1,7 +1,7 @@
{% if form.non_field_errors %} {% if form.non_field_errors %}
<div class="tw:alert tw:alert-error"> <div class="alert alert-error">
{% if form_error_title %}<h4 class="tw:font-bold">{{ form_error_title }}</h4>{% endif %} {% if form_error_title %}<h4 class="font-bold">{{ form_error_title }}</h4>{% endif %}
<ul class="tw:m-0 tw:list-disc tw:list-inside"> <ul class="m-0 list-disc list-inside">
{{ form.non_field_errors|unordered_list }} {{ form.non_field_errors|unordered_list }}
</ul> </ul>
</div> </div>
@@ -1,7 +1,7 @@
{% if formset.non_form_errors %} {% if formset.non_form_errors %}
<div class="tw:alert tw:alert-error"> <div class="alert alert-error">
{% if formset_error_title %}<h4 class="tw:font-bold">{{ formset_error_title }}</h4>{% endif %} {% if formset_error_title %}<h4 class="font-bold">{{ formset_error_title }}</h4>{% endif %}
<ul class="tw:m-0 tw:list-disc tw:list-inside"> <ul class="m-0 list-disc list-inside">
{{ formset.non_form_errors|unordered_list }} {{ formset.non_form_errors|unordered_list }}
</ul> </ul>
</div> </div>
+14 -14
View File
@@ -4,16 +4,16 @@
{{ field }} {{ field }}
{% else %} {% else %}
{% if field|is_checkbox and tag != "td" %} {% if field|is_checkbox and tag != "td" %}
<div class="tw:mb-3"> <div class="">
{% if label_class %} {% if label_class %}
<div class="{{ field_class }}"> <div class="{{ field_class }}">
{% endif %} {% endif %}
{% endif %} {% endif %}
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="{% if field|is_checkbox and form_show_labels %}tw:form-control{% else %}tw:mb-3{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> <{% if tag %}{{ tag }}{% else %}fieldset{% endif %} id="div_{{ field.auto_id }}" class="{% if field|is_checkbox and form_show_labels %}form-control{% else %}fieldset{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.label and not field|is_checkbox and form_show_labels %} {% if field.label and not field|is_checkbox and form_show_labels %}
{% if field.use_fieldset %}<fieldset class="tw:fieldset"{% if field.aria_describedby %} aria-describedby="{{ field.aria_describedby }}"{% endif %}>{% endif %} {% if field.use_fieldset %}<fieldset class="fieldset"{% if field.aria_describedby %} aria-describedby="{{ field.aria_describedby }}"{% endif %}>{% endif %}
<{% if field.use_fieldset %}legend{% else %}label{% endif %} <{% if field.use_fieldset %}legend{% else %}label{% endif %}
{% if field.id_for_label %}for="{{ field.id_for_label }}"{% endif %} class="tw:label{% if label_class %} {{ label_class }}{% endif %}{% if field.field.required %} requiredField{% endif %}"> {% if field.id_for_label %}for="{{ field.id_for_label }}"{% endif %} class="fieldset-legend{% if label_class %} {{ label_class }}{% endif %}{% if field.field.required %} requiredField{% endif %}">
{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %} {{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</{% if field.use_fieldset %}legend{% else %}label{% endif %}> </{% if field.use_fieldset %}legend{% else %}label{% endif %}>
{% endif %} {% endif %}
@@ -25,11 +25,11 @@
{% if not field|is_checkboxselectmultiple and not field|is_radioselect %} {% if not field|is_checkboxselectmultiple and not field|is_radioselect %}
{% if field|is_checkbox and form_show_labels %} {% if field|is_checkbox and form_show_labels %}
{% if field.errors %} {% if field.errors %}
{% crispy_field field 'class' 'tw:checkbox tw:checkbox-error' %} {% crispy_field field 'class' 'checkbox checkbox-error' %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:checkbox' %} {% crispy_field field 'class' 'checkbox' %}
{% endif %} {% endif %}
<label for="{{ field.id_for_label }}" class="tw:label{% if field.field.required %} requiredField{% endif %}"> <label for="{{ field.id_for_label }}" class="label{% if field.field.required %} requiredField{% endif %}">
{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %} {{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</label> </label>
{% include 'crispy-daisyui/layout/help_text_and_errors.html' %} {% include 'crispy-daisyui/layout/help_text_and_errors.html' %}
@@ -39,20 +39,20 @@
{% include 'crispy-daisyui/layout/field_file.html' %} {% include 'crispy-daisyui/layout/field_file.html' %}
{% elif field|is_select %} {% elif field|is_select %}
{% if field.errors %} {% if field.errors %}
{% crispy_field field 'class' 'tw:select tw:select-bordered tw:select-error tw:w-full' %} {% crispy_field field 'class' 'select-error input-error w-full' %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:select tw:select-bordered tw:w-full' %} {% crispy_field field 'class' '' %}
{% endif %} {% endif %}
{% elif field|is_checkbox %} {% elif field|is_checkbox %}
{% if field.errors %} {% if field.errors %}
{% crispy_field field 'class' 'tw:checkbox tw:checkbox-error' %} {% crispy_field field 'class' 'checkbox checkbox-error w-full' %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:checkbox' %} {% crispy_field field 'class' 'checkbox w-full' %}
{% endif %} {% endif %}
{% elif field.errors %} {% elif field.errors %}
{% crispy_field field 'class' 'tw:input tw:input-bordered tw:input-error tw:w-full' %} {% crispy_field field 'class' 'input input-error w-full' %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:input tw:input-bordered tw:w-full' %} {% crispy_field field 'class' 'input w-full' %}
{% endif %} {% endif %}
{% if not field|is_file %} {% if not field|is_file %}
{% include 'crispy-daisyui/layout/help_text_and_errors.html' %} {% include 'crispy-daisyui/layout/help_text_and_errors.html' %}
@@ -61,7 +61,7 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if field.use_fieldset and field.label and form_show_labels %}</fieldset>{% endif %} {% if field.use_fieldset and field.label and form_show_labels %}</fieldset>{% endif %}
</{% if tag %}{{ tag }}{% else %}div{% endif %}> </{% if tag %}{{ tag }}{% else %}fieldset{% endif %}>
{% if field|is_checkbox and tag != "td" %} {% if field|is_checkbox and tag != "td" %}
{% if label_class %} {% if label_class %}
</div> </div>
+1 -1
View File
@@ -1,5 +1,5 @@
{% if inputs %} {% if inputs %}
<div class="tw:mb-3"> <div class="">
{% if label_class %} {% if label_class %}
<div class="aab {{ label_class }}"></div> <div class="aab {{ label_class }}"></div>
{% endif %} {% endif %}
@@ -1,4 +1,4 @@
<div class="tw:alert {{ alert.css_class }}" role="alert"{% if alert.css_id %} id="{{ alert.css_id }}"{% endif %}> <div class="alert {{ alert.css_class }}" role="alert"{% if alert.css_id %} id="{{ alert.css_id }}"{% endif %}>
{{ content|safe }} {{ content|safe }}
{% if dismiss %}<button type="button" class="tw:btn tw:btn-sm tw:btn-circle tw:btn-ghost" data-bs-dismiss="alert" aria-label="Close"></button>{% endif %} {% if dismiss %}<button type="button" class="btn btn-sm btn-circle btn-ghost" data-bs-dismiss="alert" aria-label="Close"></button>{% endif %}
</div> </div>
@@ -2,7 +2,7 @@
name="{% if input.name|wordcount > 1 %}{{ input.name|slugify }}{% else %}{{ input.name }}{% endif %}" name="{% if input.name|wordcount > 1 %}{{ input.name|slugify }}{% else %}{{ input.name }}{% endif %}"
value="{{ input.value }}" value="{{ input.value }}"
{% if input.input_type != "hidden" %} {% if input.input_type != "hidden" %}
class="tw:btn {{ input.field_classes }}" class="btn {{ input.field_classes }}"
id="{% if input.id %}{{ input.id }}{% else %}{{ input.input_type }}-id-{{ input.name|slugify }}{% endif %}" id="{% if input.id %}{{ input.id }}{% else %}{{ input.input_type }}-id-{{ input.name|slugify }}{% endif %}"
{% endif %} {% endif %}
{{ input.flat_attrs }} {{ input.flat_attrs }}
@@ -1 +1 @@
<button class="tw:btn" {{ button.flat_attrs }}>{{ button.content|safe }}</button> <button class="btn" {{ button.flat_attrs }}>{{ button.content|safe }}</button>
@@ -1,4 +1,4 @@
<div {% if buttonholder.css_id %}id="{{ buttonholder.css_id }}"{% endif %} <div {% if buttonholder.css_id %}id="{{ buttonholder.css_id }}"{% endif %}
class="tw:flex tw:gap-2{% if buttonholder.css_class %} {{ buttonholder.css_class }}{% endif %}"> class="flex gap-2{% if buttonholder.css_class %} {{ buttonholder.css_class }}{% endif %}">
{{ fields_output|safe }} {{ fields_output|safe }}
</div> </div>
@@ -1,11 +1,11 @@
{% if field.is_hidden %} {% if field.is_hidden %}
{{ field }} {{ field }}
{% else %} {% else %}
<div id="div_{{ field.auto_id }}" class="tw:mb-3{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> <div id="div_{{ field.auto_id }}" class="{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.label %} {% if field.label %}
<fieldset class="tw:fieldset"{% if field.aria_describedby %} aria-describedby="{{ field.aria_describedby }}"{% endif %}> <fieldset class="fieldset"{% if field.aria_describedby %} aria-describedby="{{ field.aria_describedby }}"{% endif %}>
<legend for="{{ field.id_for_label }}" class="tw:fieldset-legend {{ label_class }}{% if field.field.required %} requiredField{% endif %}"> <legend for="{{ field.id_for_label }}" class="fieldset-legend {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %} {{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</legend> </legend>
{% endif %} {% endif %}
@@ -1,5 +1,5 @@
<div {% if div.css_id %}id="{{ div.css_id }}"{% endif %} <div {% if div.css_id %}id="{{ div.css_id }}"{% endif %}
class="tw:col-span-12 {{ div.css_class|default:'' }}" {{ div.flat_attrs }}> class="{% if 'col' in div.css_class %}{{ div.css_class|default:'' }}{% else %}md:col-span-6 {{ div.css_class|default:'' }}{% endif %}" {{ div.flat_attrs }}>
{{ fields|safe }} {{ fields|safe }}
</div> </div>
@@ -1,9 +1,9 @@
{% if form_show_errors and field.errors %} {% if form_show_errors and field.errors %}
{% if field.errors.field_id %} {% if field.errors.field_id %}
{# Django 5.2+ #} {# Django 5.2+ #}
<div id="{{field.errors.field_id}}_error" class="tw:text-error tw:text-sm tw:mt-1"> <div id="{{field.errors.field_id}}_error" class="text-error text-sm mt-1">
{% else %} {% else %}
<div id="{{field.auto_id}}_error" class="tw:text-error tw:text-sm tw:mt-1"> <div id="{{field.auto_id}}_error" class="text-error text-sm mt-1">
{% endif %} {% endif %}
{% for error in field.errors %} {% for error in field.errors %}
<span id="error_{{ forloop.counter }}_{{ field.auto_id }}"><strong>{{ error }}</strong></span> <span id="error_{{ forloop.counter }}_{{ field.auto_id }}"><strong>{{ error }}</strong></span>
@@ -1,9 +1,9 @@
{% if form_show_errors and field.errors %} {% if form_show_errors and field.errors %}
{% if field.errors.field_id %} {% if field.errors.field_id %}
{# Django 5.2+ #} {# Django 5.2+ #}
<div id="{{field.errors.field_id}}_error" class="tw:text-error tw:text-sm tw:mt-1"> <div id="{{field.errors.field_id}}_error" class="text-error text-sm mt-1">
{% else %} {% else %}
<div id="{{field.auto_id}}_error" class="tw:text-error tw:text-sm tw:mt-1"> <div id="{{field.auto_id}}_error" class="text-error text-sm mt-1">
{% endif %} {% endif %}
{% for error in field.errors %} {% for error in field.errors %}
<p id="error_{{ forloop.counter }}_{{ field.auto_id }}"><strong>{{ error }}</strong></p> <p id="error_{{ forloop.counter }}_{{ field.auto_id }}"><strong>{{ error }}</strong></p>
@@ -2,25 +2,25 @@
{% for widget in field.subwidgets %} {% for widget in field.subwidgets %}
{% if widget.data.is_initial %} {% if widget.data.is_initial %}
<div class="tw:join tw:mb-2 tw:w-full"> <div class="join mb-2">
<span class="tw:btn tw:btn-disabled tw:join-item">{{ widget.data.initial_text }}</span> <span class="btn btn-disabled join-item">{{ widget.data.initial_text }}</span>
<div class="tw:input tw:input-bordered tw:join-item tw:flex tw:items-center tw:flex-grow"> <div class="input join-item flex items-center flex-grow">
<span class="tw:break-all tw:flex-grow"> <span class="break-all flex-grow">
<a href="{{ field.value.url }}" class="tw:link">{{ field.value.name }}</a> <a href="{{ field.value.url }}" class="link">{{ field.value.name }}</a>
</span> </span>
{% if not widget.data.required %} {% if not widget.data.required %}
<span class="tw:ml-2"> <span class="ml-2">
<label class="tw:label tw:cursor-pointer tw:gap-2"> <label class="label cursor-pointer gap-2">
<input type="checkbox" name="{{ widget.data.checkbox_name }}" id="{{ widget.data.checkbox_id }}" class="tw:checkbox"{% if field.field.disabled %} disabled{% endif %} > <span class="label-text">{{ widget.data.clear_checkbox_label }}</span>
<span class="tw:label-text">{{ widget.data.clear_checkbox_label }}</span> <input type="checkbox" name="{{ widget.data.checkbox_name }}" id="{{ widget.data.checkbox_id }}" class="checkbox"{% if field.field.disabled %} disabled{% endif %}>
</label> </label>
</span> </span>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div{% if field.errors %} class="tw:input-error"{%endif%}> <div class="fieldset">
<input type="{{ widget.data.type }}" name="{{ widget.data.name }}" class="tw:file-input tw:file-input-bordered tw:w-full{% if widget.data.attrs.class %} {{ widget.data.attrs.class }}{% endif %}{% if field.errors %} tw:input-error{%endif%}"{% if field.field.disabled %} disabled{% endif %}{% for name, value in widget.data.attrs.items %}{% if value is not False and name != 'class' %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}> <input type="{{ widget.data.type }}" name="{{ widget.data.name }}" class="file-input {% if widget.data.attrs.class %} {{ widget.data.attrs.class }}{% endif %}{% if field.errors %} file-input-error{% endif %}"{% if field.field.disabled %} disabled{% endif %}{% for name, value in widget.data.attrs.items %}{% if value is not False and name != 'class' %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}{% endif %}{% endfor %}>
{% include 'crispy-daisyui/layout/help_text_and_errors.html' %} {% include 'crispy-daisyui/layout/help_text_and_errors.html' %}
</div> </div>
{% endfor %} {% endfor %}
@@ -1,34 +1,34 @@
{% load crispy_forms_field %} {% load crispy_forms_field %}
<div{% if div.css_id %} id="{{ div.css_id }}"{% endif %} class="tw:mb-3{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}{% if div.css_class %} {{ div.css_class }}{% endif %}" {{ div.flat_attrs }}> <div{% if div.css_id %} id="{{ div.css_id }}"{% endif %} class="{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}{% if div.css_class %} {{ div.css_class }}{% endif %}" {{ div.flat_attrs }}>
{% if field.label and form_show_labels %} {% if field.label and form_show_labels %}
<label for="{{ field.id_for_label }}" class="tw:label {{ label_class }}{% if field.field.required %} requiredField{% endif %}"> <label for="{{ field.id_for_label }}" class="label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
<span class="tw:label-text">{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}</span> <span class="label-text">{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}</span>
</label> </label>
{% endif %} {% endif %}
<div{% if field_class %} class="{{ field_class }}"{% endif %}> <div{% if field_class %} class="{{ field_class }}"{% endif %}>
<div class="tw:join tw:w-full{% if input_size %} {{ input_size }}{% endif %}"> <div class="join {% if input_size %} {{ input_size }}{% endif %}">
{% if field|is_select %} {% if field|is_select %}
{% if field.errors %} {% if field.errors %}
{% crispy_field field 'class' 'tw:select tw:select-bordered tw:select-error tw:join-item tw:flex-grow' %} {% crispy_field field 'class' 'select-error join-item flex-grow' %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:select tw:select-bordered tw:join-item tw:flex-grow' %} {% crispy_field field 'class' 'join-item flex-grow' %}
{% endif %} {% endif %}
{% else %} {% else %}
{% if field.errors %} {% if field.errors %}
{% crispy_field field 'class' 'tw:input tw:input-bordered tw:input-error tw:join-item tw:flex-grow' %} {% crispy_field field 'class' 'input input-bordered input-error join-item flex-grow' %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:input tw:input-bordered tw:join-item tw:flex-grow' %} {% crispy_field field 'class' 'input input-bordered join-item flex-grow' %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{{ buttons|safe }} {{ buttons|safe }}
</div> </div>
{% if field.errors.field_id %} {% if field.errors.field_id %}
{# Django 5.2+ #} {# Django 5.2+ #}
<div id="{{field.errors.field_id}}_error" class="tw:text-error tw:text-sm tw:mt-1"> <div id="{{field.errors.field_id}}_error" class="text-error text-sm mt-1">
{% else %} {% else %}
<div id="{{field.auto_id}}_error" class="tw:text-error tw:text-sm tw:mt-1"> <div id="{{field.auto_id}}_error" class="text-error text-sm mt-1">
{% endif %} {% endif %}
{% for error in field.errors %} {% for error in field.errors %}
<p id="error_{{ forloop.counter }}_{{ field.auto_id }}"><small><strong>{{ error }}</strong></small></p> <p id="error_{{ forloop.counter }}_{{ field.auto_id }}"><small><strong>{{ error }}</strong></small></p>
@@ -1,6 +1,6 @@
<fieldset {% if fieldset.css_id %}id="{{ fieldset.css_id }}"{% endif %} <fieldset {% if fieldset.css_id %}id="{{ fieldset.css_id }}"{% endif %}
class="tw:fieldset{% if fieldset.css_class %} {{ fieldset.css_class }}{% endif %}" {% if fieldset.css_class or form_style %}class="{{ fieldset.css_class }} {{ form_style }}"{% endif %}
{{ fieldset.flat_attrs }}> {{ fieldset.flat_attrs|safe }}>
{% if legend %}<legend class="tw:fieldset-legend">{{ legend|safe }}</legend>{% endif %} {% if legend %}<legend class="block text-gray-700 font-bold mb-2">{{ legend|safe }}</legend>{% endif %}
{{ fields|safe }} {{ fields|safe }}
</fieldset> </fieldset>
@@ -3,23 +3,15 @@
{% if field.is_hidden %} {% if field.is_hidden %}
{{ field }} {{ field }}
{% else %} {% else %}
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="tw:mb-3{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="fieldset{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
<label class="tw:input tw:input-bordered tw:flex tw:items-center tw:gap-2" {% if field.id_for_label %}for="{{ field.id_for_label }}"{% endif %}{% if label_class %} {{ label_class }}{% endif %}> <label class="input input-bordered flex items-center gap-2{% if field.errors %} input-error{% endif %}" {% if field.id_for_label %}for="{{ field.id_for_label }}"{% endif %}>
{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %} <span class="label-text">{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}</span>
{% if field|is_select %} {% if field|is_select %}
{%if field.errors %} {% crispy_field field 'class' 'grow' %}
{% crispy_field field 'class' 'tw:select tw:select-bordered tw:select-error tw:grow' 'placeholder' field.name %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:select tw:select-bordered tw:grow' 'placeholder' field.name %} {% crispy_field field 'class' 'grow' %}
{% endif %} {% endif %}
{% else %}
{% if field.errors %}
{% crispy_field field 'class' 'tw:grow' 'placeholder' field.name %}
{% else %}
{% crispy_field field 'class' 'tw:grow' 'placeholder' field.name %}
{% endif %}
{% endif %}
</label> </label>
{% include 'crispy-daisyui/layout/help_text_and_errors.html' %} {% include 'crispy-daisyui/layout/help_text_and_errors.html' %}
@@ -1,6 +1,6 @@
<div <div
{% if formactions.flat_attrs %}{{ formactions.flat_attrs }}{% endif %} {% if formactions.flat_attrs %}{{ formactions.flat_attrs }}{% endif %}
class="tw:mb-3 tw:flex tw:gap-2 {{ formactions.css_class|default:'' }} {{ field_class }}" class="flex gap-2 {{ formactions.css_class|default:'' }} {{ field_class }}"
{% if formactions.id %} id="{{ formactions.id }}"{% endif %}> {% if formactions.id %} id="{{ formactions.id }}"{% endif %}>
{% if label_class %} {% if label_class %}
<div class="aab {{ label_class }}"></div> <div class="aab {{ label_class }}"></div>
@@ -1,7 +1,7 @@
{% if field.help_text %} {% if field.help_text %}
{% if help_text_inline %} {% if help_text_inline %}
<span id="{{ field.auto_id }}_helptext" class="tw:text-sm tw:text-base-content/60">{{ field.help_text|safe}}</span> <span id="{{ field.auto_id }}_helptext" class="text-sm text-base-content/60">{{ field.help_text|safe}}</span>
{% else %} {% else %}
<div {% if field.auto_id %}id="{{ field.auto_id }}_helptext" {% endif %}class="tw:text-sm tw:text-base-content/60 tw:mt-1">{{ field.help_text|safe }}</div> <div {% if field.auto_id %}id="{{ field.auto_id }}_helptext" {% endif %}class="text-sm text-base-content/60 mt-1">{{ field.help_text|safe }}</div>
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -1,13 +1,13 @@
{% if help_text_inline and not error_text_inline %} {% if help_text_inline and not error_text_inline %}
{% include 'bootstrap5/layout/help_text.html' %} {% include 'crispy-daisyui/layout/help_text.html' %}
{% endif %} {% endif %}
{% if error_text_inline %} {% if error_text_inline %}
{% include 'bootstrap5/layout/field_errors.html' %} {% include 'crispy-daisyui/layout/field_errors.html' %}
{% else %} {% else %}
{% include 'bootstrap5/layout/field_errors_block.html' %} {% include 'crispy-daisyui/layout/field_errors_block.html' %}
{% endif %} {% endif %}
{% if not help_text_inline %} {% if not help_text_inline %}
{% include 'bootstrap5/layout/help_text.html' %} {% include 'crispy-daisyui/layout/help_text.html' %}
{% endif %} {% endif %}
@@ -4,21 +4,21 @@
{{ field }} {{ field }}
{% else %} {% else %}
{% if field|is_checkbox %} {% if field|is_checkbox %}
<div id="div_{{ field.auto_id }}" class="tw:form-control tw:inline-flex{% if wrapper_class %} {{ wrapper_class }}{% endif %}"> <div id="div_{{ field.auto_id }}" class="form-control inline-flex{% if wrapper_class %} {{ wrapper_class }}{% endif %}">
<label class="tw:label tw:cursor-pointer tw:gap-2"> <label class="label cursor-pointer gap-2">
{% crispy_field field 'class' 'tw:checkbox' %} {% crispy_field field 'class' 'checkbox' %}
<span class="tw:label-text">{{ field.label }}</span> <span class="label-text">{{ field.label }}</span>
</label> </label>
</div> </div>
{% else %} {% else %}
<div id="div_{{ field.auto_id }}"{% if wrapper_class %} class="{{ wrapper_class }}"{% endif %}> <div id="div_{{ field.auto_id }}"{% if wrapper_class %} class="{{ wrapper_class }}"{% endif %}>
<label for="{{ field.id_for_label }}" class="tw:sr-only"> <label for="{{ field.id_for_label }}" class="sr-only">
{{ field.label }} {{ field.label }}
</label> </label>
{% if field.errors %} {% if field.errors %}
{% crispy_field field 'class' 'tw:input tw:input-bordered tw:input-error' 'placeholder' field.label %} {% crispy_field field 'class' 'input input-bordered input-error' 'placeholder' field.label %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:input tw:input-bordered' 'placeholder' field.label %} {% crispy_field field 'class' 'input input-bordered' 'placeholder' field.label %}
{% endif %} {% endif %}
</div> </div>
@@ -1,10 +1,10 @@
<div id="{{ modal.css_id }}" class="tw:modal {{ modal.css_class }}" {{ modal.flat_attrs }}> <div id="{{ modal.css_id }}" class="modal {{ modal.css_class }}" {{ modal.flat_attrs }}>
<div class="tw:modal-box" role="document"> <div class="modal-box" role="document">
<form method="dialog"> <form method="dialog">
<button class="tw:btn tw:btn-sm tw:btn-circle tw:btn-ghost tw:absolute tw:right-2 tw:top-2" aria-label="Close"></button> <button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" aria-label="Close"></button>
</form> </form>
<h3 class="tw:font-bold tw:text-lg {{ modal.title_class }}" id="{{ modal.title_id }}-label">{{ modal.title }}</h3> <h3 class="font-bold text-lg {{ modal.title_class }}" id="{{ modal.title_id }}-label">{{ modal.title }}</h3>
<div class="tw:py-4"> <div class="py-4">
{{ fields }} {{ fields }}
</div> </div>
</div> </div>
@@ -5,7 +5,7 @@
{% else %} {% else %}
{% if field.label %} {% if field.label %}
<label for="{{ field.id_for_label }}" class="tw:label{% if labelclass %} {{ labelclass }}{% endif %}"> <label for="{{ field.id_for_label }}" class="label{% if labelclass %} {{ labelclass }}{% endif %}">
{% endif %} {% endif %}
{% if field|is_checkbox %} {% if field|is_checkbox %}
@@ -13,7 +13,7 @@
{% endif %} {% endif %}
{% if field.label %} {% if field.label %}
<span class="tw:label-text">{{ field.label }}</span> <span class="label-text">{{ field.label }}</span>
{% endif %} {% endif %}
{% if not field|is_checkbox %} {% if not field|is_checkbox %}
@@ -3,16 +3,16 @@
{% if field.is_hidden %} {% if field.is_hidden %}
{{ field }} {{ field }}
{% else %} {% else %}
<div id="div_{{ field.auto_id }}" class="tw:mb-3{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_group_wrapper_class %} {{ form_group_wrapper_class }}{% endif %}{% if form_show_errors and field.errors %} tw:has-error{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> <div id="div_{{ field.auto_id }}" class="{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_group_wrapper_class %} {{ form_group_wrapper_class }}{% endif %}{% if form_show_errors and field.errors %} has-error{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.label and form_show_labels %} {% if field.label and form_show_labels %}
<label for="{{ field.id_for_label }}" class="tw:label{{ label_class }}{% if field.field.required %} requiredField{% endif %}"> <label for="{{ field.id_for_label }}" class="label{{ label_class }}{% if field.field.required %} requiredField{% endif %}">
<span class="tw:label-text">{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}</span> <span class="label-text">{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}</span>
</label> </label>
{% endif %} {% endif %}
<div {% if field_class %}class="{{ field_class }}"{% endif %}> <div {% if field_class %}class="{{ field_class }}"{% endif %}>
<label class="tw:input tw:input-bordered tw:flex tw:items-center tw:gap-2{% if input_size %} {{ input_size }}{% endif %}{% if field.errors %} tw:input-error{% endif %}"> <label class="input input-bordered flex items-center gap-2{% if input_size %} {{ input_size }}{% endif %}{% if field.errors %} input-error{% endif %}">
{# prepend #} {# prepend #}
{% if crispy_prepended_text %} {% if crispy_prepended_text %}
<span>{{ crispy_prepended_text }}</span> <span>{{ crispy_prepended_text }}</span>
@@ -21,14 +21,14 @@
{# input #} {# input #}
{% if field|is_select %} {% if field|is_select %}
{% if field.errors %} {% if field.errors %}
{% crispy_field field 'class' 'tw:select tw:select-bordered tw:select-error tw:w-full' %} {% crispy_field field 'class' 'select-error' %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:select tw:select-bordered tw:w-full' %} {% crispy_field field 'class' '' %}
{% endif %} {% endif %}
{% elif field.errors %} {% elif field.errors %}
{% crispy_field field 'class' 'tw:grow' %} {% crispy_field field 'class' 'grow' %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:grow' %} {% crispy_field field 'class' 'grow' %}
{% endif %} {% endif %}
{# append #} {# append #}
@@ -4,17 +4,15 @@
<div {% if field_class %}class="{{ field_class }}"{% endif %}{% if flat_attrs %} {{ flat_attrs }}{% endif %}> <div {% if field_class %}class="{{ field_class }}"{% endif %}{% if flat_attrs %} {{ flat_attrs }}{% endif %}>
{% for group, options, index in field|optgroups %} {% for group, options, index in field|optgroups %}
{% if group %}<strong class="tw:font-bold tw:mb-2 tw:block">{{ group }}</strong>{% endif %} {% if group %}<strong class="font-bold mb-2 block">{{ group }}</strong>{% endif %}
{% for option in options %} {% for option in options %}
<div class="tw:form-control{% if inline_class %} tw:inline-flex{% endif %}"> <label class="label cursor-pointer{% if inline_class %} inline-flex{% endif %}">
<label class="tw:label tw:cursor-pointer{% if inline_class %} tw:inline-flex{% endif %}"> <span class="label-text">{{ option.label|unlocalize }}</span>
<input type="{{option.type}}" class="{% if option.type == 'radio' %}tw:radio{% else %}tw:checkbox{% endif %}{% if field.errors %} tw:{% if option.type == 'radio' %}radio{% else %}checkbox{% endif %}-error{% endif %} tw:me-2" name="{{ field.html_name }}" value="{{ option.value|unlocalize }}" {% include "crispy-daisyui/layout/attrs.html" with widget=option %}> <input type="{{option.type}}" class="{% if option.type == 'radio' %}radio{% else %}checkbox{% endif %}{% if field.errors %} {% if option.type == 'radio' %}radio{% else %}checkbox{% endif %}-error{% endif %}" name="{{ field.html_name }}" value="{{ option.value|unlocalize }}" {% include "crispy-daisyui/layout/attrs.html" with widget=option %}>
<span class="tw:label-text">{{ option.label|unlocalize }}</span> </label>
</label>
{% if field.errors and forloop.last and not inline_class and forloop.parentloop.last %} {% if field.errors and forloop.last and not inline_class and forloop.parentloop.last %}
{% include 'crispy-daisyui/layout/field_errors_block.html' %} {% include 'crispy-daisyui/layout/field_errors_block.html' %}
{% endif %} {% endif %}
</div>
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
@@ -22,13 +20,13 @@
{% if field.errors and inline_class %} {% if field.errors and inline_class %}
{% if field.errors.field_id %} {% if field.errors.field_id %}
{# Django 5.2+ #} {# Django 5.2+ #}
<div id="{{field.errors.field_id}}_error" class="tw:text-error tw:text-sm tw:mt-1"> <label id="{{field.errors.field_id}}_error" for="{{ field.auto_id }}" class="label">
{% else %} {% else %}
<div id="{{field.auto_id}}_error" class="tw:text-error tw:text-sm tw:mt-1"> <label id="{{field.auto_id}}_error" for="{{ field.auto_id }}" class="label">
{% endif %} {% endif %}
{% for error in field.errors %} {% for error in field.errors %}
<p id="error_{{ forloop.counter }}_{{ field.auto_id }}"><strong>{{ error }}</strong></p> <span id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="label-text-alt text-error">{{ error }}</span>
{% endfor %} {% endfor %}
</div> </label>
{% endif %} {% endif %}
{% include 'crispy-daisyui/layout/help_text.html' %} {% include 'crispy-daisyui/layout/help_text.html' %}
@@ -1,11 +1,11 @@
{% if field.is_hidden %} {% if field.is_hidden %}
{{ field }} {{ field }}
{% else %} {% else %}
<div id="div_{{ field.auto_id }}" class="tw:mb-3{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> <div id="div_{{ field.auto_id }}" class="{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.label %} {% if field.label %}
<fieldset class="tw:fieldset"{% if field.aria_describedby %} aria-describedby="{{ field.aria_describedby }}"{% endif %}> <fieldset class="fieldset"{% if field.aria_describedby %} aria-describedby="{{ field.aria_describedby }}"{% endif %}>
<legend for="{{ field.id_for_label }}" class="tw:fieldset-legend {{ label_class }}{% if field.field.required %} requiredField{% endif %}"> <legend for="{{ field.id_for_label }}" class="fieldset-legend {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %} {{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</legend> </legend>
{% endif %} {% endif %}
+1 -1
View File
@@ -1,3 +1,3 @@
<div {% if div.css_id %}id="{{ div.css_id }}"{% endif %} class="tw:grid tw:grid-cols-12 tw:gap-4 {{ div.css_class|default:'' }}" {{ div.flat_attrs }}> <div {% if div.css_id %}id="{{ div.css_id }}"{% endif %} class="grid grid-cols-12 gap-4 {{ div.css_class|default:'' }}" {{ div.flat_attrs }}>
{{ fields|safe }} {{ fields|safe }}
</div> </div>
@@ -3,17 +3,15 @@
{% if field.is_hidden %} {% if field.is_hidden %}
{{ field }} {{ field }}
{% else %} {% else %}
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="tw:form-control tw:mb-3{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> <{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" class="form-control{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
<div class="{{ field_class }}"> <label class="label cursor-pointer justify-start gap-2">
<span class="label-text">{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}</span>
{% if field.errors %} {% if field.errors %}
{% crispy_field field 'class' 'tw:toggle tw:toggle-error' 'role' 'switch' %} {% crispy_field field 'class' 'toggle toggle-error' 'role' 'switch' %}
{% else %} {% else %}
{% crispy_field field 'class' 'tw:toggle' 'role' 'switch' %} {% crispy_field field 'class' 'toggle' 'role' 'switch' %}
{% endif %} {% endif %}
<label for="{{ field.id_for_label }}" class="tw:label{% if field.field.required %} requiredField{% endif %}"> </label>
{{ field.label }} {% include 'crispy-daisyui/layout/help_text_and_errors.html' %}
</label>
{% include 'crispy-daisyui/layout/help_text_and_errors.html' %}
</div>
</{% if tag %}{{ tag }}{% else %}div{% endif %}> </{% if tag %}{{ tag }}{% else %}div{% endif %}>
{% endif %} {% endif %}
@@ -1 +1 @@
<input type="radio" name="{{ link.css_id }}_tabs" role="tab" class="tw:tab{% if 'active' in link.css_class %} tw:tab-active{% endif %}" aria-label="{{ link.name|capfirst }}{% if tab.errors %}!{% endif %}" {% if 'active' in link.css_class %}checked="checked"{% endif %} /> <input type="radio" name="{{ link.css_id }}_tabs" role="tab" class="tab{% if 'active' in link.css_class %} tab-active{% endif %}" aria-label="{{ link.name|capfirst }}{% if tab.errors %}!{% endif %}" {% if 'active' in link.css_class %}checked="checked"{% endif %} />
+1 -1
View File
@@ -1,4 +1,4 @@
<div role="tablist" class="tw:tabs tw:tabs-lifted{% if tabs.css_class %} {{ tabs.css_class }}{% endif %}"{% if tabs.css_id %} id="{{ tabs.css_id }}"{% endif %}> <div role="tablist" class="tabs tabs-lifted{% if tabs.css_class %} {{ tabs.css_class }}{% endif %}"{% if tabs.css_id %} id="{{ tabs.css_id }}"{% endif %}>
{{ links|safe }} {{ links|safe }}
{{ content|safe }} {{ content|safe }}
</div> </div>
@@ -1,9 +1,9 @@
{% load crispy_forms_field %} {% load crispy_forms_field %}
<div id="div_{{ field.auto_id }}" class="tw:mb-3{% if form_show_errors and field.errors %} tw:error{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}"> <div id="div_{{ field.auto_id }}" class="{% if form_show_errors and field.errors %} error{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
<label class="tw:label {{ label_class }}{% if field.field.required %} requiredField{% endif %}"> <label class="label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
<span class="tw:label-text">{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}</span> <span class="label-text">{{ field.label }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}</span>
</label> </label>
<div class="{{ field_class }}"> <div class="{{ field_class }}">
{% crispy_field field 'disabled' 'disabled' %} {% crispy_field field 'disabled' 'disabled' %}
@@ -14,7 +14,7 @@
{{ formset.management_form|crispy }} {{ formset.management_form|crispy }}
</div> </div>
<table{% if form_id %} id="{{ form_id }}_table"{% endif%} class="tw:table tw:table-zebra tw:table-sm"> <table{% if form_id %} id="{{ form_id }}_table"{% endif%} class="table table-zebra table-sm">
<thead> <thead>
{% if formset.readonly and not formset.queryset.exists %} {% if formset.readonly and not formset.queryset.exists %}
{% else %} {% else %}
@@ -31,7 +31,7 @@
</thead> </thead>
<tbody> <tbody>
<tr class="tw:hidden empty-form"> <tr class="hidden empty-form">
{% for field in formset.empty_form %} {% for field in formset.empty_form %}
{% include 'crispy-daisyui/field.html' with tag="td" form_show_labels=False %} {% include 'crispy-daisyui/field.html' with tag="td" form_show_labels=False %}
{% endfor %} {% endfor %}
@@ -2,7 +2,7 @@
{% include 'crispy-daisyui/uni_form.html' %} {% include 'crispy-daisyui/uni_form.html' %}
{% endwith %} {% endwith %}
{% for form in formset %} {% for form in formset %}
<div class="tw:mb-4"> <div class="mb-4">
{% include 'crispy-daisyui/uni_form.html' %} {% include 'crispy-daisyui/uni_form.html' %}
</div> </div>
{% endfor %} {% endfor %}
@@ -20,7 +20,7 @@
{% endfor %} {% endfor %}
{% if inputs %} {% if inputs %}
<div class="tw:flex tw:gap-2 tw:mt-4"> <div class="flex gap-2 mt-4">
{% for input in inputs %} {% for input in inputs %}
{% include "crispy-daisyui/layout/baseinput.html" %} {% include "crispy-daisyui/layout/baseinput.html" %}
{% endfor %} {% endfor %}
+14 -14
View File
@@ -1,9 +1,9 @@
{% load i18n %} {% load i18n %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container px-md-3 py-3 column-gap-5">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:mb-3"> <div class="text-3xl font-bold font-mono w-full mb-3">
{% spaceless %} {% spaceless %}
<div>{% translate 'Currencies' %}<span> <div>{% translate 'Currencies' %}<span>
<a class="tw:no-underline tw:text-2xl tw:p-1 category-action" <a class="no-underline text-2xl p-1 category-action"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}" data-bs-title="{% translate "Add" %}"
@@ -14,15 +14,15 @@
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-body tw:overflow-x-auto"> <div class="card-body overflow-x-auto">
{% if currencies %} {% if currencies %}
<c-config.search></c-config.search> <c-config.search></c-config.search>
<table class="tw:table tw:table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col" class="tw:w-auto">{% translate 'Code' %}</th> <th scope="col" class="w-auto">{% translate 'Code' %}</th>
<th scope="col">{% translate 'Name' %}</th> <th scope="col">{% translate 'Name' %}</th>
<th scope="col">{% translate 'Archived' %}</th> <th scope="col">{% translate 'Archived' %}</th>
</tr> </tr>
@@ -30,16 +30,16 @@
<tbody> <tbody>
{% for currency in currencies %} {% for currency in currencies %}
<tr class="currency"> <tr class="currency">
<td class="tw:w-auto"> <td class="w-auto">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}" data-bs-title="{% translate "Edit" %}"
hx-get="{% url 'currency_edit' pk=currency.id %}" hx-get="{% url 'currency_edit' pk=currency.id %}"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -52,9 +52,9 @@
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
</div> </div>
</td> </td>
<td class="tw:w-auto">{{ currency.code }}</td> <td class="w-auto">{{ currency.code }}</td>
<td>{{ currency.name }}</td> <td>{{ currency.name }}</td>
<td>{% if currency.is_archived %}<i class="fa-solid fa-solid fa-check tw:text-success"></i>{% endif %}</td> <td>{% if currency.is_archived %}<i class="fa-solid fa-solid fa-check text-success"></i>{% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@@ -1,13 +1,13 @@
{% load currency_display %} {% load currency_display %}
{% load i18n %} {% load i18n %}
<div class="tw:container-fluid tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container-fluid px-md-3 py-3 column-gap-5">
<div class="tw:lg:flex tw:justify-between tw:mb-3 tw:w-full"> <div class="lg:flex justify-between mb-3 w-full">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:flex tw:items-center"> <div class="text-3xl font-bold font-mono flex items-center">
{{ strategy.name }} {{ strategy.name }}
</div> </div>
<div class="tw:text-sm tw:lg:text-right tw:mt-2 tw:lg:mt-0"> <div class="text-sm lg:text-right mt-2 lg:mt-0">
<div class="tw:mb-2"> <div class="mb-2">
<span class="tw:badge tw:badge-secondary tw:rounded-full">{{ strategy.payment_currency.name }}</span> x <span class="tw:badge tw:badge-secondary tw:rounded-full">{{ strategy.target_currency.name }}</span> <span class="badge badge-secondary rounded-full">{{ strategy.payment_currency.name }}</span> x <span class="badge badge-secondary rounded-full">{{ strategy.target_currency.name }}</span>
</div> </div>
<div> <div>
{% if strategy.current_price %} {% if strategy.current_price %}
@@ -19,19 +19,19 @@
• {{ strategy.current_price.1|date:"SHORT_DATETIME_FORMAT" }} • {{ strategy.current_price.1|date:"SHORT_DATETIME_FORMAT" }}
</c-amount.display> </c-amount.display>
{% else %} {% else %}
<div class="tw:text-red-400">{% trans "No exchange rate available" %}</div> <div class="text-red-400">{% trans "No exchange rate available" %}</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>
<div class="tw:grid tw:lg:grid-cols-2 tw:gap-3"> <div class="grid lg:grid-cols-2 gap-3">
<div> <div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-body"> <div class="card-body">
{% spaceless %} {% spaceless %}
<div class="tw:card-title tw:text-xl">{% trans "Entries" %}<span> <div class="card-title text-xl">{% trans "Entries" %}<span>
<a class="tw:no-underline tw:p-1 category-action" <a class="no-underline p-1 category-action"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}" data-bs-title="{% translate "Add" %}"
@@ -44,8 +44,8 @@
{% endspaceless %} {% endspaceless %}
{% if entries %} {% if entries %}
<div class="tw:overflow-x-auto"> <div class="overflow-x-auto">
<table class="tw:table tw:table-hover tw:whitespace-nowrap"> <table class="table table-hover whitespace-nowrap">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
@@ -59,9 +59,9 @@
<tbody> <tbody>
{% for entry in entries %} {% for entry in entries %}
<tr> <tr>
<td class="tw:w-auto"> <td class="w-auto">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}" data-bs-title="{% translate "Edit" %}"
@@ -69,7 +69,7 @@
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-swap="innerHTML"> hx-swap="innerHTML">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -107,11 +107,11 @@
</td> </td>
<td> <td>
{% if entry.profit_loss_percentage > 0 %} {% if entry.profit_loss_percentage > 0 %}
<span class="tw:badge tw:badge-success"><i <span class="badge badge-success"><i
class="fa-solid fa-up-long tw:me-2"></i>{{ entry.profit_loss_percentage|floatformat:"2g" }}%</span> class="fa-solid fa-up-long me-2"></i>{{ entry.profit_loss_percentage|floatformat:"2g" }}%</span>
{% elif entry.profit_loss_percentage < 0 %} {% elif entry.profit_loss_percentage < 0 %}
<span class="tw:badge tw:badge-error"><i <span class="badge badge-error"><i
class="fa-solid fa-down-long tw:me-2"></i>{{ entry.profit_loss_percentage|floatformat:"2g" }}%</span> class="fa-solid fa-down-long me-2"></i>{{ entry.profit_loss_percentage|floatformat:"2g" }}%</span>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>
@@ -128,12 +128,12 @@
</div> </div>
</div> </div>
<div> <div>
<div class="tw:grid tw:grid-cols-1 tw:md:grid-cols-2 tw:xl:grid-cols-3 tw:gap-3"> <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3">
<div> <div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:h-full"> <div class="card bg-base-100 shadow-xl h-full">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{% trans "Total Invested" %}</h5> <h5 class="card-title">{% trans "Total Invested" %}</h5>
<div class="tw:text-base-content"> <div class="text-base-content">
<c-amount.display <c-amount.display
:amount="strategy.total_invested" :amount="strategy.total_invested"
:prefix="strategy.payment_currency.prefix" :prefix="strategy.payment_currency.prefix"
@@ -144,10 +144,10 @@
</div> </div>
</div> </div>
<div> <div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:h-full"> <div class="card bg-base-100 shadow-xl h-full">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{% trans "Total Received" %}</h5> <h5 class="card-title">{% trans "Total Received" %}</h5>
<div class="tw:text-base-content"> <div class="text-base-content">
<c-amount.display <c-amount.display
:amount="strategy.total_received" :amount="strategy.total_received"
:prefix="strategy.target_currency.prefix" :prefix="strategy.target_currency.prefix"
@@ -158,10 +158,10 @@
</div> </div>
</div> </div>
<div> <div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:h-full"> <div class="card bg-base-100 shadow-xl h-full">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{% trans "Current Total Value" %}</h5> <h5 class="card-title">{% trans "Current Total Value" %}</h5>
<div class="tw:text-base-content"> <div class="text-base-content">
<c-amount.display <c-amount.display
:amount="strategy.current_total_value" :amount="strategy.current_total_value"
:prefix="strategy.payment_currency.prefix" :prefix="strategy.payment_currency.prefix"
@@ -172,10 +172,10 @@
</div> </div>
</div> </div>
<div> <div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:h-full"> <div class="card bg-base-100 shadow-xl h-full">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{% trans "Average Entry Price" %}</h5> <h5 class="card-title">{% trans "Average Entry Price" %}</h5>
<div class="tw:text-base-content"> <div class="text-base-content">
<c-amount.display <c-amount.display
:amount="strategy.average_entry_price" :amount="strategy.average_entry_price"
:prefix="strategy.payment_currency.prefix" :prefix="strategy.payment_currency.prefix"
@@ -186,11 +186,11 @@
</div> </div>
</div> </div>
<div> <div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:h-full"> <div class="card bg-base-100 shadow-xl h-full">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{% trans "Total P/L" %}</h5> <h5 class="card-title">{% trans "Total P/L" %}</h5>
<div <div
class="tw:text-base-content {% if strategy.total_profit_loss >= 0 %}tw:text-green-400{% else %}tw:text-red-400{% endif %}"> class="text-base-content {% if strategy.total_profit_loss >= 0 %}text-green-400{% else %}text-red-400{% endif %}">
<c-amount.display <c-amount.display
:amount="strategy.total_profit_loss" :amount="strategy.total_profit_loss"
:prefix="strategy.payment_currency.prefix" :prefix="strategy.payment_currency.prefix"
@@ -202,19 +202,19 @@
</div> </div>
</div> </div>
<div> <div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:h-full"> <div class="card bg-base-100 shadow-xl h-full">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{% trans "Total % P/L" %}</h5> <h5 class="card-title">{% trans "Total % P/L" %}</h5>
<div <div
class="tw:text-base-content {% if strategy.total_profit_loss >= 0 %}tw:text-green-400{% else %}tw:text-red-400{% endif %}"> class="text-base-content {% if strategy.total_profit_loss >= 0 %}text-green-400{% else %}text-red-400{% endif %}">
{{ strategy.total_profit_loss_percentage|floatformat:2 }}% {{ strategy.total_profit_loss_percentage|floatformat:2 }}%
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="tw:grid tw:mt-4"> <div class="grid mt-4">
<div class="tw:col-span-12" <div class="col-span-12"
_="on htmx:afterSettle from #strategy-details _="on htmx:afterSettle from #strategy-details
set perfomancectx to #performanceChart.getContext('2d') set perfomancectx to #performanceChart.getContext('2d')
js(perfomancectx) js(perfomancectx)
@@ -283,16 +283,16 @@
}) })
end end
"> ">
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{% trans "Performance Over Time" %}</h5> <h5 class="card-title">{% trans "Performance Over Time" %}</h5>
<canvas id="performanceChart"></canvas> <canvas id="performanceChart"></canvas>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="tw:grid tw:mt-4"> <div class="grid mt-4">
<div class="tw:col-span-12" <div class="col-span-12"
_="on htmx:afterSettle from #strategy-details _="on htmx:afterSettle from #strategy-details
set pricectx to #priceChart.getContext('2d') set pricectx to #priceChart.getContext('2d')
set priceData to {{ price_comparison_data|safe }} set priceData to {{ price_comparison_data|safe }}
@@ -387,16 +387,16 @@
}) })
end end
"> ">
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{% trans "Entry Price vs Current Price" %}</h5> <h5 class="card-title">{% trans "Entry Price vs Current Price" %}</h5>
<canvas id="priceChart"></canvas> <canvas id="priceChart"></canvas>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="tw:grid tw:mt-4"> <div class="grid mt-4">
<div class="tw:col-span-12" <div class="col-span-12"
_="on htmx:afterSettle from #strategy-details _="on htmx:afterSettle from #strategy-details
set frequencyctx to #frequencyChart.getContext('2d') set frequencyctx to #frequencyChart.getContext('2d')
js(frequencyctx) js(frequencyctx)
@@ -450,10 +450,10 @@
}) })
end end
"> ">
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{% trans "Investment Frequency" %}</h5> <h5 class="card-title">{% trans "Investment Frequency" %}</h5>
<p class="tw:text-base-content/60"> <p class="text-base-content/60">
{% trans "The straighter the blue line, the more consistent your DCA strategy is." %} {% trans "The straighter the blue line, the more consistent your DCA strategy is." %}
</p> </p>
<canvas id="frequencyChart"></canvas> <canvas id="frequencyChart"></canvas>
+16 -16
View File
@@ -1,9 +1,9 @@
{% load i18n %} {% load i18n %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container px-md-3 py-3 column-gap-5">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:mb-3"> <div class="text-3xl font-bold font-mono w-full mb-3">
{% spaceless %} {% spaceless %}
<div>{% translate 'Dollar Cost Average Strategies' %}<span> <div>{% translate 'Dollar Cost Average Strategies' %}<span>
<a class="tw:no-underline tw:text-2xl tw:p-1 category-action" <a class="no-underline text-2xl p-1 category-action"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}" data-bs-title="{% translate "Add" %}"
@@ -14,23 +14,23 @@
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:grid tw:grid-cols-1 tw:md:grid-cols-2 tw:lg:grid-cols-3 tw:xl:grid-cols-4 tw:gap-3"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3">
{% for strategy in strategies %} {% for strategy in strategies %}
<div> <div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:h-full tw:flex tw:flex-col"> <div class="card bg-base-100 shadow-xl h-full flex flex-col">
<div class="tw:card-header tw:bg-base-200 tw:p-4"> <div class="card-header bg-base-200 p-4">
<span class="tw:badge tw:badge-secondary tw:rounded-full">{{ strategy.payment_currency.name }}</span> x <span <span class="badge badge-secondary rounded-full">{{ strategy.payment_currency.name }}</span> x <span
class="tw:badge tw:badge-secondary tw:rounded-full">{{ strategy.target_currency.name }}</span> class="badge badge-secondary rounded-full">{{ strategy.target_currency.name }}</span>
</div> </div>
<a href="{% url 'dca_strategy_detail_index' strategy_id=strategy.id %}" hx-boost="true" <a href="{% url 'dca_strategy_detail_index' strategy_id=strategy.id %}" hx-boost="true"
class="tw:no-underline tw:card-body tw:flex-1"> class="no-underline card-body flex-1">
<div class=""> <div class="">
<div class="tw:card-title tw:text-xl">{{ strategy.name }}</div> <div class="card-title text-xl">{{ strategy.name }}</div>
<div class="tw:text-base-content/60">{{ strategy.notes }}</div> <div class="text-base-content/60">{{ strategy.notes }}</div>
</div> </div>
</a> </a>
<div class="tw:card-footer tw:bg-base-200 tw:p-4 tw:text-right"> <div class="card-footer bg-base-200 p-4 text-right">
<a class="tw:no-underline tw:text-base-content/60 tw:p-1" <a class="no-underline text-base-content/60 p-1"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}" data-bs-title="{% translate "Edit" %}"
@@ -38,7 +38,7 @@
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-pencil fa-fw"></i> <i class="fa-solid fa-pencil fa-fw"></i>
</a> </a>
<a class="tw:text-error tw:no-underline tw:p-1" <a class="text-error no-underline p-1"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -52,7 +52,7 @@
<i class="fa-solid fa-trash fa-fw"></i> <i class="fa-solid fa-trash fa-fw"></i>
</a> </a>
{% if not strategy.owner %} {% if not strategy.owner %}
<a class="tw:text-primary tw:no-underline tw:p-1" <a class="text-primary no-underline p-1"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Take ownership" %}" data-bs-title="{% translate "Take ownership" %}"
@@ -60,7 +60,7 @@
<i class="fa-solid fa-crown fa-fw"></i></a> <i class="fa-solid fa-crown fa-fw"></i></a>
{% endif %} {% endif %}
{% if user == strategy.owner %} {% if user == strategy.owner %}
<a class="tw:text-primary tw:no-underline tw:p-1" <a class="text-primary no-underline p-1"
role="button" role="button"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
+9 -9
View File
@@ -1,9 +1,9 @@
{% load i18n %} {% load i18n %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container px-md-3 py-3 column-gap-5">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:mb-3"> <div class="text-3xl font-bold font-mono w-full mb-3">
{% spaceless %} {% spaceless %}
<div>{% translate 'Entities' %}<span> <div>{% translate 'Entities' %}<span>
<a class="tw:no-underline tw:text-2xl tw:p-1 category-action" <a class="no-underline text-2xl p-1 category-action"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}" data-bs-title="{% translate "Add" %}"
@@ -14,14 +14,14 @@
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-header tw:bg-base-200 tw:p-4"> <div class="card-header bg-base-200 p-4">
<div role="tablist" class="tw:tabs tw:tabs-lifted"> <div role="tablist" class="tabs tabs-lifted">
<button class="tw:tab tw:tab-active" data-bs-toggle="tab" type="button" role="tab" aria-selected="true" hx-get="{% url 'entities_table_active' %}" hx-trigger="load, click" hx-target="#entities-table">{% translate 'Active' %}</button> <button class="tab tab-active" data-bs-toggle="tab" type="button" role="tab" aria-selected="true" hx-get="{% url 'entities_table_active' %}" hx-trigger="load, click" hx-target="#entities-table">{% translate 'Active' %}</button>
<button class="tw:tab" hx-get="{% url 'entities_table_archived' %}" hx-target="#entities-table" data-bs-toggle="tab" type="button" role="tab" aria-selected="false">{% translate 'Archived' %}</button> <button class="tab" hx-get="{% url 'entities_table_archived' %}" hx-target="#entities-table" data-bs-toggle="tab" type="button" role="tab" aria-selected="false">{% translate 'Archived' %}</button>
</div> </div>
</div> </div>
<div class="tw:card-body"> <div class="card-body">
<div id="entities-table"></div> <div id="entities-table"></div>
</div> </div>
</div> </div>
+9 -9
View File
@@ -7,21 +7,21 @@
hx-swap="outerHTML"> hx-swap="outerHTML">
{% endif %} {% endif %}
{% if entities %} {% if entities %}
<div class="tw:overflow-x-auto"> <div class="overflow-x-auto">
<c-config.search></c-config.search> <c-config.search></c-config.search>
<table class="tw:table tw:table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col">{% translate 'Name' %}</th> <th scope="col">{% translate 'Name' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for entity in entities %} {% for entity in entities %}
<tr class="entity"> <tr class="entity">
<td class="tw:w-auto"> <td class="w-auto">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
hx-swap="innerHTML" hx-swap="innerHTML"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
@@ -29,7 +29,7 @@
hx-get="{% url 'entity_edit' entity_id=entity.id %}" hx-get="{% url 'entity_edit' entity_id=entity.id %}"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
hx-swap="innerHTML" hx-swap="innerHTML"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
@@ -42,7 +42,7 @@
data-confirm-text="{% translate "Yes, delete it!" %}" data-confirm-text="{% translate "Yes, delete it!" %}"
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
{% if not entity.owner %} {% if not entity.owner %}
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-warning" <a class="btn btn-secondary btn-sm join-item text-warning"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Take ownership" %}" data-bs-title="{% translate "Take ownership" %}"
@@ -50,7 +50,7 @@
<i class="fa-solid fa-crown fa-fw"></i></a> <i class="fa-solid fa-crown fa-fw"></i></a>
{% endif %} {% endif %}
{% if user == entity.owner %} {% if user == entity.owner %}
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-primary" <a class="btn btn-secondary btn-sm join-item text-primary"
role="button" role="button"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-swap="innerHTML" hx-swap="innerHTML"
@@ -1,10 +1,10 @@
{% load currency_display %} {% load currency_display %}
{% load i18n %} {% load i18n %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container px-md-3 py-3 column-gap-5">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:mb-3"> <div class="text-3xl font-bold font-mono w-full mb-3">
{% spaceless %} {% spaceless %}
<div>{% translate 'Exchange Rates' %}<span> <div>{% translate 'Exchange Rates' %}<span>
<a class="tw:no-underline tw:text-2xl tw:p-1 category-action" <a class="no-underline text-2xl p-1 category-action"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}" data-bs-title="{% translate "Add" %}"
@@ -15,15 +15,15 @@
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-header tw:bg-base-200 tw:p-4"> <div class="card-header bg-base-200 p-4">
<div role="tablist" class="tw:tabs tw:tabs-lifted"> <div role="tablist" class="tabs tabs-lifted">
<button class="tw:tab tw:tab-active" hx-indicator="#exchange-rates-table" data-bs-toggle="tab" type="button" <button class="tab tab-active" hx-indicator="#exchange-rates-table" data-bs-toggle="tab" type="button"
role="tab" aria-controls="home-tab-pane" aria-selected="true" role="tab" aria-controls="home-tab-pane" aria-selected="true"
hx-get="{% url 'exchange_rates_list_pair' %}" hx-trigger="load, click" hx-get="{% url 'exchange_rates_list_pair' %}" hx-trigger="load, click"
hx-target="#exchange-rates-table" aria-controls="#exchange-rates-table">{% translate 'All' %}</button> hx-target="#exchange-rates-table" aria-controls="#exchange-rates-table">{% translate 'All' %}</button>
{% for pair in pairings %} {% for pair in pairings %}
<button class="tw:tab" hx-indicator="#exchange-rates-table" <button class="tab" hx-indicator="#exchange-rates-table"
hx-get="{% url 'exchange_rates_list_pair' %}" hx-get="{% url 'exchange_rates_list_pair' %}"
hx-vals='{"from": "{{ pair.1 }}", "to": "{{ pair.2 }}"}' hx-vals='{"from": "{{ pair.1 }}", "to": "{{ pair.2 }}"}'
hx-target="#exchange-rates-table" data-bs-toggle="tab" type="button" role="tab" hx-target="#exchange-rates-table" data-bs-toggle="tab" type="button" role="tab"
@@ -32,7 +32,7 @@
</div> </div>
</div> </div>
<div class="tw:card"> <div class="card">
<div id="exchange-rates-table" class="show-loading"></div> <div id="exchange-rates-table" class="show-loading"></div>
</div> </div>
@@ -1,12 +1,12 @@
{% load currency_display %} {% load currency_display %}
{% load i18n %} {% load i18n %}
<div class="tw:card-body show-loading" hx-get="{% url 'exchange_rates_list_pair' %}" hx-trigger="updated from:window" hx-swap="outerHTML" hx-vals='{"page": "{{ page_obj.number }}", "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'> <div class="card-body show-loading" hx-get="{% url 'exchange_rates_list_pair' %}" hx-trigger="updated from:window" hx-swap="outerHTML" hx-vals='{"page": "{{ page_obj.number }}", "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'>
{% if page_obj %} {% if page_obj %}
<div class="tw:overflow-x-auto"> <div class="overflow-x-auto">
<table class="tw:table tw:table-hover tw:whitespace-nowrap"> <table class="table table-hover whitespace-nowrap">
<thead> <thead>
<tr> <tr>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col">{% translate 'Date' %}</th> <th scope="col">{% translate 'Date' %}</th>
<th scope="col">{% translate 'Pairing' %}</th> <th scope="col">{% translate 'Pairing' %}</th>
<th scope="col">{% translate 'Rate' %}</th> <th scope="col">{% translate 'Rate' %}</th>
@@ -15,9 +15,9 @@
<tbody> <tbody>
{% for exchange_rate in page_obj %} {% for exchange_rate in page_obj %}
<tr class="exchange-rate"> <tr class="exchange-rate">
<td class="tw:w-auto"> <td class="w-auto">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}" data-bs-title="{% translate "Edit" %}"
@@ -25,7 +25,7 @@
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-swap="innerHTML"> hx-swap="innerHTML">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -39,9 +39,9 @@
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
</div> </div>
</td> </td>
<td class="tw:w-1/4">{{ exchange_rate.date|date:"SHORT_DATETIME_FORMAT" }}</td> <td class="w-1/4">{{ exchange_rate.date|date:"SHORT_DATETIME_FORMAT" }}</td>
<td class="tw:w-1/4"><span class="tw:badge tw:badge-secondary tw:rounded-full">{{ exchange_rate.from_currency.name }}</span> x <span class="tw:badge tw:badge-secondary tw:rounded-full">{{ exchange_rate.to_currency.name }}</span></td> <td class="w-1/4"><span class="badge badge-secondary rounded-full">{{ exchange_rate.from_currency.name }}</span> x <span class="badge badge-secondary rounded-full">{{ exchange_rate.to_currency.name }}</span></td>
<td class="tw:w-1/4">1 {{ exchange_rate.from_currency.name }} ≅ {% currency_display amount=exchange_rate.rate prefix=exchange_rate.to_currency.prefix suffix=exchange_rate.to_currency.suffix decimal_places=exchange_rate.to_currency.decimal_places%}</td> <td class="w-1/4">1 {{ exchange_rate.from_currency.name }} ≅ {% currency_display amount=exchange_rate.rate prefix=exchange_rate.to_currency.prefix suffix=exchange_rate.to_currency.suffix decimal_places=exchange_rate.to_currency.decimal_places%}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@@ -52,12 +52,12 @@
{% endif %} {% endif %}
{% if page_obj.has_other_pages %} {% if page_obj.has_other_pages %}
<div class="tw:mt-auto"> <div class="mt-auto">
<input value="{{ page_obj.number }}" name="page" type="hidden" id="page"> <input value="{{ page_obj.number }}" name="page" type="hidden" id="page">
<nav aria-label="{% translate 'Page navigation' %}"> <nav aria-label="{% translate 'Page navigation' %}">
<div class="tw:join tw:flex tw:justify-center tw:mt-5"> <div class="join flex justify-center mt-5">
<button class="tw:join-item tw:btn tw:btn-sm {% if not page_obj.has_previous %}tw:btn-disabled{% endif %}" <button class="join-item btn btn-sm {% if not page_obj.has_previous %}btn-disabled{% endif %}"
hx-get="{% if page_obj.has_previous %}{% url 'exchange_rates_list_pair' %}{% endif %}" hx-get="{% if page_obj.has_previous %}{% url 'exchange_rates_list_pair' %}{% endif %}"
hx-vals='{"page": 1, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}' hx-vals='{"page": 1, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'
hx-include="#filter, #order" hx-include="#filter, #order"
@@ -76,11 +76,11 @@
{% endcomment %} {% endcomment %}
{% if page_number <= page_obj.number|add:3 and page_number >= page_obj.number|add:-3 %} {% if page_number <= page_obj.number|add:3 and page_number >= page_obj.number|add:-3 %}
{% if page_obj.number == page_number %} {% if page_obj.number == page_number %}
<button class="tw:join-item tw:btn tw:btn-sm tw:btn-active"> <button class="join-item btn btn-sm btn-active">
{{ page_number }} {{ page_number }}
</button> </button>
{% else %} {% else %}
<button class="tw:join-item tw:btn tw:btn-sm" <button class="join-item btn btn-sm"
hx-get="{% url 'exchange_rates_list_pair' %}" hx-get="{% url 'exchange_rates_list_pair' %}"
hx-vals='{"page": {{ page_number }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}' hx-vals='{"page": {{ page_number }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'
hx-target="#exchange-rates-table" hx-target="#exchange-rates-table"
@@ -91,11 +91,11 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if page_obj.number|add:3 < page_obj.paginator.num_pages %} {% if page_obj.number|add:3 < page_obj.paginator.num_pages %}
<button class="tw:join-item tw:btn tw:btn-sm tw:btn-disabled" <button class="join-item btn btn-sm btn-disabled"
aria-label="..."> aria-label="...">
<span aria-hidden="true">...</span> <span aria-hidden="true">...</span>
</button> </button>
<button class="tw:join-item tw:btn tw:btn-sm" <button class="join-item btn btn-sm"
hx-get="{% url 'exchange_rates_list_pair' %}" hx-target="#exchange-rates-table" hx-get="{% url 'exchange_rates_list_pair' %}" hx-target="#exchange-rates-table"
hx-vals='{"page": {{ page_obj.paginator.num_pages }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}' hx-vals='{"page": {{ page_obj.paginator.num_pages }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'
hx-include="#filter, #order" hx-include="#filter, #order"
@@ -104,7 +104,7 @@
<span aria-hidden="true">{{ page_obj.paginator.num_pages }}</span> <span aria-hidden="true">{{ page_obj.paginator.num_pages }}</span>
</button> </button>
{% endif %} {% endif %}
<button class="tw:join-item tw:btn tw:btn-sm {% if not page_obj.has_next %}tw:btn-disabled{% endif %}" <button class="join-item btn btn-sm {% if not page_obj.has_next %}btn-disabled{% endif %}"
hx-get="{% if page_obj.has_next %}{% url 'exchange_rates_list_pair' %}{% endif %}" hx-get="{% if page_obj.has_next %}{% url 'exchange_rates_list_pair' %}{% endif %}"
hx-vals='{"page": {{ page_obj.paginator.num_pages }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}' hx-vals='{"page": {{ page_obj.paginator.num_pages }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'
hx-include="#filter, #order" hx-include="#filter, #order"
@@ -1,10 +1,10 @@
{% load currency_display %} {% load currency_display %}
{% load i18n %} {% load i18n %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container px-md-3 py-3 column-gap-5">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:mb-3"> <div class="text-3xl font-bold font-mono w-full mb-3">
{% spaceless %} {% spaceless %}
<div>{% translate 'Automatic Exchange Rates' %}<span> <div>{% translate 'Automatic Exchange Rates' %}<span>
<a class="tw:no-underline tw:text-2xl tw:p-1 category-action" <a class="no-underline text-2xl p-1 category-action"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}" data-bs-title="{% translate "Add" %}"
@@ -15,21 +15,21 @@
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-header tw:bg-base-200 tw:p-4 tw:text-base-content/70"> <div class="card-header bg-base-200 p-4 text-base-content/70">
<button type="button" hx-get="{% url 'automatic_exchange_rate_force_fetch' %}" <button type="button" hx-get="{% url 'automatic_exchange_rate_force_fetch' %}"
class="tw:btn tw:btn-outline tw:btn-primary tw:btn-sm">{% trans 'Fetch all' %}</button> class="btn btn-outline btn-primary btn-sm">{% trans 'Fetch all' %}</button>
</div> </div>
<div class="tw:card-body"> <div class="card-body">
{% if services %} {% if services %}
<c-config.search></c-config.search> <c-config.search></c-config.search>
<div class="tw:overflow-x-auto"> <div class="overflow-x-auto">
<table class="tw:table tw:table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col" class="tw:w-auto">{% translate 'Name' %}</th> <th scope="col" class="w-auto">{% translate 'Name' %}</th>
<th scope="col">{% translate 'Service' %}</th> <th scope="col">{% translate 'Service' %}</th>
<th scope="col">{% translate 'Targeting' %}</th> <th scope="col">{% translate 'Targeting' %}</th>
<th scope="col">{% translate 'Last fetch' %}</th> <th scope="col">{% translate 'Last fetch' %}</th>
@@ -38,16 +38,16 @@
<tbody> <tbody>
{% for service in services %} {% for service in services %}
<tr class="services"> <tr class="services">
<td class="tw:w-auto"> <td class="w-auto">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}" data-bs-title="{% translate "Edit" %}"
hx-get="{% url 'automatic_exchange_rate_edit' pk=service.id %}" hx-get="{% url 'automatic_exchange_rate_edit' pk=service.id %}"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -60,9 +60,9 @@
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
</div> </div>
</td> </td>
<td class="tw:w-auto">{% if service.is_active %}<i class="fa-solid fa-circle tw:text-success"></i>{% else %} <td class="w-auto">{% if service.is_active %}<i class="fa-solid fa-circle text-success"></i>{% else %}
<i class="fa-solid fa-circle tw:text-error"></i>{% endif %}</td> <i class="fa-solid fa-circle text-error"></i>{% endif %}</td>
<td class="tw:w-auto">{{ service.name }}</td> <td class="w-auto">{{ service.name }}</td>
<td>{{ service.get_service_type_display }}</td> <td>{{ service.get_service_type_display }}</td>
<td>{{ service.target_currencies.count }} {% trans 'currencies' %}, {{ service.target_accounts.count }} {% trans 'accounts' %}</td> <td>{{ service.target_currencies.count }} {% trans 'currencies' %}, {{ service.target_accounts.count }} {% trans 'accounts' %}</td>
<td>{{ service.last_fetch|date:"SHORT_DATETIME_FORMAT" }}</td> <td>{{ service.last_fetch|date:"SHORT_DATETIME_FORMAT" }}</td>
@@ -1,12 +1,12 @@
{% load currency_display %} {% load currency_display %}
{% load i18n %} {% load i18n %}
<div class="tw:card-body show-loading" hx-get="{% url 'exchange_rates_list_pair' %}" hx-trigger="updated from:window" hx-swap="outerHTML" hx-vals='{"page": "{{ page_obj.number }}", "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'> <div class="card-body show-loading" hx-get="{% url 'exchange_rates_list_pair' %}" hx-trigger="updated from:window" hx-swap="outerHTML" hx-vals='{"page": "{{ page_obj.number }}", "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'>
{% if page_obj %} {% if page_obj %}
<div class="tw:overflow-x-auto"> <div class="overflow-x-auto">
<table class="tw:table tw:table-hover tw:whitespace-nowrap"> <table class="table table-hover whitespace-nowrap">
<thead> <thead>
<tr> <tr>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col">{% translate 'Date' %}</th> <th scope="col">{% translate 'Date' %}</th>
<th scope="col">{% translate 'Pairing' %}</th> <th scope="col">{% translate 'Pairing' %}</th>
<th scope="col">{% translate 'Rate' %}</th> <th scope="col">{% translate 'Rate' %}</th>
@@ -15,9 +15,9 @@
<tbody> <tbody>
{% for exchange_rate in page_obj %} {% for exchange_rate in page_obj %}
<tr class="exchange-rate"> <tr class="exchange-rate">
<td class="tw:w-auto"> <td class="w-auto">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}" data-bs-title="{% translate "Edit" %}"
@@ -25,7 +25,7 @@
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
hx-swap="innerHTML"> hx-swap="innerHTML">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -39,9 +39,9 @@
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a> _="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
</div> </div>
</td> </td>
<td class="tw:w-1/4">{{ exchange_rate.date|date:"SHORT_DATETIME_FORMAT" }}</td> <td class="w-1/4">{{ exchange_rate.date|date:"SHORT_DATETIME_FORMAT" }}</td>
<td class="tw:w-1/4"><span class="tw:badge tw:badge-secondary tw:rounded-full">{{ exchange_rate.from_currency.name }}</span> x <span class="tw:badge tw:badge-secondary tw:rounded-full">{{ exchange_rate.to_currency.name }}</span></td> <td class="w-1/4"><span class="badge badge-secondary rounded-full">{{ exchange_rate.from_currency.name }}</span> x <span class="badge badge-secondary rounded-full">{{ exchange_rate.to_currency.name }}</span></td>
<td class="tw:w-1/4">1 {{ exchange_rate.from_currency.name }} ≅ {% currency_display amount=exchange_rate.rate prefix=exchange_rate.to_currency.prefix suffix=exchange_rate.to_currency.suffix decimal_places=exchange_rate.to_currency.decimal_places%}</td> <td class="w-1/4">1 {{ exchange_rate.from_currency.name }} ≅ {% currency_display amount=exchange_rate.rate prefix=exchange_rate.to_currency.prefix suffix=exchange_rate.to_currency.suffix decimal_places=exchange_rate.to_currency.decimal_places%}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@@ -52,12 +52,12 @@
{% endif %} {% endif %}
{% if page_obj.has_other_pages %} {% if page_obj.has_other_pages %}
<div class="tw:mt-auto"> <div class="mt-auto">
<input value="{{ page_obj.number }}" name="page" type="hidden" id="page"> <input value="{{ page_obj.number }}" name="page" type="hidden" id="page">
<nav aria-label="{% translate 'Page navigation' %}"> <nav aria-label="{% translate 'Page navigation' %}">
<div class="tw:join tw:flex tw:justify-center tw:mt-5"> <div class="join flex justify-center mt-5">
<button class="tw:join-item tw:btn tw:btn-sm tw:cursor-pointer {% if not page_obj.has_previous %}tw:btn-disabled{% endif %}" <button class="join-item btn btn-sm cursor-pointer {% if not page_obj.has_previous %}btn-disabled{% endif %}"
hx-get="{% if page_obj.has_previous %}{% url 'exchange_rates_list_pair' %}{% endif %}" hx-get="{% if page_obj.has_previous %}{% url 'exchange_rates_list_pair' %}{% endif %}"
hx-vals='{"page": 1, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}' hx-vals='{"page": 1, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'
hx-include="#filter, #order" hx-include="#filter, #order"
@@ -76,11 +76,11 @@
{% endcomment %} {% endcomment %}
{% if page_number <= page_obj.number|add:3 and page_number >= page_obj.number|add:-3 %} {% if page_number <= page_obj.number|add:3 and page_number >= page_obj.number|add:-3 %}
{% if page_obj.number == page_number %} {% if page_obj.number == page_number %}
<button class="tw:join-item tw:btn tw:btn-sm tw:btn-active tw:cursor-pointer"> <button class="join-item btn btn-sm btn-active cursor-pointer">
{{ page_number }} {{ page_number }}
</button> </button>
{% else %} {% else %}
<button class="tw:join-item tw:btn tw:btn-sm tw:cursor-pointer" <button class="join-item btn btn-sm cursor-pointer"
hx-get="{% url 'exchange_rates_list_pair' %}" hx-get="{% url 'exchange_rates_list_pair' %}"
hx-vals='{"page": {{ page_number }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}' hx-vals='{"page": {{ page_number }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'
hx-target="#exchange-rates-table" hx-target="#exchange-rates-table"
@@ -91,11 +91,11 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if page_obj.number|add:3 < page_obj.paginator.num_pages %} {% if page_obj.number|add:3 < page_obj.paginator.num_pages %}
<button class="tw:join-item tw:btn tw:btn-sm tw:btn-disabled" <button class="join-item btn btn-sm btn-disabled"
aria-label="..."> aria-label="...">
<span aria-hidden="true">...</span> <span aria-hidden="true">...</span>
</button> </button>
<button class="tw:join-item tw:btn tw:btn-sm tw:cursor-pointer" <button class="join-item btn btn-sm cursor-pointer"
hx-get="{% url 'exchange_rates_list_pair' %}" hx-target="#exchange-rates-table" hx-get="{% url 'exchange_rates_list_pair' %}" hx-target="#exchange-rates-table"
hx-vals='{"page": {{ page_obj.paginator.num_pages }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}' hx-vals='{"page": {{ page_obj.paginator.num_pages }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'
hx-include="#filter, #order" hx-include="#filter, #order"
@@ -104,7 +104,7 @@
<span aria-hidden="true">{{ page_obj.paginator.num_pages }}</span> <span aria-hidden="true">{{ page_obj.paginator.num_pages }}</span>
</button> </button>
{% endif %} {% endif %}
<button class="tw:join-item tw:btn tw:btn-sm {% if not page_obj.has_next %}tw:btn-disabled{% endif %} tw:cursor-pointer" <button class="join-item btn btn-sm {% if not page_obj.has_next %}btn-disabled{% endif %} cursor-pointer"
hx-get="{% if page_obj.has_next %}{% url 'exchange_rates_list_pair' %}{% endif %}" hx-get="{% if page_obj.has_next %}{% url 'exchange_rates_list_pair' %}{% endif %}"
hx-vals='{"page": {{ page_obj.paginator.num_pages }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}' hx-vals='{"page": {{ page_obj.paginator.num_pages }}, "from": "{{ from_currency|default_if_none:"" }}", "to": "{{ to_currency|default_if_none:"" }}"}'
hx-include="#filter, #order" hx-include="#filter, #order"
@@ -5,8 +5,8 @@
{% block title %}{% translate 'Export' %}{% endblock %} {% block title %}{% translate 'Export' %}{% endblock %}
{% block body %} {% block body %}
<div class="tw:container tw:p-3"> <div class="container p-3">
<form hx-post="{% url 'export_form' %}" hx-ext="htmx-download" hx-swap="none" id="export-form" class="show-loading tw:px-1" target="_blank"> <form hx-post="{% url 'export_form' %}" hx-ext="htmx-download" hx-swap="none" id="export-form" class="show-loading px-1" target="_blank">
{% crispy form %} {% crispy form %}
</form> </form>
</div> </div>
@@ -5,12 +5,12 @@
{% block title %}{% translate 'Restore' %}{% endblock %} {% block title %}{% translate 'Restore' %}{% endblock %}
{% block body %} {% block body %}
<div class="tw:container tw:p-3"> <div class="container p-3">
<form hx-post="{% url 'restore_form' %}" <form hx-post="{% url 'restore_form' %}"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
id="restore-form" id="restore-form"
enctype="multipart/form-data" enctype="multipart/form-data"
class="show-loading tw:px-1"> class="show-loading px-1">
{% crispy form %} {% crispy form %}
</form> </form>
</div> </div>
+3 -3
View File
@@ -1,7 +1,7 @@
{% load i18n %} {% load i18n %}
<div class="offcanvas-header tw:flex tw:justify-between tw:items-center"> <div class="offcanvas-header flex justify-between items-center">
<h5 class="offcanvas-title tw:font-medium tw:text-xl">{% block title %}{% endblock %}</h5> <h5 class="offcanvas-title font-medium text-xl">{% block title %}{% endblock %}</h5>
<button type="button" class="tw:btn tw:btn-ghost tw:btn-sm tw:btn-circle" aria-label="{% trans 'Close' %}" data-bs-dismiss="offcanvas"><i class="fa-solid fa-xmark"></i></button> <button type="button" class="btn btn-ghost btn-sm btn-circle" aria-label="{% trans 'Close' %}" data-bs-dismiss="offcanvas"><i class="fa-solid fa-xmark"></i></button>
</div> </div>
<div id="generic-offcanvas-body" class="offcanvas-body" <div id="generic-offcanvas-body" class="offcanvas-body"
_="install init_tom_select _="install init_tom_select
@@ -8,7 +8,7 @@
{% block body %} {% block body %}
{% if message %} {% if message %}
<div class="alert alert-info" role="alert" id="msg" hx-preserve="true"> <div class="alert alert-info" role="alert" id="msg" hx-preserve="true">
<h6 class="alert-heading tw:italic tw:font-bold">{% trans 'A message from the author' %}</h6> <h6 class="alert-heading italic font-bold">{% trans 'A message from the author' %}</h6>
<hr> <hr>
<p class="mb-0">{{ message|linebreaksbr }}</p> <p class="mb-0">{{ message|linebreaksbr }}</p>
</div> </div>
@@ -1,16 +1,16 @@
{% load i18n %} {% load i18n %}
<div class="tw:container tw:px-md-3 tw:py-3 tw:column-gap-5"> <div class="container px-md-3 py-3 column-gap-5">
<div class="tw:text-3xl tw:font-bold tw:font-mono tw:w-full tw:mb-3"> <div class="text-3xl font-bold font-mono w-full mb-3">
{% spaceless %} {% spaceless %}
<div>{% translate 'Import Profiles' %}<span> <div>{% translate 'Import Profiles' %}<span>
<span class="tw:dropdown" data-bs-toggle="tooltip" <span class="dropdown" data-bs-toggle="tooltip"
data-bs-title="{% translate "Add" %}"> data-bs-title="{% translate "Add" %}">
<a class="tw:no-underline tw:text-2xl tw:p-1" role="button" <a class="no-underline text-2xl p-1" role="button"
data-bs-toggle="dropdown" data-bs-toggle="dropdown"
data-bs-title="{% translate "Add" %}" aria-expanded="false"> data-bs-title="{% translate "Add" %}" aria-expanded="false">
<i class="fa-solid fa-circle-plus fa-fw"></i> <i class="fa-solid fa-circle-plus fa-fw"></i>
</a> </a>
<ul class="tw:dropdown-content tw:menu tw:bg-base-100 tw:rounded-box tw:z-[1] tw:w-52 tw:p-2 tw:shadow"> <ul class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow">
<li><a role="button" <li><a role="button"
hx-get="{% url 'import_profiles_add' %}" hx-get="{% url 'import_profiles_add' %}"
hx-target="#generic-offcanvas">{% trans 'New' %}</a></li> hx-target="#generic-offcanvas">{% trans 'New' %}</a></li>
@@ -23,14 +23,14 @@
{% endspaceless %} {% endspaceless %}
</div> </div>
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-body tw:overflow-x-auto"> <div class="card-body overflow-x-auto">
{% if profiles %} {% if profiles %}
<c-config.search></c-config.search> <c-config.search></c-config.search>
<table class="tw:table tw:table-hover tw:whitespace-nowrap"> <table class="table table-hover whitespace-nowrap">
<thead> <thead>
<tr> <tr>
<th scope="col" class="tw:w-auto"></th> <th scope="col" class="w-auto"></th>
<th scope="col">{% translate 'Name' %}</th> <th scope="col">{% translate 'Name' %}</th>
<th scope="col">{% translate 'Version' %}</th> <th scope="col">{% translate 'Version' %}</th>
</tr> </tr>
@@ -38,30 +38,30 @@
<tbody> <tbody>
{% for profile in profiles %} {% for profile in profiles %}
<tr class="profile"> <tr class="profile">
<td class="tw:w-auto"> <td class="w-auto">
<div class="tw:join" role="group" aria-label="{% translate 'Actions' %}"> <div class="join" role="group" aria-label="{% translate 'Actions' %}">
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item" <a class="btn btn-secondary btn-sm join-item"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Edit" %}" data-bs-title="{% translate "Edit" %}"
hx-get="{% url 'import_profile_edit' profile_id=profile.id %}" hx-get="{% url 'import_profile_edit' profile_id=profile.id %}"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-pencil fa-fw"></i></a> <i class="fa-solid fa-pencil fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-success" <a class="btn btn-secondary btn-sm join-item text-success"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Runs" %}" data-bs-title="{% translate "Runs" %}"
hx-get="{% url 'import_profile_runs_list' profile_id=profile.id %}" hx-get="{% url 'import_profile_runs_list' profile_id=profile.id %}"
hx-target="#persistent-generic-offcanvas-left"> hx-target="#persistent-generic-offcanvas-left">
<i class="fa-solid fa-person-running fa-fw"></i></a> <i class="fa-solid fa-person-running fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-primary" <a class="btn btn-secondary btn-sm join-item text-primary"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Import" %}" data-bs-title="{% translate "Import" %}"
hx-get="{% url 'import_run_add' profile_id=profile.id %}" hx-get="{% url 'import_run_add' profile_id=profile.id %}"
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<i class="fa-solid fa-file-import fa-fw"></i></a> <i class="fa-solid fa-file-import fa-fw"></i></a>
<a class="tw:btn tw:btn-secondary tw:btn-sm tw:join-item tw:text-error" <a class="btn btn-secondary btn-sm join-item text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -6,28 +6,28 @@
{% block body %} {% block body %}
{% if presets %} {% if presets %}
<div id="search" class="tw:mb-3"> <div id="search" class="mb-3">
<label class="tw:w-full"> <label class="w-full">
<input type="search" <input type="search"
class="tw:input tw:input-bordered tw:w-full" class="input input-bordered w-full"
placeholder="{% translate 'Search' %}" placeholder="{% translate 'Search' %}"
_="on input or search _="on input or search
show < .col /> in <#items/> show < .col /> in <#items/>
when its textContent.toLowerCase() contains my value.toLowerCase()"/> when its textContent.toLowerCase() contains my value.toLowerCase()"/>
</label> </label>
</div> </div>
<div class="tw:grid tw:grid-cols-1 tw:gap-4" id="items"> <div class="grid grid-cols-1 gap-4" id="items">
{% for preset in presets %} {% for preset in presets %}
<a class="tw:no-underline" <a class="no-underline"
role="button" role="button"
hx-post="{% url 'import_profiles_add' %}" hx-post="{% url 'import_profiles_add' %}"
hx-vals='{"yaml_config": {{ preset.config }}, "name": "{{ preset.name }}", "version": "{{ preset.schema_version }}", "message": {{ preset.message }}}' hx-vals='{"yaml_config": {{ preset.config }}, "name": "{{ preset.name }}", "version": "{{ preset.schema_version }}", "message": {{ preset.message }}}'
hx-target="#generic-offcanvas"> hx-target="#generic-offcanvas">
<div class="col"> <div class="col">
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title">{{ preset.name }}</h5> <h5 class="card-title">{{ preset.name }}</h5>
<hr> <hr>
<p>{{ preset.description }}</p> <p>{{ preset.description }}</p>
<p>{% trans 'By' %} {{ preset.authors|join:", " }}</p> <p>{% trans 'By' %} {{ preset.authors|join:", " }}</p>
@@ -11,24 +11,24 @@
class="show-loading" class="show-loading"
hx-swap="show:none scroll:none"> hx-swap="show:none scroll:none">
{% if runs %} {% if runs %}
<div class="tw:grid tw:grid-cols-1 tw:gap-4"> <div class="grid grid-cols-1 gap-4">
{% for run in runs %} {% for run in runs %}
<div class="col"> <div class="col">
<div class="tw:card tw:bg-base-100 tw:shadow-xl"> <div class="card bg-base-100 shadow-xl">
<div class="tw:card-header tw:bg-base-200 tw:p-4 tw:text-sm {% if run.status == run.Status.QUEUED %}tw:text-base-content{% elif run.status == run.Status.PROCESSING %}tw:text-warning{% elif run.status == run.Status.FINISHED %}tw:text-success{% else %}tw:text-error{% endif %}"> <div class="card-header bg-base-200 p-4 text-sm {% if run.status == run.Status.QUEUED %}text-base-content{% elif run.status == run.Status.PROCESSING %}text-warning{% elif run.status == run.Status.FINISHED %}text-success{% else %}text-error{% endif %}">
<span><i class="fa-solid {% if run.status == run.Status.QUEUED %}fa-hourglass-half{% elif run.status == run.Status.PROCESSING %}fa-spinner{% elif run.status == run.Status.FINISHED %}fa-check{% else %}fa-xmark{% endif %} fa-fw tw:me-2"></i>{{ run.get_status_display }}</span> <span><i class="fa-solid {% if run.status == run.Status.QUEUED %}fa-hourglass-half{% elif run.status == run.Status.PROCESSING %}fa-spinner{% elif run.status == run.Status.FINISHED %}fa-check{% else %}fa-xmark{% endif %} fa-fw me-2"></i>{{ run.get_status_display }}</span>
</div> </div>
<div class="tw:card-body"> <div class="card-body">
<h5 class="tw:card-title"><i class="fa-solid fa-hashtag tw:me-1 tw:text-xs tw:text-gray-400"></i>{{ run.id }}<span class="tw:text-xs tw:text-gray-400 tw:ms-1">({{ run.file_name }})</span></h5> <h5 class="card-title"><i class="fa-solid fa-hashtag me-1 text-xs text-gray-400"></i>{{ run.id }}<span class="text-xs text-gray-400 ms-1">({{ run.file_name }})</span></h5>
<hr> <hr>
<div class="tw:grid tw:grid-cols-1 tw:md:grid-cols-2 tw:lg:grid-cols-3 tw:w-full tw:gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 w-full gap-4">
<div class="col"> <div class="col">
<div class="tw:flex tw:flex-row"> <div class="flex flex-row">
<div class="tw:flex tw:flex-col"> <div class="flex flex-col">
<div class="tw:text-base-content/70 tw:text-xs tw:font-medium"> <div class="text-base-content/70 text-xs font-medium">
{% trans 'Total Items' %} {% trans 'Total Items' %}
</div> </div>
<div class="tw:text-sm"> <div class="text-sm">
{{ run.total_rows }} {{ run.total_rows }}
</div> </div>
</div> </div>
@@ -36,12 +36,12 @@
</div> </div>
<div class="col"> <div class="col">
<div class="tw:flex tw:flex-row"> <div class="flex flex-row">
<div class="tw:flex tw:flex-col"> <div class="flex flex-col">
<div class="tw:text-base-content/70 tw:text-xs tw:font-medium"> <div class="text-base-content/70 text-xs font-medium">
{% trans 'Processed Items' %} {% trans 'Processed Items' %}
</div> </div>
<div class="tw:text-sm"> <div class="text-sm">
{{ run.processed_rows }} {{ run.processed_rows }}
</div> </div>
</div> </div>
@@ -49,12 +49,12 @@
</div> </div>
<div class="col"> <div class="col">
<div class="tw:flex tw:flex-row"> <div class="flex flex-row">
<div class="tw:flex tw:flex-col"> <div class="flex flex-col">
<div class="tw:text-base-content/70 tw:text-xs tw:font-medium"> <div class="text-base-content/70 text-xs font-medium">
{% trans 'Skipped Items' %} {% trans 'Skipped Items' %}
</div> </div>
<div class="tw:text-sm"> <div class="text-sm">
{{ run.skipped_rows }} {{ run.skipped_rows }}
</div> </div>
</div> </div>
@@ -62,12 +62,12 @@
</div> </div>
<div class="col"> <div class="col">
<div class="tw:flex tw:flex-row"> <div class="flex flex-row">
<div class="tw:flex tw:flex-col"> <div class="flex flex-col">
<div class="tw:text-base-content/70 tw:text-xs tw:font-medium"> <div class="text-base-content/70 text-xs font-medium">
{% trans 'Failed Items' %} {% trans 'Failed Items' %}
</div> </div>
<div class="tw:text-sm"> <div class="text-sm">
{{ run.failed_rows }} {{ run.failed_rows }}
</div> </div>
</div> </div>
@@ -75,12 +75,12 @@
</div> </div>
<div class="col"> <div class="col">
<div class="tw:flex tw:flex-row"> <div class="flex flex-row">
<div class="tw:flex tw:flex-col"> <div class="flex flex-col">
<div class="tw:text-base-content/70 tw:text-xs tw:font-medium"> <div class="text-base-content/70 text-xs font-medium">
{% trans 'Successful Items' %} {% trans 'Successful Items' %}
</div> </div>
<div class="tw:text-sm"> <div class="text-sm">
{{ run.successful_rows }} {{ run.successful_rows }}
</div> </div>
</div> </div>
@@ -89,14 +89,14 @@
</div> </div>
</div> </div>
<div class="tw:card-footer tw:bg-base-200 tw:p-4 tw:text-base-content/70"> <div class="card-footer bg-base-200 p-4 text-base-content/70">
<a class="tw:no-underline tw:text-info" <a class="no-underline text-info"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Logs" %}" data-bs-title="{% translate "Logs" %}"
hx-get="{% url 'import_run_log' profile_id=profile.id run_id=run.id %}" hx-get="{% url 'import_run_log' profile_id=profile.id run_id=run.id %}"
hx-target="#generic-offcanvas"><i class="fa-solid fa-file-lines"></i></a> hx-target="#generic-offcanvas"><i class="fa-solid fa-file-lines"></i></a>
<a class="tw:no-underline tw:text-error" <a class="no-underline text-error"
role="button" role="button"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
data-bs-title="{% translate "Delete" %}" data-bs-title="{% translate "Delete" %}"
@@ -5,8 +5,8 @@
{% block title %}{% translate 'Logs for' %} #{{ run.id }}{% endblock %} {% block title %}{% translate 'Logs for' %} #{{ run.id }}{% endblock %}
{% block body %} {% block body %}
<div class="tw:card tw:bg-base-100 tw:shadow-xl tw:max-h-full tw:overflow-auto"> <div class="card bg-base-100 shadow-xl max-h-full overflow-auto">
<div class="tw:card-body"> <div class="card-body">
{{ run.logs|linebreaks }} {{ run.logs|linebreaks }}
</div> </div>
</div> </div>
+5 -5
View File
@@ -3,14 +3,14 @@
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% load active_link %} {% load active_link %}
<nav class="tw:navbar tw:border-b tw:border-base-300 tw:bg-base-200 tw:flex tw:lg:hidden" hx-boost="true"> <nav class="navbar border-b border-base-300 bg-base-200 flex lg:hidden" hx-boost="true">
<div class="tw:container tw:mx-auto tw:px-4 tw:flex tw:justify-between tw:items-center tw:w-full"> <div class="container mx-auto px-4 flex justify-between items-center w-full">
<a class="tw:text-xl tw:font-bold tw:text-primary" href="{% url 'index' %}"> <a class="text-xl font-bold text-primary" href="{% url 'index' %}">
<img src="{% static 'img/logo-icon.svg' %}" alt="WYGIWYH Logo" height="40" width="40" title="WYGIWYH"/> <img src="{% static 'img/logo-icon.svg' %}" alt="WYGIWYH Logo" height="40" width="40" title="WYGIWYH"/>
</a> </a>
<button class="tw:btn tw:btn-ghost tw:lg:hidden" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebar" <button class="btn btn-ghost lg:hidden" type="button" data-bs-toggle="offcanvas" data-bs-target="#sidebar"
aria-controls="sidebar" aria-label={% translate "Toggle navigation" %}> aria-controls="sidebar" aria-label={% translate "Toggle navigation" %}>
<svg class="tw:w-6 tw:h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg> </svg>
</button> </button>
+2 -2
View File
@@ -166,11 +166,11 @@
{% get_update_check as update_check %} {% get_update_check as update_check %}
{% if update_check.update_available %} {% if update_check.update_available %}
<li class="nav-item my-auto"> <li class="nav-item my-auto">
<a class="badge text-bg-secondary text-decoration-none tw:cursor-pointer" href="https://github.com/eitchtee/WYGIWYH/releases/latest" target="_blank"><i class="fa-solid fa-circle-info fa-fw me-2"></i>v.{{ update_check.latest_version }} {% translate 'is available' %}!</a> <a class="badge text-bg-secondary text-decoration-none cursor-pointer" href="https://github.com/eitchtee/WYGIWYH/releases/latest" target="_blank"><i class="fa-solid fa-circle-info fa-fw me-2"></i>v.{{ update_check.latest_version }} {% translate 'is available' %}!</a>
</li> </li>
{% endif %} {% endif %}
<li class="nav-item"> <li class="nav-item">
<div class="nav-link tw:lg:text-2xl! tw:cursor-pointer" <div class="nav-link lg:text-2xl! cursor-pointer"
data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="{% trans "Calculator" %}" data-bs-toggle="tooltip" data-bs-placement="left" data-bs-title="{% trans "Calculator" %}"
_="on click trigger show on #calculator"> _="on click trigger show on #calculator">
<i class="fa-solid fa-calculator"></i> <i class="fa-solid fa-calculator"></i>
+10 -10
View File
@@ -1,21 +1,21 @@
{% load settings %} {% load settings %}
{% load i18n %} {% load i18n %}
<div class="tw:dropdown tw:dropdown-top tw:dropdown-end"> <div class="dropdown dropdown-top dropdown-end">
<div tabindex="0" role="button" class="tw:btn tw:btn-secondary tw:btn-sm"> <div tabindex="0" role="button" class="btn btn-secondary btn-sm">
<i class="fa-solid fa-cog"></i> <i class="fa-solid fa-cog"></i>
</div> </div>
<ul tabindex="0" class="tw:dropdown-content tw:menu tw:bg-base-100 tw:rounded-box tw:z-[1] tw:w-52 tw:p-2 tw:shadow"> <ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow">
<li><a <li><a
hx-get="{% url 'user_settings' %}" hx-get="{% url 'user_settings' %}"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
role="button"> role="button">
<i class="fa-solid fa-gear tw:me-2 fa-fw"></i>{% translate 'Settings' %}</a></li> <i class="fa-solid fa-gear me-2 fa-fw"></i>{% translate 'Settings' %}</a></li>
<li><a <li><a
hx-get="{% url 'user_edit' pk=request.user.id %}" hx-get="{% url 'user_edit' pk=request.user.id %}"
hx-target="#generic-offcanvas" hx-target="#generic-offcanvas"
role="button"> role="button">
<i class="fa-solid fa-user tw:me-2 fa-fw"></i>{% translate 'Edit profile' %}</a></li> <i class="fa-solid fa-user me-2 fa-fw"></i>{% translate 'Edit profile' %}</a></li>
<hr class="tw:my-1 tw:text-base-content/60"> <hr class="my-1 hr">
{% spaceless %} {% spaceless %}
<li> <li>
<a hx-get="{% url 'toggle_amount_visibility' %}" role="button"> <a hx-get="{% url 'toggle_amount_visibility' %}" role="button">
@@ -38,15 +38,15 @@
</a> </a>
</li> </li>
{% endspaceless %} {% endspaceless %}
<hr class="tw:my-1 tw:text-base-content/60"> <hr class="my-1 hr">
<li> <li>
<a hx-get="{% url 'invalidate_cache' %}" role="button"> <a hx-get="{% url 'invalidate_cache' %}" role="button">
<i class="fa-solid fa-broom tw:me-2 fa-fw"></i>{% translate 'Clear cache' %} <i class="fa-solid fa-broom me-2 fa-fw"></i>{% translate 'Clear cache' %}
</a> </a>
</li> </li>
<li><a href="{% url 'logout' %}"><i class="fa-solid fa-door-open tw:me-2 fa-fw"></i <li><a href="{% url 'logout' %}"><i class="fa-solid fa-door-open me-2 fa-fw"></i
>{% translate 'Logout' %}</a></li> >{% translate 'Logout' %}</a></li>
<hr class="tw:my-1 tw:text-base-content/60"> <hr class="my-1 hr">
<li><a href="https://github.com/eitchtee/WYGIWYH/releases" target="_blank" rel="nofollow">v. {% settings "APP_VERSION" %}</a></li> <li><a href="https://github.com/eitchtee/WYGIWYH/releases" target="_blank" rel="nofollow">v. {% settings "APP_VERSION" %}</a></li>
</ul> </ul>
</div> </div>
+4 -4
View File
@@ -1,4 +1,4 @@
<div id="persistent-generic-offcanvas" class="offcanvas offcanvas-end offcanvas-size-xl tw:z-1100! tw:bg-base-200! tw:text-base-content!" <div id="persistent-generic-offcanvas" class="offcanvas offcanvas-end offcanvas-size-xl z-1100!"
data-bs-backdrop="static" data-bs-backdrop="static"
tabindex="-1" tabindex="-1"
_="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end _="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end
@@ -6,7 +6,7 @@
on force_hide_offcanvas call bootstrap.Offcanvas.getOrCreateInstance(me).hide() end on force_hide_offcanvas call bootstrap.Offcanvas.getOrCreateInstance(me).hide() end
on hidden.bs.offcanvas set my innerHTML to '' end"> on hidden.bs.offcanvas set my innerHTML to '' end">
</div> </div>
<div id="persistent-generic-offcanvas-left" class="offcanvas offcanvas-start offcanvas-size-xl tw:z-1100! tw:bg-base-200! tw:text-base-content!" <div id="persistent-generic-offcanvas-left" class="offcanvas offcanvas-start offcanvas-size-xl z-1100!"
data-bs-backdrop="static" data-bs-backdrop="static"
tabindex="-1" tabindex="-1"
_="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end _="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end
@@ -16,7 +16,7 @@
</div> </div>
<div id="generic-offcanvas" class="offcanvas offcanvas-end offcanvas-size-xl tw:z-1100! tw:bg-base-200! tw:text-base-content!" <div id="generic-offcanvas" class="offcanvas offcanvas-end offcanvas-size-xl z-1100!"
data-bs-backdrop="static" data-bs-backdrop="static"
tabindex="-1" tabindex="-1"
_="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end _="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end
@@ -24,7 +24,7 @@
on htmx:beforeOnLoad[detail.boosted] call bootstrap.Offcanvas.getOrCreateInstance(me).hide() on htmx:beforeOnLoad[detail.boosted] call bootstrap.Offcanvas.getOrCreateInstance(me).hide()
on hidden.bs.offcanvas set my innerHTML to '' end"> on hidden.bs.offcanvas set my innerHTML to '' end">
</div> </div>
<div id="generic-offcanvas-left" class="offcanvas offcanvas-start offcanvas-size-xl tw:z-1100! tw:bg-base-200! tw:text-base-content!" <div id="generic-offcanvas-left" class="offcanvas offcanvas-start offcanvas-size-xl z-1100!"
data-bs-backdrop="static" data-bs-backdrop="static"
tabindex="-1" tabindex="-1"
_="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end _="on htmx:afterSettle call bootstrap.Offcanvas.getOrCreateInstance(me).show() end

Some files were not shown because too many files have changed in this diff Show More