From 4358708262fcad57473a7f847648ef453a3abe9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?= Date: Tue, 3 Sep 2024 15:25:14 +0200 Subject: [PATCH] add links to add a new X to: game, edition, purchase, session, device, platform --- common/utils.py | 59 ++++++++++++++++++++++-- games/static/base.css | 20 ++++---- games/templates/cotton/button.html | 1 + games/templates/cotton/simple_table.html | 5 ++ games/templates/cotton/table_header.html | 3 ++ games/templates/list_purchases.html | 2 +- games/views/device.py | 2 + games/views/edition.py | 3 +- games/views/game.py | 3 +- games/views/platform.py | 2 + games/views/purchase.py | 3 +- games/views/session.py | 18 +++----- 12 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 games/templates/cotton/table_header.html diff --git a/common/utils.py b/common/utils.py index 264c9ca..ea2ea39 100644 --- a/common/utils.py +++ b/common/utils.py @@ -1,8 +1,9 @@ from random import choices from string import ascii_lowercase -from typing import Any +from typing import Any, Callable from django.template.loader import render_to_string +from django.urls import NoReverseMatch, reverse from django.utils.safestring import mark_safe @@ -31,16 +32,68 @@ HTMLAttribute = tuple[str, str] HTMLTag = str -def A(attributes: list[HTMLAttribute], children: list[HTMLTag] | HTMLTag) -> HTMLTag: +def Component( + attributes: list[HTMLAttribute] = [], + children: list[HTMLTag] | HTMLTag = [], + template: str = "", + tag_name: str = "", +) -> HTMLTag: + if not tag_name and not template: + raise ValueError("One of template or tag_name is required.") if isinstance(children, str): children = [children] childrenBlob = "\n".join(children) attributesList = [f'{name} = "{value}"' for name, value in attributes] attributesBlob = " ".join(attributesList) - tag: str = f"{childrenBlob}" + tag: str = "" + if tag_name != "": + tag = f"{childrenBlob}" + elif template != "": + tag = render_to_string( + template, + {name: value for name, value in attributes} | {"slot": "\n".join(children)}, + ) return mark_safe(tag) +def A( + attributes: list[HTMLAttribute] = [], + children: list[HTMLTag] | HTMLTag = [], + url: str | Callable[..., Any] = "", +): + """ + Returns the HTML tag "a". + "url" can either be: + - URL (string) + - path name passed to reverse() (string) + - function + """ + additional_attributes = [] + if url: + if type(url) is str: + try: + url_result = reverse(url) + except NoReverseMatch: + url_result = url + elif callable(url): + url_result = url() + else: + raise TypeError("'url' is neither str nor function.") + additional_attributes = [("href", url_result)] + return Component( + tag_name="a", attributes=attributes + additional_attributes, children=children + ) + + +def Button( + attributes: list[HTMLAttribute] = [], + children: list[HTMLTag] | HTMLTag = [], +): + return Component( + template="cotton/button.html", attributes=attributes, children=children + ) + + def safe_division(numerator: int | float, denominator: int | float) -> int | float: """ Divides without triggering division by zero exception. diff --git a/games/static/base.css b/games/static/base.css index 7679a6d..ccaff9f 100644 --- a/games/static/base.css +++ b/games/static/base.css @@ -1,5 +1,5 @@ /* -! tailwindcss v3.4.10 | MIT License | https://tailwindcss.com +! tailwindcss v3.4.7 | MIT License | https://tailwindcss.com */ /* @@ -1816,11 +1816,6 @@ input:checked + .toggle-bg { border-color: rgb(220 215 254 / var(--tw-border-opacity)); } -.\!bg-gray-50 { - --tw-bg-opacity: 1 !important; - background-color: rgb(249 250 251 / var(--tw-bg-opacity)) !important; -} - .bg-blue-100 { --tw-bg-opacity: 1; background-color: rgb(225 239 254 / var(--tw-bg-opacity)); @@ -1966,6 +1961,10 @@ input:checked + .toggle-bg { text-align: center; } +.text-right { + text-align: right; +} + .align-top { vertical-align: top; } @@ -2722,11 +2721,6 @@ textarea:disabled:is(.dark *) { border-color: transparent; } -.dark\:\!bg-gray-700:is(.dark *) { - --tw-bg-opacity: 1 !important; - background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important; -} - .dark\:bg-blue-200:is(.dark *) { --tw-bg-opacity: 1; background-color: rgb(195 221 253 / var(--tw-bg-opacity)); @@ -3075,6 +3069,10 @@ textarea:disabled:is(.dark *) { --tw-space-x-reverse: 1; } +.rtl\:text-left:where([dir="rtl"], [dir="rtl"] *) { + text-align: left; +} + .rtl\:text-right:where([dir="rtl"], [dir="rtl"] *) { text-align: right; } diff --git a/games/templates/cotton/button.html b/games/templates/cotton/button.html index 83f0695..3cc1f1f 100644 --- a/games/templates/cotton/button.html +++ b/games/templates/cotton/button.html @@ -1,4 +1,5 @@ diff --git a/games/templates/cotton/simple_table.html b/games/templates/cotton/simple_table.html index 62c413f..8f08b99 100644 --- a/games/templates/cotton/simple_table.html +++ b/games/templates/cotton/simple_table.html @@ -2,6 +2,11 @@
+ {% if header_action %} + + {{ header_action }} + + {% endif %} {% for column in columns %}{% endfor %} diff --git a/games/templates/cotton/table_header.html b/games/templates/cotton/table_header.html new file mode 100644 index 0000000..2fa28cc --- /dev/null +++ b/games/templates/cotton/table_header.html @@ -0,0 +1,3 @@ + diff --git a/games/templates/list_purchases.html b/games/templates/list_purchases.html index 8ea2bb0..d39e80a 100644 --- a/games/templates/list_purchases.html +++ b/games/templates/list_purchases.html @@ -5,6 +5,6 @@ {% endblock title %} {% block content %}
- +
{% endblock content %} diff --git a/games/views/device.py b/games/views/device.py index 7842a40..3fc1a2b 100644 --- a/games/views/device.py +++ b/games/views/device.py @@ -7,6 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.template.loader import render_to_string from django.urls import reverse +from common.utils import A, Button from games.forms import DeviceForm from games.models import Device from games.views.general import dateformat @@ -35,6 +36,7 @@ def list_devices(request: HttpRequest) -> HttpResponse: else None ), "data": { + "header_action": A([], Button([], "Add device"), url="add_device"), "columns": [ "Name", "Type", diff --git a/games/views/edition.py b/games/views/edition.py index f569390..a2568cf 100644 --- a/games/views/edition.py +++ b/games/views/edition.py @@ -7,7 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.template.loader import render_to_string from django.urls import reverse -from common.utils import A, truncate_with_popover +from common.utils import A, Button, truncate_with_popover from games.forms import EditionForm from games.models import Edition, Game from games.views.general import dateformat @@ -36,6 +36,7 @@ def list_editions(request: HttpRequest) -> HttpResponse: else None ), "data": { + "header_action": A([], Button([], "Add edition"), url="add_edition"), "columns": [ "Game", "Name", diff --git a/games/views/game.py b/games/views/game.py index 51132e7..db12442 100644 --- a/games/views/game.py +++ b/games/views/game.py @@ -9,7 +9,7 @@ from django.template.loader import render_to_string from django.urls import reverse from common.time import format_duration -from common.utils import A, safe_division, truncate_with_popover +from common.utils import A, Button, safe_division, truncate_with_popover from games.forms import GameForm from games.models import Edition, Game, Purchase, Session from games.views.general import ( @@ -45,6 +45,7 @@ def list_games(request: HttpRequest) -> HttpResponse: else None ), "data": { + "header_action": A([], Button([], "Add game"), url="add_game"), "columns": [ "Name", "Sort Name", diff --git a/games/views/platform.py b/games/views/platform.py index 49995b1..3906510 100644 --- a/games/views/platform.py +++ b/games/views/platform.py @@ -7,6 +7,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.template.loader import render_to_string from django.urls import reverse +from common.utils import A, Button from games.forms import PlatformForm from games.models import Platform from games.views.general import dateformat, use_custom_redirect @@ -35,6 +36,7 @@ def list_platforms(request: HttpRequest) -> HttpResponse: else None ), "data": { + "header_action": A([], Button([], "Add platform"), url="add_platform"), "columns": [ "Name", "Group", diff --git a/games/views/purchase.py b/games/views/purchase.py index 87b4f77..9680948 100644 --- a/games/views/purchase.py +++ b/games/views/purchase.py @@ -13,7 +13,7 @@ from django.template.loader import render_to_string from django.urls import reverse from django.utils import timezone -from common.utils import A, truncate_with_popover +from common.utils import A, Button, truncate_with_popover from games.forms import PurchaseForm from games.models import Edition, Purchase from games.views.general import dateformat, use_custom_redirect @@ -42,6 +42,7 @@ def list_purchases(request: HttpRequest) -> HttpResponse: else None ), "data": { + "header_action": A([], Button([], "Add purchase"), url="add_purchase"), "columns": [ "Name", "Type", diff --git a/games/views/session.py b/games/views/session.py index 9022a32..0e85639 100644 --- a/games/views/session.py +++ b/games/views/session.py @@ -9,7 +9,7 @@ from django.urls import reverse from django.utils import timezone from common.time import format_duration -from common.utils import A, truncate_with_popover +from common.utils import A, Button, truncate_with_popover from games.forms import SessionForm from games.models import Purchase, Session from games.views.general import ( @@ -45,6 +45,7 @@ def list_sessions(request: HttpRequest) -> HttpResponse: else None ), "data": { + "header_action": A([], Button([], "Add session"), url="add_session"), "columns": [ "Name", "Date", @@ -57,16 +58,11 @@ def list_sessions(request: HttpRequest) -> HttpResponse: "rows": [ [ A( - [ - ( - "href", - reverse( - "view_game", - args=[session.purchase.edition.game.pk], - ), - ) - ], - truncate_with_popover(session.purchase.edition.name), + children=truncate_with_popover(session.purchase.edition.name), + url=reverse( + "view_game", + args=[session.purchase.edition.game.pk], + ), ), f"{session.timestamp_start.strftime(datetimeformat)}{f" — {session.timestamp_end.strftime(timeformat)}" if session.timestamp_end else ""}", (
{{ column }}
+ {{ slot }} +