mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-02-26 01:14:50 +01:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10a0ac42a2 | ||
|
|
1b47c12a22 | ||
|
|
091f73bf8d | ||
|
|
73fe17de64 | ||
|
|
52af1b2260 | ||
|
|
8efa087aee | ||
|
|
6f69f15474 | ||
|
|
905e80cffe | ||
|
|
baae6bb96a | ||
|
|
f5132e24bd | ||
|
|
41303f39a0 | ||
|
|
0fc8b0ee49 | ||
|
|
037014d024 |
@@ -1,3 +1,4 @@
|
||||
from .transactions import *
|
||||
from .accounts import *
|
||||
from .currencies import *
|
||||
from .dca import *
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from rest_framework import serializers
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from apps.api.serializers.currencies import CurrencySerializer
|
||||
from apps.accounts.models import AccountGroup, Account
|
||||
@@ -6,6 +7,8 @@ from apps.currencies.models import Currency
|
||||
|
||||
|
||||
class AccountGroupSerializer(serializers.ModelSerializer):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
class Meta:
|
||||
model = AccountGroup
|
||||
fields = "__all__"
|
||||
@@ -31,6 +34,8 @@ class AccountSerializer(serializers.ModelSerializer):
|
||||
allow_null=True,
|
||||
)
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
class Meta:
|
||||
model = Account
|
||||
fields = [
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from rest_framework import serializers
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from apps.currencies.models import Currency, ExchangeRate
|
||||
|
||||
|
||||
class CurrencySerializer(serializers.ModelSerializer):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
class Meta:
|
||||
model = Currency
|
||||
fields = "__all__"
|
||||
@@ -24,6 +28,8 @@ class ExchangeRateSerializer(serializers.ModelSerializer):
|
||||
queryset=Currency.objects.all(), source="to_currency", write_only=True
|
||||
)
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
class Meta:
|
||||
model = ExchangeRate
|
||||
fields = "__all__"
|
||||
|
||||
85
app/apps/api/serializers/dca.py
Normal file
85
app/apps/api/serializers/dca.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from rest_framework import serializers
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from apps.dca.models import DCAEntry, DCAStrategy
|
||||
|
||||
|
||||
class DCAEntrySerializer(serializers.ModelSerializer):
|
||||
profit_loss = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
profit_loss_percentage = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
current_value = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
entry_price = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
class Meta:
|
||||
model = DCAEntry
|
||||
fields = [
|
||||
"id",
|
||||
"strategy",
|
||||
"date",
|
||||
"amount_paid",
|
||||
"amount_received",
|
||||
"notes",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"profit_loss",
|
||||
"profit_loss_percentage",
|
||||
"current_value",
|
||||
"entry_price",
|
||||
]
|
||||
read_only_fields = ["created_at", "updated_at"]
|
||||
|
||||
|
||||
class DCAStrategySerializer(serializers.ModelSerializer):
|
||||
entries = DCAEntrySerializer(many=True, read_only=True)
|
||||
total_invested = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
total_received = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
average_entry_price = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
total_entries = serializers.IntegerField(read_only=True)
|
||||
current_total_value = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
total_profit_loss = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
total_profit_loss_percentage = serializers.DecimalField(
|
||||
max_digits=42, decimal_places=30, read_only=True
|
||||
)
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
class Meta:
|
||||
model = DCAStrategy
|
||||
fields = [
|
||||
"id",
|
||||
"name",
|
||||
"target_currency",
|
||||
"payment_currency",
|
||||
"notes",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"entries",
|
||||
"total_invested",
|
||||
"total_received",
|
||||
"average_entry_price",
|
||||
"total_entries",
|
||||
"current_total_value",
|
||||
"total_profit_loss",
|
||||
"total_profit_loss_percentage",
|
||||
]
|
||||
read_only_fields = ["created_at", "updated_at"]
|
||||
@@ -19,6 +19,7 @@ from apps.transactions.models import (
|
||||
TransactionTag,
|
||||
InstallmentPlan,
|
||||
TransactionEntity,
|
||||
RecurringTransaction,
|
||||
)
|
||||
|
||||
|
||||
@@ -47,11 +48,77 @@ class TransactionEntitySerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class InstallmentPlanSerializer(serializers.ModelSerializer):
|
||||
category = TransactionCategoryField(required=False)
|
||||
tags = TransactionTagField(required=False)
|
||||
entities = TransactionEntityField(required=False)
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
class Meta:
|
||||
model = InstallmentPlan
|
||||
fields = "__all__"
|
||||
fields = [
|
||||
"id",
|
||||
"account",
|
||||
"type",
|
||||
"description",
|
||||
"number_of_installments",
|
||||
"installment_start",
|
||||
"installment_total_number",
|
||||
"start_date",
|
||||
"reference_date",
|
||||
"end_date",
|
||||
"recurrence",
|
||||
"installment_amount",
|
||||
"category",
|
||||
"tags",
|
||||
"entities",
|
||||
"notes",
|
||||
]
|
||||
read_only_fields = ["installment_total_number", "end_date"]
|
||||
|
||||
def create(self, validated_data):
|
||||
instance = super().create(validated_data)
|
||||
instance.create_transactions()
|
||||
return instance
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
instance = super().update(instance, validated_data)
|
||||
instance.update_transactions()
|
||||
return instance
|
||||
|
||||
|
||||
class RecurringTransactionSerializer(serializers.ModelSerializer):
|
||||
category = TransactionCategoryField(required=False)
|
||||
tags = TransactionTagField(required=False)
|
||||
entities = TransactionEntityField(required=False)
|
||||
|
||||
class Meta:
|
||||
model = RecurringTransaction
|
||||
fields = [
|
||||
"id",
|
||||
"is_paused",
|
||||
"account",
|
||||
"type",
|
||||
"amount",
|
||||
"description",
|
||||
"category",
|
||||
"tags",
|
||||
"entities",
|
||||
"notes",
|
||||
"reference_date",
|
||||
"start_date",
|
||||
"end_date",
|
||||
"recurrence_type",
|
||||
"recurrence_interval",
|
||||
"last_generated_date",
|
||||
"last_generated_reference_date",
|
||||
]
|
||||
read_only_fields = ["last_generated_date", "last_generated_reference_date"]
|
||||
|
||||
def create(self, validated_data):
|
||||
instance = super().create(validated_data)
|
||||
instance.create_upcoming_transactions()
|
||||
return instance
|
||||
|
||||
|
||||
class TransactionSerializer(serializers.ModelSerializer):
|
||||
|
||||
@@ -9,10 +9,13 @@ router.register(r"categories", views.TransactionCategoryViewSet)
|
||||
router.register(r"tags", views.TransactionTagViewSet)
|
||||
router.register(r"entities", views.TransactionEntityViewSet)
|
||||
router.register(r"installment-plans", views.InstallmentPlanViewSet)
|
||||
router.register(r"recurring-transactions", views.RecurringTransactionViewSet)
|
||||
router.register(r"account-groups", views.AccountGroupViewSet)
|
||||
router.register(r"accounts", views.AccountViewSet)
|
||||
router.register(r"currencies", views.CurrencyViewSet)
|
||||
router.register(r"exchange-rates", views.ExchangeRateViewSet)
|
||||
router.register(r"dca/strategies", views.DCAStrategyViewSet)
|
||||
router.register(r"dca/entries", views.DCAEntryViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
path("", include(router.urls)),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from .transactions import *
|
||||
from .accounts import *
|
||||
from .currencies import *
|
||||
from .dca import *
|
||||
|
||||
41
app/apps/api/views/dca.py
Normal file
41
app/apps/api/views/dca.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from apps.dca.models import DCAStrategy, DCAEntry
|
||||
from apps.api.serializers import DCAStrategySerializer, DCAEntrySerializer
|
||||
|
||||
|
||||
class DCAStrategyViewSet(viewsets.ModelViewSet):
|
||||
queryset = DCAStrategy.objects.all()
|
||||
serializer_class = DCAStrategySerializer
|
||||
|
||||
@action(detail=True, methods=["get"])
|
||||
def investment_frequency(self, request, pk=None):
|
||||
strategy = self.get_object()
|
||||
return Response(strategy.investment_frequency_data())
|
||||
|
||||
@action(detail=True, methods=["get"])
|
||||
def price_comparison(self, request, pk=None):
|
||||
strategy = self.get_object()
|
||||
return Response(strategy.price_comparison_data())
|
||||
|
||||
@action(detail=True, methods=["get"])
|
||||
def current_price(self, request, pk=None):
|
||||
strategy = self.get_object()
|
||||
price_data = strategy.current_price()
|
||||
if price_data:
|
||||
price, date = price_data
|
||||
return Response({"price": price, "date": date})
|
||||
return Response({"price": None, "date": None})
|
||||
|
||||
|
||||
class DCAEntryViewSet(viewsets.ModelViewSet):
|
||||
queryset = DCAEntry.objects.all()
|
||||
serializer_class = DCAEntrySerializer
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = DCAEntry.objects.all()
|
||||
strategy_id = self.request.query_params.get("strategy", None)
|
||||
if strategy_id is not None:
|
||||
queryset = queryset.filter(strategy_id=strategy_id)
|
||||
return queryset
|
||||
@@ -1,4 +1,4 @@
|
||||
from rest_framework import permissions, viewsets
|
||||
from rest_framework import viewsets
|
||||
|
||||
from apps.api.serializers import (
|
||||
TransactionSerializer,
|
||||
@@ -6,6 +6,7 @@ from apps.api.serializers import (
|
||||
TransactionTagSerializer,
|
||||
InstallmentPlanSerializer,
|
||||
TransactionEntitySerializer,
|
||||
RecurringTransactionSerializer,
|
||||
)
|
||||
from apps.transactions.models import (
|
||||
Transaction,
|
||||
@@ -13,6 +14,7 @@ from apps.transactions.models import (
|
||||
TransactionTag,
|
||||
InstallmentPlan,
|
||||
TransactionEntity,
|
||||
RecurringTransaction,
|
||||
)
|
||||
from apps.rules.signals import transaction_updated, transaction_created
|
||||
|
||||
@@ -53,10 +55,7 @@ class InstallmentPlanViewSet(viewsets.ModelViewSet):
|
||||
queryset = InstallmentPlan.objects.all()
|
||||
serializer_class = InstallmentPlanSerializer
|
||||
|
||||
def perform_create(self, serializer):
|
||||
instance = serializer.save()
|
||||
instance.create_transactions()
|
||||
|
||||
def perform_update(self, serializer):
|
||||
instance = serializer.save()
|
||||
instance.create_transactions()
|
||||
class RecurringTransactionViewSet(viewsets.ModelViewSet):
|
||||
queryset = RecurringTransaction.objects.all()
|
||||
serializer_class = RecurringTransactionSerializer
|
||||
|
||||
@@ -53,6 +53,8 @@ urlpatterns = [
|
||||
),
|
||||
path("tags/", views.tags_index, name="tags_index"),
|
||||
path("tags/list/", views.tags_list, name="tags_list"),
|
||||
path("tags/table/active/", views.tags_table_active, name="tags_table_active"),
|
||||
path("tags/table/archived/", views.tags_table_archived, name="tags_table_archived"),
|
||||
path("tags/add/", views.tag_add, name="tag_add"),
|
||||
path(
|
||||
"tags/<int:tag_id>/edit/",
|
||||
@@ -66,6 +68,16 @@ urlpatterns = [
|
||||
),
|
||||
path("entities/", views.entities_index, name="entities_index"),
|
||||
path("entities/list/", views.entities_list, name="entities_list"),
|
||||
path(
|
||||
"entities/table/active/",
|
||||
views.entities_table_active,
|
||||
name="entities_table_active",
|
||||
),
|
||||
path(
|
||||
"entities/table/archived/",
|
||||
views.entities_table_archived,
|
||||
name="entities_table_archived",
|
||||
),
|
||||
path("entities/add/", views.entity_add, name="entity_add"),
|
||||
path(
|
||||
"entities/<int:entity_id>/edit/",
|
||||
@@ -79,6 +91,16 @@ urlpatterns = [
|
||||
),
|
||||
path("categories/", views.categories_index, name="categories_index"),
|
||||
path("categories/list/", views.categories_list, name="categories_list"),
|
||||
path(
|
||||
"categories/table/active/",
|
||||
views.categories_table_active,
|
||||
name="categories_table_active",
|
||||
),
|
||||
path(
|
||||
"categories/table/archived/",
|
||||
views.categories_table_archived,
|
||||
name="categories_table_archived",
|
||||
),
|
||||
path("categories/add/", views.category_add, name="category_add"),
|
||||
path(
|
||||
"categories/<int:category_id>/edit/",
|
||||
|
||||
@@ -25,11 +25,33 @@ def categories_index(request):
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def categories_list(request):
|
||||
categories = TransactionCategory.objects.all().order_by("id")
|
||||
return render(
|
||||
request,
|
||||
"categories/fragments/list.html",
|
||||
{"categories": categories},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def categories_table_active(request):
|
||||
categories = TransactionCategory.objects.filter(active=True).order_by("id")
|
||||
return render(
|
||||
request,
|
||||
"categories/fragments/table.html",
|
||||
{"categories": categories, "active": True},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def categories_table_archived(request):
|
||||
categories = TransactionCategory.objects.filter(active=False).order_by("id")
|
||||
return render(
|
||||
request,
|
||||
"categories/fragments/table.html",
|
||||
{"categories": categories, "active": False},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -24,11 +24,33 @@ def entities_index(request):
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def entities_list(request):
|
||||
entities = TransactionEntity.objects.all().order_by("id")
|
||||
return render(
|
||||
request,
|
||||
"entities/fragments/list.html",
|
||||
{"entities": entities},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def entities_table_active(request):
|
||||
entities = TransactionEntity.objects.filter(active=True).order_by("id")
|
||||
return render(
|
||||
request,
|
||||
"entities/fragments/table.html",
|
||||
{"entities": entities, "active": True},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def entities_table_archived(request):
|
||||
entities = TransactionEntity.objects.filter(active=False).order_by("id")
|
||||
return render(
|
||||
request,
|
||||
"entities/fragments/table.html",
|
||||
{"entities": entities, "active": False},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -24,11 +24,33 @@ def tags_index(request):
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def tags_list(request):
|
||||
tags = TransactionTag.objects.all().order_by("id")
|
||||
return render(
|
||||
request,
|
||||
"tags/fragments/list.html",
|
||||
{"tags": tags},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def tags_table_active(request):
|
||||
tags = TransactionTag.objects.filter(active=True).order_by("id")
|
||||
return render(
|
||||
request,
|
||||
"tags/fragments/table.html",
|
||||
{"tags": tags, "active": True},
|
||||
)
|
||||
|
||||
|
||||
@only_htmx
|
||||
@login_required
|
||||
@require_http_methods(["GET"])
|
||||
def tags_table_archived(request):
|
||||
tags = TransactionTag.objects.filter(active=False).order_by("id")
|
||||
return render(
|
||||
request,
|
||||
"tags/fragments/table.html",
|
||||
{"tags": tags, "active": False},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -15,53 +15,18 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body table-responsive">
|
||||
{% if categories %}
|
||||
<c-config.search></c-config.search>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
<th scope="col" class="col">{% translate 'Muted' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for category in categories %}
|
||||
<tr class="category">
|
||||
<td class="col-auto text-center">
|
||||
<div class="btn-group" role="group" aria-label="{% translate 'Actions' %}">
|
||||
<a class="btn btn-secondary btn-sm"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'category_edit' category_id=category.id %}"
|
||||
hx-target="#generic-offcanvas">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="btn btn-secondary btn-sm text-danger"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'category_delete' category_id=category.id %}"
|
||||
hx-trigger='confirmed'
|
||||
data-bypass-on-ctrl="true"
|
||||
data-title="{% translate "Are you sure?" %}"
|
||||
data-text="{% translate "You won't be able to revert this!" %}"
|
||||
data-confirm-text="{% translate "Yes, delete it!" %}"
|
||||
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col">{{ category.name }}</td>
|
||||
<td class="col">
|
||||
{% if category.mute %}<i class="fa-solid fa-check text-success"></i>{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<c-msg.empty title="{% translate "No categories" %}" remove-padding></c-msg.empty>
|
||||
{% endif %}
|
||||
<div class="card-header">
|
||||
<ul class="nav nav-pills card-header-pills" id="myTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link 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>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" hx-get="{% url 'categories_table_archived' %}" hx-target="#categories-table" data-bs-toggle="tab" type="button" role="tab" aria-selected="false">{% translate 'Archived' %}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="categories-table"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
59
app/templates/categories/fragments/table.html
Normal file
59
app/templates/categories/fragments/table.html
Normal file
@@ -0,0 +1,59 @@
|
||||
{% load i18n %}
|
||||
{% if active %}
|
||||
<div class="show-loading" hx-get="{% url 'categories_table_active' %}" hx-trigger="updated from:window"
|
||||
hx-swap="outerHTML">
|
||||
{% else %}
|
||||
<div class="show-loading" hx-get="{% url 'categories_table_archived' %}" hx-trigger="updated from:window"
|
||||
hx-swap="outerHTML">
|
||||
{% endif %}
|
||||
{% if categories %}
|
||||
<div class="table-responsive">
|
||||
<c-config.search></c-config.search>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
<th scope="col" class="col">{% translate 'Muted' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for category in categories %}
|
||||
<tr class="category">
|
||||
<td class="col-auto text-center">
|
||||
<div class="btn-group" role="group" aria-label="{% translate 'Actions' %}">
|
||||
<a class="btn btn-secondary btn-sm"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
hx-swap="innerHTML"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'category_edit' category_id=category.id %}"
|
||||
hx-target="#generic-offcanvas">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="btn btn-secondary btn-sm text-danger"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'category_delete' category_id=category.id %}"
|
||||
hx-trigger='confirmed'
|
||||
hx-swap="innerHTML"
|
||||
data-bypass-on-ctrl="true"
|
||||
data-title="{% translate "Are you sure?" %}"
|
||||
data-text="{% translate "You won't be able to revert this!" %}"
|
||||
data-confirm-text="{% translate "Yes, delete it!" %}"
|
||||
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col">{{ category.name }}</td>
|
||||
<td class="col">
|
||||
{% if category.mute %}<i class="fa-solid fa-check text-success"></i>{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<c-msg.empty title="{% translate "No categories" %}" remove-padding></c-msg.empty>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -4,5 +4,5 @@
|
||||
{% block title %}{% translate 'Categories' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div hx-get="{% url 'categories_list' %}" hx-trigger="load, updated from:window" class="show-loading"></div>
|
||||
<div hx-get="{% url 'categories_list' %}" hx-trigger="load" class="show-loading"></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -15,49 +15,18 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body table-responsive">
|
||||
{% if entities %}
|
||||
<c-config.search></c-config.search>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entity in entities %}
|
||||
<tr class="entity">
|
||||
<td class="col-auto">
|
||||
<div class="btn-group" role="group" aria-label="{% translate 'Actions' %}">
|
||||
<a class="btn btn-secondary btn-sm"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'entity_edit' entity_id=entity.id %}"
|
||||
hx-target="#generic-offcanvas">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="btn btn-secondary btn-sm text-danger"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'entity_delete' entity_id=entity.id %}"
|
||||
hx-trigger='confirmed'
|
||||
data-bypass-on-ctrl="true"
|
||||
data-title="{% translate "Are you sure?" %}"
|
||||
data-text="{% translate "You won't be able to revert this!" %}"
|
||||
data-confirm-text="{% translate "Yes, delete it!" %}"
|
||||
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col">{{ entity.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<c-msg.empty title="{% translate "No entities" %}" remove-padding></c-msg.empty>
|
||||
{% endif %}
|
||||
<div class="card-header">
|
||||
<ul class="nav nav-pills card-header-pills" id="myTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link 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>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" hx-get="{% url 'entities_table_archived' %}" hx-target="#entities-table" data-bs-toggle="tab" type="button" role="tab" aria-selected="false">{% translate 'Archived' %}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="entities-table"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
55
app/templates/entities/fragments/table.html
Normal file
55
app/templates/entities/fragments/table.html
Normal file
@@ -0,0 +1,55 @@
|
||||
{% load i18n %}
|
||||
{% if active %}
|
||||
<div class="show-loading" hx-get="{% url 'entities_table_active' %}" hx-trigger="updated from:window"
|
||||
hx-swap="outerHTML">
|
||||
{% else %}
|
||||
<div class="show-loading" hx-get="{% url 'entities_table_archived' %}" hx-trigger="updated from:window"
|
||||
hx-swap="outerHTML">
|
||||
{% endif %}
|
||||
{% if entities %}
|
||||
<div class="table-responsive">
|
||||
<c-config.search></c-config.search>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entity in entities %}
|
||||
<tr class="entity">
|
||||
<td class="col-auto">
|
||||
<div class="btn-group" role="group" aria-label="{% translate 'Actions' %}">
|
||||
<a class="btn btn-secondary btn-sm"
|
||||
role="button"
|
||||
hx-swap="innerHTML"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'entity_edit' entity_id=entity.id %}"
|
||||
hx-target="#generic-offcanvas">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="btn btn-secondary btn-sm text-danger"
|
||||
role="button"
|
||||
hx-swap="innerHTML"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'entity_delete' entity_id=entity.id %}"
|
||||
hx-trigger='confirmed'
|
||||
data-bypass-on-ctrl="true"
|
||||
data-title="{% translate "Are you sure?" %}"
|
||||
data-text="{% translate "You won't be able to revert this!" %}"
|
||||
data-confirm-text="{% translate "Yes, delete it!" %}"
|
||||
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col">{{ entity.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<c-msg.empty title="{% translate "No entities" %}" remove-padding></c-msg.empty>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -4,5 +4,5 @@
|
||||
{% block title %}{% translate 'Entities' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div hx-get="{% url 'entities_list' %}" hx-trigger="load, updated from:window" class="show-loading"></div>
|
||||
<div hx-get="{% url 'entities_list' %}" hx-trigger="load" class="show-loading"></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -15,49 +15,18 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body table-responsive">
|
||||
{% if tags %}
|
||||
<c-config.search></c-config.search>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for tag in tags %}
|
||||
<tr class="tag">
|
||||
<td class="col-auto">
|
||||
<div class="btn-group" role="group" aria-label="{% translate 'Actions' %}">
|
||||
<a class="btn btn-secondary btn-sm"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'tag_edit' tag_id=tag.id %}"
|
||||
hx-target="#generic-offcanvas">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="btn btn-secondary btn-sm text-danger"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'tag_delete' tag_id=tag.id %}"
|
||||
hx-trigger='confirmed'
|
||||
data-bypass-on-ctrl="true"
|
||||
data-title="{% translate "Are you sure?" %}"
|
||||
data-text="{% translate "You won't be able to revert this!" %}"
|
||||
data-confirm-text="{% translate "Yes, delete it!" %}"
|
||||
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col">{{ tag.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<c-msg.empty title="{% translate "No tags" %}" remove-padding></c-msg.empty>
|
||||
{% endif %}
|
||||
<div class="card-header">
|
||||
<ul class="nav nav-pills card-header-pills" id="myTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" data-bs-toggle="tab" type="button" role="tab" aria-selected="true" hx-get="{% url 'tags_table_active' %}" hx-trigger="load, click" hx-target="#tags-table">{% translate 'Active' %}</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" hx-get="{% url 'tags_table_archived' %}" hx-target="#tags-table" data-bs-toggle="tab" type="button" role="tab" aria-selected="false">{% translate 'Archived' %}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="tags-table"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
55
app/templates/tags/fragments/table.html
Normal file
55
app/templates/tags/fragments/table.html
Normal file
@@ -0,0 +1,55 @@
|
||||
{% load i18n %}
|
||||
{% if active %}
|
||||
<div class="show-loading" hx-get="{% url 'tags_table_active' %}" hx-trigger="updated from:window"
|
||||
hx-swap="outerHTML">
|
||||
{% else %}
|
||||
<div class="show-loading" hx-get="{% url 'tags_table_archived' %}" hx-trigger="updated from:window"
|
||||
hx-swap="outerHTML">
|
||||
{% endif %}
|
||||
{% if tags %}
|
||||
<div class="table-responsive">
|
||||
<c-config.search></c-config.search>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-auto"></th>
|
||||
<th scope="col" class="col">{% translate 'Name' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for tag in tags %}
|
||||
<tr class="tag">
|
||||
<td class="col-auto">
|
||||
<div class="btn-group" role="group" aria-label="{% translate 'Actions' %}">
|
||||
<a class="btn btn-secondary btn-sm"
|
||||
role="button"
|
||||
hx-swap="innerHTML"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-title="{% translate "Edit" %}"
|
||||
hx-get="{% url 'tag_edit' tag_id=tag.id %}"
|
||||
hx-target="#generic-offcanvas">
|
||||
<i class="fa-solid fa-pencil fa-fw"></i></a>
|
||||
<a class="btn btn-secondary btn-sm text-danger"
|
||||
role="button"
|
||||
data-bs-toggle="tooltip"
|
||||
hx-swap="innerHTML"
|
||||
data-bs-title="{% translate "Delete" %}"
|
||||
hx-delete="{% url 'tag_delete' tag_id=tag.id %}"
|
||||
hx-trigger='confirmed'
|
||||
data-bypass-on-ctrl="true"
|
||||
data-title="{% translate "Are you sure?" %}"
|
||||
data-text="{% translate "You won't be able to revert this!" %}"
|
||||
data-confirm-text="{% translate "Yes, delete it!" %}"
|
||||
_="install prompt_swal"><i class="fa-solid fa-trash fa-fw"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col">{{ tag.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<c-msg.empty title="{% translate "No tags" %}" remove-padding></c-msg.empty>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -4,5 +4,5 @@
|
||||
{% block title %}{% translate 'Tags' %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div hx-get="{% url 'tags_list' %}" hx-trigger="load, updated from:window" class="show-loading"></div>
|
||||
<div hx-get="{% url 'tags_list' %}" hx-trigger="load" class="show-loading"></div>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user