mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-03-19 07:54:08 +01:00
feat: add Unit Price Calculator
This commit is contained in:
@@ -46,4 +46,5 @@ urlpatterns = [
|
||||
path("", include("apps.rules.urls")),
|
||||
path("", include("apps.calendar_view.urls")),
|
||||
path("", include("apps.dca.urls")),
|
||||
path("", include("apps.mini_tools.urls")),
|
||||
]
|
||||
|
||||
0
app/apps/mini_tools/__init__.py
Normal file
0
app/apps/mini_tools/__init__.py
Normal file
3
app/apps/mini_tools/admin.py
Normal file
3
app/apps/mini_tools/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
app/apps/mini_tools/apps.py
Normal file
6
app/apps/mini_tools/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MiniToolsConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "apps.mini_tools"
|
||||
0
app/apps/mini_tools/migrations/__init__.py
Normal file
0
app/apps/mini_tools/migrations/__init__.py
Normal file
3
app/apps/mini_tools/models.py
Normal file
3
app/apps/mini_tools/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
3
app/apps/mini_tools/tests.py
Normal file
3
app/apps/mini_tools/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
11
app/apps/mini_tools/urls.py
Normal file
11
app/apps/mini_tools/urls.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
"tools/unit-price-calculator/",
|
||||
views.unit_price_calculator,
|
||||
name="unit_price_calculator",
|
||||
),
|
||||
]
|
||||
7
app/apps/mini_tools/views.py
Normal file
7
app/apps/mini_tools/views.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render
|
||||
|
||||
|
||||
@login_required
|
||||
def unit_price_calculator(request):
|
||||
return render(request, "mini_tools/unit_price_calculator.html")
|
||||
@@ -8,8 +8,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-11-12 14:32+0000\n"
|
||||
"PO-Revision-Date: 2024-11-12 11:39-0300\n"
|
||||
"POT-Creation-Date: 2024-11-15 14:47+0000\n"
|
||||
"PO-Revision-Date: 2024-11-15 11:47-0300\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: pt_BR\n"
|
||||
@@ -42,10 +42,11 @@ msgstr "Atualizar"
|
||||
#: templates/accounts/fragments/list.html:9
|
||||
#: templates/categories/fragments/list.html:9
|
||||
#: templates/currencies/fragments/list.html:9
|
||||
#: templates/dca/fragments/strategy/details.html:101
|
||||
#: templates/dca/fragments/strategy/details.html:16
|
||||
#: templates/dca/fragments/strategy/list.html:9
|
||||
#: templates/exchange_rates/fragments/list.html:10
|
||||
#: templates/installment_plans/fragments/list.html:9
|
||||
#: templates/mini_tools/unit_price_calculator.html:174
|
||||
#: templates/recurring_transactions/fragments/list.html:9
|
||||
#: templates/rules/fragments/list.html:9 templates/tags/fragments/list.html:9
|
||||
msgid "Add"
|
||||
@@ -77,7 +78,7 @@ msgstr "Categoria"
|
||||
msgid "Tags"
|
||||
msgstr "Tags"
|
||||
|
||||
#: apps/accounts/models.py:8 apps/accounts/models.py:20 apps/dca/models.py:11
|
||||
#: apps/accounts/models.py:8 apps/accounts/models.py:20 apps/dca/models.py:14
|
||||
#: apps/rules/models.py:9 apps/transactions/models.py:19
|
||||
#: apps/transactions/models.py:32
|
||||
#: templates/account_groups/fragments/list.html:23
|
||||
@@ -279,6 +280,7 @@ msgid "Remove"
|
||||
msgstr "Remover"
|
||||
|
||||
#: apps/common/widgets/tom_select.py:14
|
||||
#: templates/mini_tools/unit_price_calculator.html:186
|
||||
#: templates/transactions/pages/transactions.html:18
|
||||
msgid "Clear"
|
||||
msgstr "Limpar"
|
||||
@@ -359,60 +361,60 @@ msgstr "Taxa de câmbio atualizada com sucesso"
|
||||
msgid "Exchange rate deleted successfully"
|
||||
msgstr "Taxa de câmbio apagada com sucesso"
|
||||
|
||||
#: apps/dca/models.py:14
|
||||
#: apps/dca/models.py:17
|
||||
msgid "Target Currency"
|
||||
msgstr "Moeda de destino"
|
||||
|
||||
#: apps/dca/models.py:20
|
||||
#: apps/dca/models.py:23
|
||||
msgid "Payment Currency"
|
||||
msgstr "Moeda de pagamento"
|
||||
|
||||
#: apps/dca/models.py:24 apps/dca/models.py:98 apps/rules/models.py:26
|
||||
#: apps/dca/models.py:27 apps/dca/models.py:167 apps/rules/models.py:26
|
||||
#: apps/transactions/forms.py:209 apps/transactions/models.py:72
|
||||
#: apps/transactions/models.py:188 apps/transactions/models.py:356
|
||||
msgid "Notes"
|
||||
msgstr "Notas"
|
||||
|
||||
#: apps/dca/models.py:29
|
||||
#: apps/dca/models.py:32
|
||||
msgid "DCA Strategy"
|
||||
msgstr "Estratégia CMP"
|
||||
|
||||
#: apps/dca/models.py:30
|
||||
#: apps/dca/models.py:33
|
||||
msgid "DCA Strategies"
|
||||
msgstr "Estratégias CMP"
|
||||
|
||||
#: apps/dca/models.py:73
|
||||
#: apps/dca/models.py:142
|
||||
msgid "Strategy"
|
||||
msgstr "Estratégia"
|
||||
|
||||
#: apps/dca/models.py:75 apps/rules/models.py:22 apps/transactions/forms.py:197
|
||||
#: apps/transactions/models.py:61
|
||||
#: templates/dca/fragments/strategy/details.html:116
|
||||
#: apps/dca/models.py:144 apps/rules/models.py:22
|
||||
#: apps/transactions/forms.py:197 apps/transactions/models.py:61
|
||||
#: templates/dca/fragments/strategy/details.html:31
|
||||
#: templates/exchange_rates/fragments/table.html:14
|
||||
msgid "Date"
|
||||
msgstr "Data"
|
||||
|
||||
#: apps/dca/models.py:77 templates/dca/fragments/strategy/details.html:117
|
||||
#: apps/dca/models.py:146 templates/dca/fragments/strategy/details.html:32
|
||||
msgid "Amount Paid"
|
||||
msgstr "Quantia paga"
|
||||
|
||||
#: apps/dca/models.py:80 templates/dca/fragments/strategy/details.html:118
|
||||
#: apps/dca/models.py:149 templates/dca/fragments/strategy/details.html:33
|
||||
msgid "Amount Received"
|
||||
msgstr "Quantia recebida"
|
||||
|
||||
#: apps/dca/models.py:88
|
||||
#: apps/dca/models.py:157
|
||||
msgid "Expense Transaction"
|
||||
msgstr "Transação de saída"
|
||||
|
||||
#: apps/dca/models.py:96
|
||||
#: apps/dca/models.py:165
|
||||
msgid "Income Transaction"
|
||||
msgstr "Transação de entrada"
|
||||
|
||||
#: apps/dca/models.py:103
|
||||
#: apps/dca/models.py:172
|
||||
msgid "DCA Entry"
|
||||
msgstr "Entrada CMP"
|
||||
|
||||
#: apps/dca/models.py:104
|
||||
#: apps/dca/models.py:173
|
||||
msgid "DCA Entries"
|
||||
msgstr "Entradas CMP"
|
||||
|
||||
@@ -428,15 +430,15 @@ msgstr "Estratégia CMP atualizada com sucesso"
|
||||
msgid "DCA strategy deleted successfully"
|
||||
msgstr "Estratégia CMP apagada com sucesso"
|
||||
|
||||
#: apps/dca/views.py:169
|
||||
#: apps/dca/views.py:172
|
||||
msgid "Entry added successfully"
|
||||
msgstr "Entrada adicionada com sucesso"
|
||||
|
||||
#: apps/dca/views.py:196
|
||||
#: apps/dca/views.py:199
|
||||
msgid "Entry updated successfully"
|
||||
msgstr "Entrada atualizada com sucesso"
|
||||
|
||||
#: apps/dca/views.py:223
|
||||
#: apps/dca/views.py:226
|
||||
msgid "Entry deleted successfully"
|
||||
msgstr "Entrada apagada com sucesso"
|
||||
|
||||
@@ -798,27 +800,27 @@ msgstr "Parcelamento atualizado com sucesso"
|
||||
msgid "Installment Plan deleted successfully"
|
||||
msgstr "Parcelamento apagado com sucesso"
|
||||
|
||||
#: apps/transactions/views/recurring_transactions.py:115
|
||||
#: apps/transactions/views/recurring_transactions.py:114
|
||||
msgid "Recurring Transaction added successfully"
|
||||
msgstr "Transação Recorrente adicionada com sucesso"
|
||||
|
||||
#: apps/transactions/views/recurring_transactions.py:145
|
||||
#: apps/transactions/views/recurring_transactions.py:144
|
||||
msgid "Recurring Transaction updated successfully"
|
||||
msgstr "Transação Recorrente atualizada com sucesso"
|
||||
|
||||
#: apps/transactions/views/recurring_transactions.py:175
|
||||
#: apps/transactions/views/recurring_transactions.py:174
|
||||
msgid "Recurring transaction unpaused successfully"
|
||||
msgstr "Transação Recorrente despausada com sucesso"
|
||||
|
||||
#: apps/transactions/views/recurring_transactions.py:178
|
||||
#: apps/transactions/views/recurring_transactions.py:177
|
||||
msgid "Recurring transaction paused successfully"
|
||||
msgstr "Transação Recorrente pausada com sucesso"
|
||||
|
||||
#: apps/transactions/views/recurring_transactions.py:201
|
||||
#: apps/transactions/views/recurring_transactions.py:200
|
||||
msgid "Recurring transaction finished successfully"
|
||||
msgstr "Transação Recorrente finalizada com sucesso"
|
||||
|
||||
#: apps/transactions/views/recurring_transactions.py:222
|
||||
#: apps/transactions/views/recurring_transactions.py:221
|
||||
msgid "Recurring Transaction deleted successfully"
|
||||
msgstr "Transação Recorrente apagada com sucesso"
|
||||
|
||||
@@ -959,7 +961,7 @@ msgstr "Editar grupo de conta"
|
||||
#: templates/accounts/fragments/list.html:34
|
||||
#: templates/categories/fragments/list.html:31
|
||||
#: templates/currencies/fragments/list.html:31
|
||||
#: templates/dca/fragments/strategy/details.html:127
|
||||
#: templates/dca/fragments/strategy/details.html:42
|
||||
#: templates/exchange_rates/fragments/table.html:23
|
||||
#: templates/installment_plans/fragments/table.html:21
|
||||
#: templates/recurring_transactions/fragments/table.html:23
|
||||
@@ -972,7 +974,7 @@ msgstr "Ações"
|
||||
#: templates/categories/fragments/list.html:35
|
||||
#: templates/cotton/transaction/item.html:99
|
||||
#: templates/currencies/fragments/list.html:35
|
||||
#: templates/dca/fragments/strategy/details.html:131
|
||||
#: templates/dca/fragments/strategy/details.html:46
|
||||
#: templates/dca/fragments/strategy/list.html:31
|
||||
#: templates/exchange_rates/fragments/table.html:27
|
||||
#: templates/installment_plans/fragments/table.html:25
|
||||
@@ -989,10 +991,11 @@ msgstr "Editar"
|
||||
#: templates/cotton/transaction/item.html:106
|
||||
#: templates/cotton/ui/transactions_action_bar.html:50
|
||||
#: templates/currencies/fragments/list.html:42
|
||||
#: templates/dca/fragments/strategy/details.html:139
|
||||
#: templates/dca/fragments/strategy/details.html:54
|
||||
#: templates/dca/fragments/strategy/list.html:39
|
||||
#: templates/exchange_rates/fragments/table.html:35
|
||||
#: templates/installment_plans/fragments/table.html:54
|
||||
#: templates/mini_tools/unit_price_calculator.html:18
|
||||
#: templates/recurring_transactions/fragments/table.html:89
|
||||
#: templates/rules/fragments/list.html:42
|
||||
#: templates/rules/fragments/transaction_rule/view.html:56
|
||||
@@ -1006,7 +1009,7 @@ msgstr "Apagar"
|
||||
#: templates/cotton/transaction/item.html:110
|
||||
#: templates/cotton/ui/transactions_action_bar.html:52
|
||||
#: templates/currencies/fragments/list.html:46
|
||||
#: templates/dca/fragments/strategy/details.html:144
|
||||
#: templates/dca/fragments/strategy/details.html:59
|
||||
#: templates/dca/fragments/strategy/list.html:43
|
||||
#: templates/exchange_rates/fragments/table.html:40
|
||||
#: templates/installment_plans/fragments/table.html:46
|
||||
@@ -1027,7 +1030,7 @@ msgstr "Tem certeza?"
|
||||
#: templates/cotton/transaction/item.html:111
|
||||
#: templates/cotton/ui/transactions_action_bar.html:53
|
||||
#: templates/currencies/fragments/list.html:47
|
||||
#: templates/dca/fragments/strategy/details.html:145
|
||||
#: templates/dca/fragments/strategy/details.html:60
|
||||
#: templates/dca/fragments/strategy/list.html:44
|
||||
#: templates/exchange_rates/fragments/table.html:41
|
||||
#: templates/rules/fragments/list.html:47
|
||||
@@ -1041,7 +1044,7 @@ msgstr "Você não será capaz de reverter isso!"
|
||||
#: templates/categories/fragments/list.html:48
|
||||
#: templates/cotton/transaction/item.html:112
|
||||
#: templates/currencies/fragments/list.html:48
|
||||
#: templates/dca/fragments/strategy/details.html:146
|
||||
#: templates/dca/fragments/strategy/details.html:61
|
||||
#: templates/dca/fragments/strategy/list.html:45
|
||||
#: templates/exchange_rates/fragments/table.html:42
|
||||
#: templates/installment_plans/fragments/table.html:60
|
||||
@@ -1198,7 +1201,7 @@ msgstr "Marcar como não pago"
|
||||
msgid "Yes, delete them!"
|
||||
msgstr "Sim, apague!"
|
||||
|
||||
#: templates/cotton/ui/transactions_action_bar.html:72
|
||||
#: templates/cotton/ui/transactions_action_bar.html:73
|
||||
msgid "copied!"
|
||||
msgstr "copiado!"
|
||||
|
||||
@@ -1230,62 +1233,91 @@ msgstr "Editar entrada CMP"
|
||||
msgid "Add DCA strategy"
|
||||
msgstr "Adicionar estratégia CMP"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:11
|
||||
msgid "Total Invested"
|
||||
msgstr "Total investido"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:25
|
||||
msgid "Total Received"
|
||||
msgstr "Total recebido"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:39
|
||||
msgid "Average Entry Price"
|
||||
msgstr "Preço médio de entrada"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:53
|
||||
msgid "Current Total Value"
|
||||
msgstr "Valor total atual"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:67
|
||||
msgid "Total P/L"
|
||||
msgstr "P/L total"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:82
|
||||
#, python-format
|
||||
msgid "Total %% P/L"
|
||||
msgstr "P/L%% Total"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:97
|
||||
#: templates/dca/fragments/strategy/details.html:12
|
||||
msgid "Entries"
|
||||
msgstr "Entradas"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:119
|
||||
#: templates/dca/fragments/strategy/details.html:34
|
||||
msgid "Current Value"
|
||||
msgstr "Valor atual"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:120
|
||||
#: templates/dca/fragments/strategy/details.html:35
|
||||
msgid "P/L"
|
||||
msgstr "P/L"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:180
|
||||
#: templates/dca/fragments/strategy/details.html:103
|
||||
msgid "No entries for this DCA"
|
||||
msgstr "Nenhuma entrada neste CMP"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:181
|
||||
#: templates/dca/fragments/strategy/details.html:104
|
||||
#: templates/monthly_overview/fragments/list.html:41
|
||||
#: templates/transactions/fragments/list_all.html:40
|
||||
msgid "Try adding one"
|
||||
msgstr "Tente adicionar uma"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:192
|
||||
msgid "Performance Over Time"
|
||||
msgstr "Desempenho ao longo do tempo"
|
||||
#: templates/dca/fragments/strategy/details.html:114
|
||||
msgid "Total Invested"
|
||||
msgstr "Total investido"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:208
|
||||
#: templates/dca/fragments/strategy/details.html:128
|
||||
msgid "Total Received"
|
||||
msgstr "Total recebido"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:142
|
||||
msgid "Current Total Value"
|
||||
msgstr "Valor total atual"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:156
|
||||
msgid "Average Entry Price"
|
||||
msgstr "Preço médio de entrada"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:170
|
||||
msgid "Total P/L"
|
||||
msgstr "P/L total"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:186
|
||||
#, python-format
|
||||
msgid "Total %% P/L"
|
||||
msgstr "P/L%% Total"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:205
|
||||
#, python-format
|
||||
msgid "P/L %%"
|
||||
msgstr "P/L %%"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:267
|
||||
msgid "Performance Over Time"
|
||||
msgstr "Desempenho ao longo do tempo"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:285
|
||||
msgid "Entry Price"
|
||||
msgstr "Preço de Entrada"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:293
|
||||
msgid "Current Price"
|
||||
msgstr "Preço atual"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:301
|
||||
msgid "Amount Bought"
|
||||
msgstr "Quantia comprada"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:369
|
||||
msgid "Entry Price vs Current Price"
|
||||
msgstr "Preço de Entrada vs Preço Atual"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:385
|
||||
msgid "Days Between Investments"
|
||||
msgstr "Dias entre investimentos"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:432
|
||||
msgid "Investment Frequency"
|
||||
msgstr "Frequência de Investimento"
|
||||
|
||||
#: templates/dca/fragments/strategy/details.html:434
|
||||
msgid "The straighter the blue line, the more consistent your DCA strategy is."
|
||||
msgstr ""
|
||||
"Quanto mais reta for a linha azul, mais consistente é sua estratégia de CMP."
|
||||
|
||||
#: templates/dca/fragments/strategy/edit.html:5
|
||||
msgid "Edit DCA strategy"
|
||||
msgstr "Editar estratégia CMP"
|
||||
@@ -1295,6 +1327,10 @@ msgstr "Editar estratégia CMP"
|
||||
msgid "Dollar Cost Average Strategies"
|
||||
msgstr "Estratégias de Custo Médio Ponderado"
|
||||
|
||||
#: templates/dca/pages/strategy_detail_index.html:4
|
||||
msgid "Dollar Cost Average Strategy"
|
||||
msgstr "Estratégia de Custo Médio Ponderado"
|
||||
|
||||
#: templates/exchange_rates/fragments/add.html:5
|
||||
msgid "Add exchange rate"
|
||||
msgstr "Adicionar taxa de câmbio"
|
||||
@@ -1426,6 +1462,35 @@ msgstr ""
|
||||
"Algo deu errado ao carregar seus dados. Tente recarregar a página ou "
|
||||
"verifique o console para obter mais informações."
|
||||
|
||||
#: templates/mini_tools/unit_price_calculator.html:5
|
||||
#: templates/mini_tools/unit_price_calculator.html:10
|
||||
msgid "Unit Price Calculator"
|
||||
msgstr "Calculadora de preço unitário"
|
||||
|
||||
#: templates/mini_tools/unit_price_calculator.html:27
|
||||
#: templates/mini_tools/unit_price_calculator.html:112
|
||||
#: templates/mini_tools/unit_price_calculator.html:137
|
||||
msgid "Item price"
|
||||
msgstr "Preço"
|
||||
|
||||
#: templates/mini_tools/unit_price_calculator.html:33
|
||||
#: templates/mini_tools/unit_price_calculator.html:118
|
||||
#: templates/mini_tools/unit_price_calculator.html:143
|
||||
msgid "Item amount"
|
||||
msgstr "Quantidade"
|
||||
|
||||
#: templates/mini_tools/unit_price_calculator.html:38
|
||||
#: templates/mini_tools/unit_price_calculator.html:123
|
||||
#: templates/mini_tools/unit_price_calculator.html:148
|
||||
msgid "Unit price"
|
||||
msgstr "Preço unitário"
|
||||
|
||||
#: templates/mini_tools/unit_price_calculator.html:106
|
||||
#: templates/mini_tools/unit_price_calculator.html:131
|
||||
#: templates/mini_tools/unit_price_calculator.html:170
|
||||
msgid "Item"
|
||||
msgstr "Item"
|
||||
|
||||
#: templates/monthly_overview/fragments/list.html:40
|
||||
msgid "No transactions this month"
|
||||
msgstr "Nenhuma transação neste mês"
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle {% active_link views='dca_strategy_index||dca_strategy_detail_index' %}"
|
||||
<a class="nav-link dropdown-toggle {% active_link views='dca_strategy_index||dca_strategy_detail_index||unit_price_calculator' %}"
|
||||
href="#" role="button"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
@@ -67,6 +67,9 @@
|
||||
<li><a class="dropdown-item {% active_link views='dca_strategy_index||dca_strategy_detail_index' %}"
|
||||
href="{% url 'dca_strategy_index' %}">{% translate 'Dollar Cost Average Tracker' %}</a></li>
|
||||
<li>
|
||||
<li><a class="dropdown-item {% active_link views='unit_price_calculator' %}"
|
||||
href="{% url 'unit_price_calculator' %}">{% translate 'Unit Price Calculator' %}</a></li>
|
||||
<li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
|
||||
@@ -13,4 +13,8 @@ end
|
||||
on htmx:afterSettle
|
||||
call initTooltips(event.detail.target)
|
||||
end
|
||||
|
||||
on tooltips
|
||||
call initTooltips(body)
|
||||
end
|
||||
</script>
|
||||
|
||||
179
app/templates/mini_tools/unit_price_calculator.html
Normal file
179
app/templates/mini_tools/unit_price_calculator.html
Normal file
@@ -0,0 +1,179 @@
|
||||
{% extends "layouts/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load webpack_loader %}
|
||||
|
||||
{% block title %}{% translate 'Unit Price Calculator' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container px-md-3 py-3 column-gap-5">
|
||||
<div class="tw-text-3xl fw-bold font-monospace tw-w-full mb-3">
|
||||
<div>{% translate 'Unit Price Calculator' %}</div>
|
||||
</div>
|
||||
<div class="card mb-3 d-none" id="card-placeholder">
|
||||
<div class="card-header d-flex flex-row justify-content-between">
|
||||
<h5 class="title flex-grow-1"></h5>
|
||||
<button class="btn btn-secondary btn-sm text-danger"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
_="on click remove the closest .card to me then trigger update on #items then call bootstrap.Tooltip.getOrCreateInstance(me).dispose()">
|
||||
<i class="fa-solid fa-trash fa-fw"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row gy-3">
|
||||
<div class="col-lg">
|
||||
<div>
|
||||
<label for="price" class="form-label">{% trans 'Item price' %}</label>
|
||||
<input type="number" inputmode="decimal" class="form-control item-price" id="price">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg">
|
||||
<div>
|
||||
<label for="amount" class="form-label">{% trans 'Item amount' %}</label>
|
||||
<input type="number" inputmode="decimal" class="form-control item-amount" id="amount">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg">
|
||||
<label class="form-label">{% trans 'Unit price' %}</label>
|
||||
<div class="unit-price tw-text-xl" data-amount="0">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="items" _="on input or update
|
||||
for card in <.card />
|
||||
set price to card.querySelector('.item-price').value
|
||||
set amount to card.querySelector('.item-amount').value
|
||||
|
||||
// Calculate and format unit price
|
||||
if amount > 0 set unitPrice to (price / amount) else set unitPrice to 0 end
|
||||
set formattedUnitPrice to unitPrice.toLocaleString(undefined, {minimumFractionDigits: 0, maximumFractionDigits: 40})
|
||||
|
||||
set card.querySelector('.unit-price').innerHTML to formattedUnitPrice
|
||||
call card.querySelector('.unit-price').setAttribute('data-amount', unitPrice)
|
||||
end
|
||||
|
||||
then
|
||||
|
||||
// Remove existing highlight classes from all unit prices
|
||||
for unitPriceEl in <.unit-price/>
|
||||
remove .bg-danger-subtle from the closest .card to unitPriceEl
|
||||
remove .bg-success-subtle from the closest .card to unitPriceEl
|
||||
end
|
||||
|
||||
// Get all unit prices and find min/max
|
||||
set unitPrices to <.card:not(#card-placeholder) .unit-price/>
|
||||
set unitPricesAmounts to <.card:not(#card-placeholder) .unit-price/> @data-amount
|
||||
js(unitPricesAmounts)
|
||||
unitPricesAmounts = unitPricesAmounts.filter(element => element !== '0')
|
||||
return Math.min(...unitPricesAmounts)
|
||||
end
|
||||
set minAmount to it
|
||||
js(unitPricesAmounts)
|
||||
unitPricesAmounts = unitPricesAmounts.filter(element => element !== '0')
|
||||
return Math.max(...unitPricesAmounts)
|
||||
end
|
||||
set maxAmount to it
|
||||
|
||||
if maxAmount and minAmount
|
||||
for unitPriceEl in unitPrices
|
||||
set amount to parseFloat(unitPriceEl.getAttribute('data-amount'))
|
||||
if amount == minAmount
|
||||
add .bg-success-subtle to the closest .card to unitPriceEl
|
||||
continue
|
||||
end
|
||||
if amount == maxAmount
|
||||
add .bg-danger-subtle to the closest .card to unitPriceEl
|
||||
end
|
||||
end
|
||||
end
|
||||
end">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5>{% trans "Item" %} A</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row gy-3">
|
||||
<div class="col-lg">
|
||||
<div>
|
||||
<label for="price" class="form-label">{% trans 'Item price' %}</label>
|
||||
<input type="number" inputmode="decimal" class="form-control item-price" id="price">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg">
|
||||
<div>
|
||||
<label for="amount" class="form-label">{% trans 'Item amount' %}</label>
|
||||
<input type="number" inputmode="decimal" class="form-control item-amount" id="amount">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg">
|
||||
<label class="form-label">{% trans 'Unit price' %}</label>
|
||||
<div class="unit-price tw-text-xl" data-amount="0">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<h5>{% trans "Item" %} B</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row gy-3">
|
||||
<div class="col-lg">
|
||||
<div>
|
||||
<label for="price" class="form-label">{% trans 'Item price' %}</label>
|
||||
<input type="number" inputmode="decimal" class="form-control item-price" id="price">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg">
|
||||
<div>
|
||||
<label for="amount" class="form-label">{% trans 'Item amount' %}</label>
|
||||
<input type="number" inputmode="decimal" class="form-control item-amount" id="amount">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg">
|
||||
<label class="form-label">{% trans 'Unit price' %}</label>
|
||||
<div class="unit-price tw-text-xl" data-amount="0">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-lg-8">
|
||||
<button class="btn btn-outline-primary w-100"
|
||||
_="on click
|
||||
get #card-placeholder
|
||||
set newCard to it.cloneNode(true)
|
||||
remove @id from newCard
|
||||
remove .d-none from newCard then
|
||||
set itemCount to <#items .card/>'s length
|
||||
if itemCount < 26
|
||||
set letter to String.fromCharCode(65 + itemCount)
|
||||
else
|
||||
set letter to String.fromCharCode(65 + Math.floor((itemCount - 26) / 26)) + String.fromCharCode(65 + ((itemCount - 26) mod 26))
|
||||
end
|
||||
set newCard.querySelector('.title').innerHTML to `{% trans "Item" %} ${letter}`
|
||||
put newCard as HTML at the end of #items
|
||||
trigger tooltips on body
|
||||
end">
|
||||
{% trans 'Add' %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-lg-4 mt-3 mt-lg-0">
|
||||
<button class="btn btn-outline-danger w-100"
|
||||
_="on click
|
||||
for el in <.item-price, .item-amount />
|
||||
set card to the closest .card to el
|
||||
set el's value to ''
|
||||
end
|
||||
trigger update on #items
|
||||
end">
|
||||
{% trans 'Clear' %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -4,6 +4,7 @@ import Alpine from "alpinejs";
|
||||
import mask from '@alpinejs/mask';
|
||||
|
||||
window.Alpine = Alpine;
|
||||
window._hyperscript = _hyperscript;
|
||||
|
||||
Alpine.plugin(mask);
|
||||
Alpine.start();
|
||||
|
||||
Reference in New Issue
Block a user