diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index fb89d82..8bc4d57 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -3,10 +3,10 @@ name: Docker Image CI on: push: branches: - - "main" + - "fbl-integration" - "staging" pull_request: - branches: ["main"] + branches: ["fbl-integration"] env: REGISTRY: ghcr.io diff --git a/fbl_integration/__init__.py b/fbl_integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fbl_integration/admin.py b/fbl_integration/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/fbl_integration/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/fbl_integration/apps.py b/fbl_integration/apps.py new file mode 100644 index 0000000..e953f84 --- /dev/null +++ b/fbl_integration/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class FblIntegrationConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "fbl_integration" diff --git a/fbl_integration/forms.py b/fbl_integration/forms.py new file mode 100644 index 0000000..c604a60 --- /dev/null +++ b/fbl_integration/forms.py @@ -0,0 +1,40 @@ +from typing import Any +from django import forms +from django.conf import settings +from django.utils.translation import gettext_lazy as _ +from datetime import datetime + +class FblAuthForm(forms.Form): + badge_number = forms.CharField(required=True) + dob = forms.CharField(required=True) + + def clean(self) -> dict[str, Any]: + cleaned_data = super(FblAuthForm, self).clean() + badge_number = cleaned_data.get("badge_number") + dob = cleaned_data.get("dob") + + if not badge_number: + raise forms.ValidationError( + _("Badge number is required") + ) + + try: + int(badge_number) + except ValueError: + raise forms.ValidationError( + _("Badge number must be a number") + ) + + if not dob: + raise forms.ValidationError( + _("Date of birth is required") + ) + try: + dob = datetime.strptime(dob, "%d/%m/%Y").strftime("%Y-%m-%d") + except ValueError: + raise forms.ValidationError( + _("Date of birth must be in the format DD/MM/YYYY") + ) + cleaned_data["dob"] = dob + + return cleaned_data \ No newline at end of file diff --git a/fbl_integration/migrations/__init__.py b/fbl_integration/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fbl_integration/models.py b/fbl_integration/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/fbl_integration/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/fbl_integration/templates/fbl_auth_get_code.html b/fbl_integration/templates/fbl_auth_get_code.html new file mode 100644 index 0000000..a3ff6e0 --- /dev/null +++ b/fbl_integration/templates/fbl_auth_get_code.html @@ -0,0 +1,31 @@ +{% extends 'base.html' %} +{% block content %} +{% load i18n %} +
+
+

+ {% trans 'We sent you an email that contains a code for your login. Please enter the code in the field below.' %} +

+
+ {% csrf_token %} + + {% if get_code_form.non_field_errors %} + + {% endif %} + +
+ + +
+
+ +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/fbl_integration/templates/fbl_auth_start.html b/fbl_integration/templates/fbl_auth_start.html new file mode 100644 index 0000000..4b3f843 --- /dev/null +++ b/fbl_integration/templates/fbl_auth_start.html @@ -0,0 +1,10 @@ + +{% extends 'base.html' %} +{% block content %} +{% load i18n %} +
+
+ {% include 'partials/attendee_login.html' with fbl_auth_form=form %} +
+
+{% endblock %} \ No newline at end of file diff --git a/fbl_integration/templates/partials/attendee_login.html b/fbl_integration/templates/partials/attendee_login.html new file mode 100644 index 0000000..3572dd8 --- /dev/null +++ b/fbl_integration/templates/partials/attendee_login.html @@ -0,0 +1,32 @@ +{% block attendee_login %} +{% load i18n %} +
+ {% csrf_token %} + + {% if fbl_auth_form.non_field_errors %} + + {% endif %} + +
+ + +
+ + + +
+ +
+
+ {% if general_login %}
{% trans "I don\'t have a seat for this years FBL" %}
{% endif %} +{% endblock %} \ No newline at end of file diff --git a/fbl_integration/tests.py b/fbl_integration/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/fbl_integration/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/fbl_integration/urls.py b/fbl_integration/urls.py new file mode 100644 index 0000000..4678c3a --- /dev/null +++ b/fbl_integration/urls.py @@ -0,0 +1,8 @@ +from django.urls import path + +from .views import fbl_authentication_start, fbl_authentication_get_code + +urlpatterns = [ + path("auth_start", fbl_authentication_start, name="fbl-auth-start"), + path("auth_get_code", fbl_authentication_get_code, name="fbl-auth-get-code") +] diff --git a/fbl_integration/views.py b/fbl_integration/views.py new file mode 100644 index 0000000..6104dd1 --- /dev/null +++ b/fbl_integration/views.py @@ -0,0 +1,19 @@ +from django.shortcuts import render +from .forms import FblAuthForm + +def fbl_authentication_start(request): + if request.method == "POST": + form = FblAuthForm(request.POST) + if form.is_valid(): + + # set hidden fields with dob and badge number + return render(request, "fbl_auth_get_code.html") + else: + return render(request, "fbl_auth_start.html", {"form": form}) + else: + form = FblAuthForm() + + return render(request, "auth_start.html", {"form": form}) + +def fbl_authentication_get_code(request): + return render(request, "fbl_auth_get_code.html") \ No newline at end of file diff --git a/paw/settings.py b/paw/settings.py index 39e68ee..412cf7b 100644 --- a/paw/settings.py +++ b/paw/settings.py @@ -45,6 +45,7 @@ INSTALLED_APPS = [ "django.contrib.messages", "django.contrib.staticfiles", "status", + "fbl_integration", ] AUTH_USER_MODEL = "core.PawUser" @@ -65,7 +66,10 @@ ROOT_URLCONF = "paw.urls" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [BASE_DIR / 'paw' / 'templates'], + "DIRS": [ + BASE_DIR / 'paw' / 'templates', + BASE_DIR / 'fbl_integration' / 'templates', + ], "APP_DIRS": True, "OPTIONS": { "context_processors": [ diff --git a/paw/templates/core/login.html b/paw/templates/core/login.html index 7a25655..5830f4d 100644 --- a/paw/templates/core/login.html +++ b/paw/templates/core/login.html @@ -9,6 +9,9 @@ {% trans 'Register Account' %}
+ + {% include 'partials/attendee_login.html' with general_login=True %} +
{% csrf_token %} diff --git a/paw/urls.py b/paw/urls.py index 21ca5ff..232fa9b 100644 --- a/paw/urls.py +++ b/paw/urls.py @@ -26,6 +26,7 @@ urlpatterns = [ path("", include("core.urls")), path("", include("ticketing.urls")), path("status", include("status.urls")), + path("fbl/", include("fbl_integration.urls")), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) if settings.DEBUG: -- 2.51.2 From 2e14ae740323288074387f0c96733a6263e7549d Mon Sep 17 00:00:00 2001 From: Alex Ottr Date: Sat, 6 Apr 2024 21:21:05 +0200 Subject: [PATCH 2/7] add fbl auth handlers and remaining views --- fbl_integration/admin.py | 9 +- fbl_integration/forms.py | 28 + fbl_integration/migrations/0001_initial.py | 44 ++ fbl_integration/models.py | 31 +- .../templates/complete_registration.html | 43 ++ .../templates/fbl_auth_get_code.html | 41 +- .../templates/partials/fbl_logo.html | 1 + fbl_integration/urls.py | 7 +- fbl_integration/utils.py | 52 ++ fbl_integration/views.py | 66 ++- paw/settings.py | 4 + paw/static/css/paw.css | 506 ++---------------- paw/templates/core/account_finish.html | 8 +- paw/templates/core/login.html | 5 +- paw/templates/partials/logo.html | 2 +- theme/tailwind.config.js | 1 + 16 files changed, 343 insertions(+), 505 deletions(-) create mode 100644 fbl_integration/migrations/0001_initial.py create mode 100644 fbl_integration/templates/complete_registration.html create mode 100644 fbl_integration/templates/partials/fbl_logo.html create mode 100644 fbl_integration/utils.py diff --git a/fbl_integration/admin.py b/fbl_integration/admin.py index 8c38f3f..c766727 100644 --- a/fbl_integration/admin.py +++ b/fbl_integration/admin.py @@ -1,3 +1,10 @@ from django.contrib import admin +from .models import FblAccount + +@admin.register(FblAccount) +class FblAccountAdmin(admin.ModelAdmin): + list_display = ["username", "badge_number", "status", "tags_secured", "created_at"] + search_fields = ["badge_number", "username"] + list_filter = ["status"] + readonly_fields = ["badge_number", "account_id", "username", "status", "tags_secured", "created_at"] -# Register your models here. diff --git a/fbl_integration/forms.py b/fbl_integration/forms.py index c604a60..51db919 100644 --- a/fbl_integration/forms.py +++ b/fbl_integration/forms.py @@ -37,4 +37,32 @@ class FblAuthForm(forms.Form): ) cleaned_data["dob"] = dob + return cleaned_data + +class FblAuthCodeForm(FblAuthForm): + validation_code = forms.CharField(required=True) + + def clean(self) -> dict[str, Any]: + cleaned_data = super(FblAuthCodeForm, self).clean() + validation_code = cleaned_data.get("validation_code") + + if not validation_code: + raise forms.ValidationError( + _("The validation code is required. Please check your emails.") + ) + + return cleaned_data + +class RegistrationCompletionForm(forms.Form): + email = forms.EmailField(required=True) + + def clean(self) -> dict[str, Any]: + cleaned_data = super(RegistrationCompletionForm, self).clean() + email = cleaned_data.get("email") + + if not email: + raise forms.ValidationError( + _("A Mail is required for notifications.") + ) + return cleaned_data \ No newline at end of file diff --git a/fbl_integration/migrations/0001_initial.py b/fbl_integration/migrations/0001_initial.py new file mode 100644 index 0000000..6c8a110 --- /dev/null +++ b/fbl_integration/migrations/0001_initial.py @@ -0,0 +1,44 @@ +# Generated by Django 5.0.3 on 2024-04-06 18:26 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="FblAccount", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("badge_number", models.IntegerField()), + ("account_id", models.CharField(max_length=255)), + ("username", models.CharField(max_length=255)), + ("tags_secured", models.CharField(max_length=255)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("status", models.CharField(max_length=64)), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/fbl_integration/models.py b/fbl_integration/models.py index 71a8362..c36f1ab 100644 --- a/fbl_integration/models.py +++ b/fbl_integration/models.py @@ -1,3 +1,32 @@ from django.db import models +from core.models import PawUser -# Create your models here. +class FblAccount(models.Model): + user = models.OneToOneField(PawUser, on_delete=models.CASCADE) + badge_number = models.IntegerField() + account_id = models.CharField(max_length=255) + username = models.CharField(max_length=255) + tags_secured = models.CharField(max_length=255) + created_at = models.DateTimeField(auto_now_add=True) + status = models.CharField(max_length=64) + + @classmethod + def create_user(cls, username) -> PawUser: + if not PawUser.objects.filter(username=username).exists(): + user = PawUser.objects.create(username=username) + else: + counter = 1 + while True: + new_username = f"{username}_{counter}" + if not PawUser.objects.filter(username=new_username).exists(): + user = PawUser.objects.create(username=new_username) + break + counter += 1 + + user.set_unusable_password() + user.save() + return user + + + def __str__(self): + return self.user.username \ No newline at end of file diff --git a/fbl_integration/templates/complete_registration.html b/fbl_integration/templates/complete_registration.html new file mode 100644 index 0000000..67d9d8b --- /dev/null +++ b/fbl_integration/templates/complete_registration.html @@ -0,0 +1,43 @@ +{% extends 'base.html' %} +{% block content %} +{% load i18n %} +
+
+

{% trans 'FurryBlacklight Login' %}

+
+
+
    +
  • {% trans 'Authenticate' %}
  • +
  • {% trans 'Confirm Code' %}
  • +
  • {% trans 'Account Completion' %}
  • +
  • {% trans 'Done' %}
  • +
+ + + + + {% csrf_token %} + + {% if form.non_field_errors %} + + {% endif %} + +
+ + +
+ +
+ +
+ +
+
+{% endblock %} \ No newline at end of file diff --git a/fbl_integration/templates/fbl_auth_get_code.html b/fbl_integration/templates/fbl_auth_get_code.html index a3ff6e0..1d4e42f 100644 --- a/fbl_integration/templates/fbl_auth_get_code.html +++ b/fbl_integration/templates/fbl_auth_get_code.html @@ -2,10 +2,22 @@ {% block content %} {% load i18n %}
+
+

{% trans 'FurryBlacklight Login' %}

+
+ {% trans 'Go Back' %} +
-

- {% trans 'We sent you an email that contains a code for your login. Please enter the code in the field below.' %} -

+
    +
  • {% trans 'Authenticate' %}
  • +
  • {% trans 'Confirm Code' %}
  • +
  • {% trans 'Account Completion' %}
  • +
  • {% trans 'Done' %}
  • +
+ +
{% csrf_token %} @@ -15,15 +27,30 @@ {{ get_code_form.non_field_errors }}
{% endif %} - -
+ +
+ + +
+
+ + +
+ + + +
- +
- +
diff --git a/fbl_integration/templates/partials/fbl_logo.html b/fbl_integration/templates/partials/fbl_logo.html new file mode 100644 index 0000000..9201a52 --- /dev/null +++ b/fbl_integration/templates/partials/fbl_logo.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fbl_integration/urls.py b/fbl_integration/urls.py index 4678c3a..91a8da9 100644 --- a/fbl_integration/urls.py +++ b/fbl_integration/urls.py @@ -1,8 +1,9 @@ from django.urls import path -from .views import fbl_authentication_start, fbl_authentication_get_code +from .views import fbl_authentication_start, fbl_authentication_get_code, complete_registration urlpatterns = [ - path("auth_start", fbl_authentication_start, name="fbl-auth-start"), - path("auth_get_code", fbl_authentication_get_code, name="fbl-auth-get-code") + path("auth_start", fbl_authentication_start, name="fbl_auth_start"), + path("auth_get_code", fbl_authentication_get_code, name="fbl_auth_get_code"), + path("complete_registration", complete_registration, name="fbl_complete_registration"), ] diff --git a/fbl_integration/utils.py b/fbl_integration/utils.py new file mode 100644 index 0000000..c346719 --- /dev/null +++ b/fbl_integration/utils.py @@ -0,0 +1,52 @@ +import requests +from django.conf import settings +from .models import FblAccount + +def fbl_auth_request_code(badge_number: int, dob: str) -> bool: + res = requests.post(f'{settings.FBL_AUTH_SERVER}/auth/get-code', data={ + "badge": badge_number, + "password": dob + }) + if res.status_code != 201: + return False + return True + +def fbl_auth_validate_code(badge_number: str, dob: str, validation_code: str) -> str | None: + res = requests.post(f'{settings.FBL_AUTH_SERVER}/auth/signin', json={ + "badge": badge_number, + "password": dob, + "code": validation_code + }) + if res.status_code != 201: + return None + + return res.json()["accessToken"] + +def fbl_auth_get_account(jwt_token: str) -> dict[str, str] | None: + res = requests.get(f'{settings.FBL_AUTH_SERVER}/attendees/me', headers={ + "Authorization": f"Bearer {jwt_token}" + }) + if res.status_code != 200: + return None + + return res.json() + +def get_or_create_account(account_info: dict[str, str]) -> FblAccount: + """Get or create a FblAccount object based on the account info""" + if FblAccount.objects.filter(account_id=account_info["account_id"]).exists(): + # Get existing user and update status + user = FblAccount.objects.get(account_id=account_info["account_id"]) + user.status = account_info["status"] + user.save() + return user + + # Create new user with (generic) username + user = FblAccount.create_user(account_info["username"]) + return FblAccount.objects.create( + user=user, + badge_number=account_info["badge"], + account_id=account_info["account_id"], + status=account_info["status"], + username=account_info["username"], + tags_secured=','.join(account_info["tags_secured"]) + ) \ No newline at end of file diff --git a/fbl_integration/views.py b/fbl_integration/views.py index 6104dd1..21b5103 100644 --- a/fbl_integration/views.py +++ b/fbl_integration/views.py @@ -1,19 +1,61 @@ -from django.shortcuts import render -from .forms import FblAuthForm +from django.shortcuts import render, redirect +from django.contrib.auth import login +from datetime import datetime +from .forms import FblAuthForm, FblAuthCodeForm, RegistrationCompletionForm +from .utils import fbl_auth_request_code, fbl_auth_validate_code, fbl_auth_get_account, get_or_create_account def fbl_authentication_start(request): + auth_form = FblAuthForm(request.POST or None) if request.method == "POST": - form = FblAuthForm(request.POST) - if form.is_valid(): + if auth_form.is_valid(): + + valid_info = fbl_auth_request_code( + badge_number=auth_form.cleaned_data["badge_number"], + dob=auth_form.cleaned_data["dob"] + ) + if valid_info: + code_form = FblAuthCodeForm(initial={ + **auth_form.cleaned_data, + "dob": datetime.strptime(auth_form.cleaned_data["dob"], "%Y-%m-%d").strftime("%d/%m/%Y"), + }) + + return render(request, "fbl_auth_get_code.html", {"get_code_form": code_form}) - # set hidden fields with dob and badge number - return render(request, "fbl_auth_get_code.html") - else: - return render(request, "fbl_auth_start.html", {"form": form}) - else: - form = FblAuthForm() + auth_form.add_error(None, "Invalid badge number or date of birth") + + return render(request, "fbl_auth_start.html", {"form": auth_form}) - return render(request, "auth_start.html", {"form": form}) def fbl_authentication_get_code(request): - return render(request, "fbl_auth_get_code.html") \ No newline at end of file + code_form = FblAuthCodeForm(request.POST or None) + if request.method == "POST": + if code_form.is_valid(): + access_token = fbl_auth_validate_code( + badge_number=code_form.cleaned_data["badge_number"], + dob=code_form.cleaned_data["dob"], + validation_code=code_form.cleaned_data["validation_code"] + ) + if access_token: + account_info = fbl_auth_get_account(access_token) + fbl_account = get_or_create_account(account_info) + + login(request=request, user=fbl_account.user) + + if not fbl_account.user.email: + return redirect("fbl_complete_registration") + else: + return redirect("all_tickets") + + code_form.add_error(None, "Invalid validation code") + + + return render(request, "fbl_auth_get_code.html", {"get_code_form": code_form}) + +def complete_registration(request): + form = RegistrationCompletionForm(request.POST or None) + if request.method == "POST": + if form.is_valid(): + request.user.email = form.cleaned_data["email"] + request.user.save() + return redirect("all_tickets") + return render(request, "complete_registration.html", {"form": form}) \ No newline at end of file diff --git a/paw/settings.py b/paw/settings.py index 412cf7b..25ba106 100644 --- a/paw/settings.py +++ b/paw/settings.py @@ -190,3 +190,7 @@ GOOGLE_OAUTH_PROJECT_ID = environ['GOOGLE_OAUTH_PROJECT_ID'] GOOGLE_OAUTH_CLIENT_SECRET = environ['GOOGLE_OAUTH_CLIENT_SECRET'] GOOGLE_OAUTH_REDIRECT_URI = environ['GOOGLE_OAUTH_REDIRECT_URI'] GOOGLE_OAUTH_SCOPES = environ.get('GOOGLE_OAUTH_SCOPES', '').split(",") + +# FBL Integration +FBL_AUTH_ENABLED = environ.get('FBL_AUTH_ENABLED').lower() == 'true' +FBL_AUTH_SERVER = environ['FBL_AUTH_SERVER'] diff --git a/paw/static/css/paw.css b/paw/static/css/paw.css index 03898cf..766f45a 100644 --- a/paw/static/css/paw.css +++ b/paw/static/css/paw.css @@ -1310,18 +1310,6 @@ html { } } - .btn-outline.btn-primary:hover { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-primary:hover { - background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - } - } - .btn-outline.btn-accent:hover { --tw-text-opacity: 1; color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity))); @@ -1707,56 +1695,6 @@ html { height: auto; } -.stats { - display: inline-grid; - border-radius: var(--rounded-box, 1rem); - --tw-bg-opacity: 1; - background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); -} - -:where(.stats) { - grid-auto-flow: column; - overflow-x: auto; -} - -.stat { - display: inline-grid; - width: 100%; - grid-template-columns: repeat(1, 1fr); - -moz-column-gap: 1rem; - column-gap: 1rem; - border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); - --tw-border-opacity: 0.1; - padding-left: 1.5rem; - padding-right: 1.5rem; - padding-top: 1rem; - padding-bottom: 1rem; -} - -.stat-figure { - grid-column-start: 2; - grid-row: span 3 / span 3; - grid-row-start: 1; - place-self: center; - justify-self: end; -} - -.stat-title { - grid-column-start: 1; - white-space: nowrap; - color: var(--fallback-bc,oklch(var(--bc)/0.6)); -} - -.stat-value { - grid-column-start: 1; - white-space: nowrap; - font-size: 2.25rem; - line-height: 2.5rem; - font-weight: 800; -} - .steps { display: inline-grid; grid-auto-flow: column; @@ -1893,6 +1831,14 @@ html { var(--togglehandleborder); } +.alert-info { + border-color: var(--fallback-in,oklch(var(--in)/0.2)); + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); + --alert-bg: var(--fallback-in,oklch(var(--in)/1)); + --alert-bg-mix: var(--fallback-b1,oklch(var(--b1)/1)); +} + .alert-success { border-color: var(--fallback-su,oklch(var(--su)/0.2)); --tw-text-opacity: 1; @@ -1967,15 +1913,6 @@ html { color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity))); } -.badge-ghost { - --tw-border-opacity: 1; - border-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b2,oklch(var(--b2)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); -} - .badge-outline.badge-neutral { --tw-text-opacity: 1; color: var(--fallback-n,oklch(var(--n)/var(--tw-text-opacity))); @@ -2018,10 +1955,6 @@ html { border-color: var(--btn-color, var(--fallback-b2)); } - .btn-primary { - --btn-color: var(--fallback-p); - } - .btn-accent { --btn-color: var(--fallback-a); } @@ -2044,11 +1977,6 @@ html { } @supports (color: color-mix(in oklab, black, black)) { - .btn-outline.btn-primary.btn-active { - background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - } - .btn-outline.btn-accent.btn-active { background-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); border-color: color-mix(in oklab, var(--fallback-a,oklch(var(--a)/1)) 90%, black); @@ -2076,17 +2004,7 @@ html { outline-offset: 2px; } -.btn-primary { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); - outline-color: var(--fallback-p,oklch(var(--p)/1)); -} - @supports (color: oklch(0 0 0)) { - .btn-primary { - --btn-color: var(--p); - } - .btn-accent { --btn-color: var(--a); } @@ -2166,16 +2084,6 @@ html { background-color: var(--fallback-bc,oklch(var(--bc)/0.2)); } -.btn-outline.btn-primary { - --tw-text-opacity: 1; - color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); -} - -.btn-outline.btn-primary.btn-active { - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); -} - .btn-outline.btn-accent { --tw-text-opacity: 1; color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity))); @@ -2750,19 +2658,6 @@ html { } } -:where(.stats) > :not([hidden]) ~ :not([hidden]) { - --tw-divide-x-reverse: 0; - border-right-width: calc(1px * var(--tw-divide-x-reverse)); - border-left-width: calc(1px * calc(1 - var(--tw-divide-x-reverse))); - --tw-divide-y-reverse: 0; - border-top-width: calc(0px * calc(1 - var(--tw-divide-y-reverse))); - border-bottom-width: calc(0px * var(--tw-divide-y-reverse)); -} - -:is([dir="rtl"] .stats > :not([hidden]) ~ :not([hidden])) { - --tw-divide-x-reverse: 1; -} - .steps .step:before { top: 0px; grid-column-start: 1; @@ -3421,10 +3316,6 @@ html:has(.drawer-toggle:checked) { margin-left: 0.5rem; } -.ml-20 { - margin-left: 5rem; -} - .ml-4 { margin-left: 1rem; } @@ -3453,14 +3344,14 @@ html:has(.drawer-toggle:checked) { margin-top: 1rem; } -.mt-8 { - margin-top: 2rem; -} - .mt-6 { margin-top: 1.5rem; } +.mt-8 { + margin-top: 2rem; +} + .block { display: block; } @@ -3481,10 +3372,6 @@ html:has(.drawer-toggle:checked) { display: none; } -.h-10 { - height: 2.5rem; -} - .h-12 { height: 3rem; } @@ -3505,22 +3392,18 @@ html:has(.drawer-toggle:checked) { height: 1.5rem; } -.h-7 { - height: 1.75rem; -} - .h-full { height: 100%; } -.min-h-screen { - min-height: 100vh; -} - .min-h-full { min-height: 100%; } +.min-h-screen { + min-height: 100vh; +} + .w-10 { width: 2.5rem; } @@ -3529,10 +3412,6 @@ html:has(.drawer-toggle:checked) { width: 3rem; } -.w-20 { - width: 5rem; -} - .w-4 { width: 1rem; } @@ -3549,22 +3428,14 @@ html:has(.drawer-toggle:checked) { width: 1.5rem; } -.w-7 { - width: 1.75rem; +.w-72 { + width: 18rem; } .w-full { width: 100%; } -.w-80 { - width: 20rem; -} - -.w-72 { - width: 18rem; -} - .max-w-3xl { max-width: 48rem; } @@ -3601,6 +3472,10 @@ html:has(.drawer-toggle:checked) { flex-grow: 1; } +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + .cursor-pointer { cursor: pointer; } @@ -3645,10 +3520,6 @@ html:has(.drawer-toggle:checked) { align-self: center; } -.overflow-y-auto { - overflow-y: auto; -} - .whitespace-pre-line { white-space: pre-line; } @@ -3717,11 +3588,6 @@ html:has(.drawer-toggle:checked) { background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); } -.bg-base-content { - --tw-bg-opacity: 1; - background-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-bg-opacity))); -} - .stroke-current { stroke: currentColor; } @@ -3751,6 +3617,11 @@ html:has(.drawer-toggle:checked) { padding-right: 0.5rem; } +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; @@ -3761,11 +3632,6 @@ html:has(.drawer-toggle:checked) { padding-bottom: 0.5rem; } -.px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; -} - .text-center { text-align: center; } @@ -3835,26 +3701,11 @@ html:has(.drawer-toggle:checked) { color: var(--fallback-bc,oklch(var(--bc)/0.85)); } -.text-error { - --tw-text-opacity: 1; - color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity))); -} - .text-neutral-content { --tw-text-opacity: 1; color: var(--fallback-nc,oklch(var(--nc)/var(--tw-text-opacity))); } -.text-success { - --tw-text-opacity: 1; - color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity))); -} - -.text-warning { - --tw-text-opacity: 1; - color: var(--fallback-wa,oklch(var(--wa)/var(--tw-text-opacity))); -} - .text-white { --tw-text-opacity: 1; color: rgb(255 255 255 / var(--tw-text-opacity)); @@ -3868,273 +3719,15 @@ html:has(.drawer-toggle:checked) { opacity: 0.5; } -.opacity-70 { - opacity: 0.7; -} - .opacity-60 { opacity: 0.6; } -@media (min-width: 1024px) { - .lg\:btn { - display: inline-flex; - height: 3rem; - min-height: 3rem; - flex-shrink: 0; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - flex-wrap: wrap; - align-items: center; - justify-content: center; - border-radius: var(--rounded-btn, 0.5rem); - border-color: transparent; - border-color: oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity)); - padding-left: 1rem; - padding-right: 1rem; - text-align: center; - font-size: 0.875rem; - line-height: 1em; - gap: 0.5rem; - font-weight: 600; - text-decoration-line: none; - transition-duration: 200ms; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - border-width: var(--border-btn, 1px); - animation: button-pop var(--animation-btn, 0.25s) ease-out; - transition-property: color, background-color, border-color, opacity, box-shadow, transform; - --tw-text-opacity: 1; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); - --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - outline-color: var(--fallback-bc,oklch(var(--bc)/1)); - background-color: oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity)); - --tw-bg-opacity: 1; - --tw-border-opacity: 1; - } - - .lg\:btn[disabled],.lg\:btn:disabled { - pointer-events: none; - } - - :where(.lg\:btn:is(input[type="checkbox"])), -:where(.lg\:btn:is(input[type="radio"])) { - width: auto; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - } - - .lg\:btn:is(input[type="checkbox"]):after,.lg\:btn:is(input[type="radio"]):after { - --tw-content: attr(aria-label); - content: var(--tw-content); - } - - @media (hover: hover) { - .lg\:btn:hover { - --tw-border-opacity: 1; - border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .lg\:btn:hover { - background-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity, 1)) 90%, - black - ); - border-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity, 1)) 90%, - black - ); - } - } - - @supports not (color: oklch(0 0 0)) { - .lg\:btn:hover { - background-color: var(--btn-color, var(--fallback-b2)); - border-color: var(--btn-color, var(--fallback-b2)); - } - } - - .lg\:btn:hover { - --tw-border-opacity: 1; - border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .lg\:btn:hover { - background-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity, 1)) 90%, - black - ); - border-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity, 1)) 90%, - black - ); - } - } - - @supports not (color: oklch(0 0 0)) { - .lg\:btn:hover { - background-color: var(--btn-color, var(--fallback-b2)); - border-color: var(--btn-color, var(--fallback-b2)); - } - } - - .lg\:btn:hover { - --tw-border-opacity: 1; - border-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-b3,oklch(var(--b3)/var(--tw-bg-opacity))); - } - - @supports (color: color-mix(in oklab, black, black)) { - .lg\:btn:hover { - background-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-bg-opacity, 1)) 90%, - black - ); - border-color: color-mix( - in oklab, - oklch(var(--btn-color, var(--b2)) / var(--tw-border-opacity, 1)) 90%, - black - ); - } - } - - @supports not (color: oklch(0 0 0)) { - .lg\:btn:hover { - background-color: var(--btn-color, var(--fallback-b2)); - border-color: var(--btn-color, var(--fallback-b2)); - } - } - - .lg\:btn.glass:hover { - --glass-opacity: 25%; - --glass-border-opacity: 15%; - } - - .lg\:btn[disabled]:hover,.lg\:btn:disabled:hover { - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.2; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; - } - - @supports (color: color-mix(in oklab, black, black)) { - .lg\:btn:is(input[type="checkbox"]:checked):hover,.lg\:btn:is(input[type="radio"]:checked):hover { - background-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - border-color: color-mix(in oklab, var(--fallback-p,oklch(var(--p)/1)) 90%, black); - } - } - } - - .lg\:btn:active:hover,.lg\:btn:active:focus { - animation: button-pop 0s ease-out; - transform: scale(var(--btn-focus-scale, 0.97)); - } - - @supports not (color: oklch(0 0 0)) { - .lg\:btn { - background-color: var(--btn-color, var(--fallback-b2)); - border-color: var(--btn-color, var(--fallback-b2)); - } - } - - .lg\:btn:focus-visible { - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - } - - .lg\:btn.glass { - --tw-shadow: 0 0 #0000; - --tw-shadow-colored: 0 0 #0000; - box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); - outline-color: currentColor; - } - - .lg\:btn.glass.btn-active { - --glass-opacity: 25%; - --glass-border-opacity: 15%; - } - - .lg\:btn.btn-disabled,.lg\:btn[disabled],.lg\:btn:disabled { - --tw-border-opacity: 0; - background-color: var(--fallback-n,oklch(var(--n)/var(--tw-bg-opacity))); - --tw-bg-opacity: 0.2; - color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); - --tw-text-opacity: 0.2; - } - - .lg\:btn:is(input[type="checkbox"]:checked),.lg\:btn:is(input[type="radio"]:checked) { - --tw-border-opacity: 1; - border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); - --tw-bg-opacity: 1; - background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); - --tw-text-opacity: 1; - color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); - } - - .lg\:btn:is(input[type="checkbox"]:checked):focus-visible,.lg\:btn:is(input[type="radio"]:checked):focus-visible { - outline-color: var(--fallback-p,oklch(var(--p)/1)); - } - - .lg\:btn-sm { - height: 2rem; - min-height: 2rem; - padding-left: 0.75rem; - padding-right: 0.75rem; - font-size: 0.875rem; - } - - .lg\:btn-lg { - height: 4rem; - min-height: 4rem; - padding-left: 1.5rem; - padding-right: 1.5rem; - font-size: 1.125rem; - } - - .btn-square:where(.lg\:btn-sm) { - height: 2rem; - width: 2rem; - padding: 0px; - } - - .btn-square:where(.lg\:btn-lg) { - height: 4rem; - width: 4rem; - padding: 0px; - } - - .btn-circle:where(.lg\:btn-sm) { - height: 2rem; - width: 2rem; - border-radius: 9999px; - padding: 0px; - } - - .btn-circle:where(.lg\:btn-lg) { - height: 4rem; - width: 4rem; - border-radius: 9999px; - padding: 0px; - } +.opacity-70 { + opacity: 0.7; +} +@media (min-width: 1024px) { .lg\:drawer-open > .drawer-toggle { display: none; } @@ -4191,34 +3784,14 @@ html:has(.drawer-toggle:checked) { order: 0; } - .lg\:ml-72 { - margin-left: 18rem; - } - .lg\:block { display: block; } - .lg\:flex { - display: flex; - } - .lg\:hidden { display: none; } - .lg\:w-72 { - width: 18rem; - } - - .lg\:w-full { - width: 100%; - } - - .lg\:max-w-md { - max-width: 28rem; - } - .lg\:max-w-sm { max-width: 24rem; } @@ -4226,21 +3799,4 @@ html:has(.drawer-toggle:checked) { .lg\:flex-row { flex-direction: row; } - - .lg\:justify-start { - justify-content: flex-start; - } - - .lg\:p-2 { - padding: 0.5rem; - } - - .lg\:p-4 { - padding: 1rem; - } - - .lg\:px-3 { - padding-left: 0.75rem; - padding-right: 0.75rem; - } } \ No newline at end of file diff --git a/paw/templates/core/account_finish.html b/paw/templates/core/account_finish.html index b588893..bbd66a3 100644 --- a/paw/templates/core/account_finish.html +++ b/paw/templates/core/account_finish.html @@ -10,10 +10,10 @@

{% trans 'Register a new account' %}

    -
  • {% trans 'Authenticate' %}
  • -
  • {% trans 'Set up account' %}
  • -
  • {% trans 'Done' %}
  • -
+
  • {% trans 'Authenticate' %}
  • +
  • {% trans 'Set up account' %}
  • +
  • {% trans 'Done' %}
  • +
    {% csrf_token %} diff --git a/paw/templates/core/login.html b/paw/templates/core/login.html index 5830f4d..5aeb455 100644 --- a/paw/templates/core/login.html +++ b/paw/templates/core/login.html @@ -3,6 +3,9 @@ {% block content %} {% load i18n %}
    +
    + {% include 'partials/fbl_logo.html' %} +

    {% trans 'Log In' %}

    @@ -43,7 +46,7 @@
    {% if google_sso_enabled %} -
    +
    Staff Login
    {% trans 'Log in with Google' %} diff --git a/paw/templates/partials/logo.html b/paw/templates/partials/logo.html index 28da6d9..00509da 100644 --- a/paw/templates/partials/logo.html +++ b/paw/templates/partials/logo.html @@ -3,6 +3,6 @@
    - +
    {% endblock %} \ No newline at end of file diff --git a/theme/tailwind.config.js b/theme/tailwind.config.js index 290f2ed..0d68849 100644 --- a/theme/tailwind.config.js +++ b/theme/tailwind.config.js @@ -2,6 +2,7 @@ module.exports = { content: [ "../paw/templates/**/*.{html,js}", + "../fbl_integration/templates/**/*.{html,js}", "../status/templates/**/*.{html,js}", ], theme: { -- 2.51.2 From 5cf711b71db915bd1b1fce025c7ed913b7540e1a Mon Sep 17 00:00:00 2001 From: Alex Ottr Date: Sat, 6 Apr 2024 21:42:17 +0200 Subject: [PATCH 3/7] add strategic prints --- fbl_integration/utils.py | 3 +++ fbl_integration/views.py | 1 + 2 files changed, 4 insertions(+) diff --git a/fbl_integration/utils.py b/fbl_integration/utils.py index c346719..f09021b 100644 --- a/fbl_integration/utils.py +++ b/fbl_integration/utils.py @@ -8,6 +8,7 @@ def fbl_auth_request_code(badge_number: int, dob: str) -> bool: "password": dob }) if res.status_code != 201: + print(res.status_code, res.json()) return False return True @@ -18,6 +19,7 @@ def fbl_auth_validate_code(badge_number: str, dob: str, validation_code: str) -> "code": validation_code }) if res.status_code != 201: + print(res.status_code, res.json()) return None return res.json()["accessToken"] @@ -27,6 +29,7 @@ def fbl_auth_get_account(jwt_token: str) -> dict[str, str] | None: "Authorization": f"Bearer {jwt_token}" }) if res.status_code != 200: + print(res.status_code, res.json()) return None return res.json() diff --git a/fbl_integration/views.py b/fbl_integration/views.py index 21b5103..c51dacc 100644 --- a/fbl_integration/views.py +++ b/fbl_integration/views.py @@ -5,6 +5,7 @@ from .forms import FblAuthForm, FblAuthCodeForm, RegistrationCompletionForm from .utils import fbl_auth_request_code, fbl_auth_validate_code, fbl_auth_get_account, get_or_create_account def fbl_authentication_start(request): + print('fbl auth started') auth_form = FblAuthForm(request.POST or None) if request.method == "POST": if auth_form.is_valid(): -- 2.51.2 From 90b351ba640ebec06ebb7227f9e526d0360e68fb Mon Sep 17 00:00:00 2001 From: Alex Ottr Date: Sat, 6 Apr 2024 21:54:29 +0200 Subject: [PATCH 4/7] remove faulty prints --- fbl_integration/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fbl_integration/utils.py b/fbl_integration/utils.py index f09021b..a64b399 100644 --- a/fbl_integration/utils.py +++ b/fbl_integration/utils.py @@ -8,7 +8,7 @@ def fbl_auth_request_code(badge_number: int, dob: str) -> bool: "password": dob }) if res.status_code != 201: - print(res.status_code, res.json()) + print(res.status_code) return False return True @@ -19,7 +19,7 @@ def fbl_auth_validate_code(badge_number: str, dob: str, validation_code: str) -> "code": validation_code }) if res.status_code != 201: - print(res.status_code, res.json()) + print(res.status_code) return None return res.json()["accessToken"] @@ -29,7 +29,7 @@ def fbl_auth_get_account(jwt_token: str) -> dict[str, str] | None: "Authorization": f"Bearer {jwt_token}" }) if res.status_code != 200: - print(res.status_code, res.json()) + print(res.status_code) return None return res.json() -- 2.51.2 From 7c529211fdca2d1a05eeead2205b71e85390fe87 Mon Sep 17 00:00:00 2001 From: Alex Ottr Date: Sat, 6 Apr 2024 22:51:51 +0200 Subject: [PATCH 5/7] add fbl iteration and system user field --- fbl_integration/admin.py | 5 ++++- paw/__init__.py | 5 +++-- paw/templates/partials/logo.html | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fbl_integration/admin.py b/fbl_integration/admin.py index c766727..98df4aa 100644 --- a/fbl_integration/admin.py +++ b/fbl_integration/admin.py @@ -3,8 +3,11 @@ from .models import FblAccount @admin.register(FblAccount) class FblAccountAdmin(admin.ModelAdmin): - list_display = ["username", "badge_number", "status", "tags_secured", "created_at"] + list_display = ["system_username", "username", "badge_number", "status", "tags_secured", "created_at"] search_fields = ["badge_number", "username"] list_filter = ["status"] readonly_fields = ["badge_number", "account_id", "username", "status", "tags_secured", "created_at"] + def system_username(self, x): + return x.user.username + system_username.short_description = 'System Username' diff --git a/paw/__init__.py b/paw/__init__.py index 71a898a..81999d2 100644 --- a/paw/__init__.py +++ b/paw/__init__.py @@ -1,5 +1,6 @@ from django import get_version -VERSION = (0, 5, 8, "final", 0) +PAW_VERSION = (0, 5, 8, "final", 0) +FBL_ITERATION = 2 -__version__ = get_version(VERSION) +__version__ = f"{get_version(PAW_VERSION)}-fbl{FBL_ITERATION}" diff --git a/paw/templates/partials/logo.html b/paw/templates/partials/logo.html index 00509da..28da6d9 100644 --- a/paw/templates/partials/logo.html +++ b/paw/templates/partials/logo.html @@ -3,6 +3,6 @@
    - +
    {% endblock %} \ No newline at end of file -- 2.51.2 From 09b4f5eaecbf72602e0d59b5c1e9549a3069a622 Mon Sep 17 00:00:00 2001 From: Alex Ottr Date: Sun, 7 Apr 2024 14:59:26 +0200 Subject: [PATCH 6/7] adjust fbl partials --- fbl_integration/templates/partials/attendee_info.html | 9 +++++++++ paw/__init__.py | 2 +- paw/templates/partials/ticket_priority_badge.html | 1 + paw/templates/ticketing/ticket_detail.html | 2 ++ 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 fbl_integration/templates/partials/attendee_info.html diff --git a/fbl_integration/templates/partials/attendee_info.html b/fbl_integration/templates/partials/attendee_info.html new file mode 100644 index 0000000..6f7c582 --- /dev/null +++ b/fbl_integration/templates/partials/attendee_info.html @@ -0,0 +1,9 @@ +{% block attendee_info %} +{% load i18n %} +

    {% trans 'Rawrgister Info' %}

    +
    +{{ fbl_account.username }}
    #{{ fbl_account.badge_number }}
    + +{% trans 'Status' %}:
    {{ fbl_account.status }}
    +
    +{% endblock %} \ No newline at end of file diff --git a/paw/__init__.py b/paw/__init__.py index 81999d2..36f7bc8 100644 --- a/paw/__init__.py +++ b/paw/__init__.py @@ -1,6 +1,6 @@ from django import get_version PAW_VERSION = (0, 5, 8, "final", 0) -FBL_ITERATION = 2 +FBL_ITERATION = 3 __version__ = f"{get_version(PAW_VERSION)}-fbl{FBL_ITERATION}" diff --git a/paw/templates/partials/ticket_priority_badge.html b/paw/templates/partials/ticket_priority_badge.html index c912d93..159c60c 100644 --- a/paw/templates/partials/ticket_priority_badge.html +++ b/paw/templates/partials/ticket_priority_badge.html @@ -1,4 +1,5 @@ {% block ticket_priority_badge %} +{% load i18n %} {% if ticket.priority == 3 %} {% trans 'Low' %} {% elif ticket.priority == 2 %} diff --git a/paw/templates/ticketing/ticket_detail.html b/paw/templates/ticketing/ticket_detail.html index 9b7cd00..0d3668d 100644 --- a/paw/templates/ticketing/ticket_detail.html +++ b/paw/templates/ticketing/ticket_detail.html @@ -159,6 +159,8 @@
    {% endif %} + {% include 'partials/attendee_info.html' with fbl_account=ticket.user.fblaccount %} +

    {% trans 'Category' %}

    -- 2.51.2 From f5c56f0d5402165e6574bcf6bee34cd09ddbcd41 Mon Sep 17 00:00:00 2001 From: Alex Ottr Date: Sun, 7 Apr 2024 15:33:54 +0200 Subject: [PATCH 7/7] improve fbl partial --- fbl_integration/models.py | 3 ++ .../templates/partials/attendee_info.html | 18 +++++++-- paw/__init__.py | 2 +- paw/static/css/paw.css | 40 +++++++++++++++++++ 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/fbl_integration/models.py b/fbl_integration/models.py index c36f1ab..bfd7e7d 100644 --- a/fbl_integration/models.py +++ b/fbl_integration/models.py @@ -10,6 +10,9 @@ class FblAccount(models.Model): created_at = models.DateTimeField(auto_now_add=True) status = models.CharField(max_length=64) + def tags_list(self): + return self.tags_secured.split(',') + @classmethod def create_user(cls, username) -> PawUser: if not PawUser.objects.filter(username=username).exists(): diff --git a/fbl_integration/templates/partials/attendee_info.html b/fbl_integration/templates/partials/attendee_info.html index 6f7c582..894421d 100644 --- a/fbl_integration/templates/partials/attendee_info.html +++ b/fbl_integration/templates/partials/attendee_info.html @@ -1,9 +1,21 @@ {% block attendee_info %} {% load i18n %} +{% if fbl_account %}

    {% trans 'Rawrgister Info' %}

    -
    -{{ fbl_account.username }}
    #{{ fbl_account.badge_number }}
    -{% trans 'Status' %}:
    {{ fbl_account.status }}
    +
    +
    + {{ fbl_account.username }}
    #{{ fbl_account.badge_number }}
    +
    +
    +
    {{ fbl_account.status }}
    +
    +
    +

    {% trans 'Tags' %}

    + {% for tag in fbl_account.tags_list %} +
    {{ tag }}
    + {% endfor %} +
    +{% endif %} {% endblock %} \ No newline at end of file diff --git a/paw/__init__.py b/paw/__init__.py index 36f7bc8..398c8fb 100644 --- a/paw/__init__.py +++ b/paw/__init__.py @@ -1,6 +1,6 @@ from django import get_version PAW_VERSION = (0, 5, 8, "final", 0) -FBL_ITERATION = 3 +FBL_ITERATION = 4 __version__ = f"{get_version(PAW_VERSION)}-fbl{FBL_ITERATION}" diff --git a/paw/static/css/paw.css b/paw/static/css/paw.css index 766f45a..4372dcf 100644 --- a/paw/static/css/paw.css +++ b/paw/static/css/paw.css @@ -1889,6 +1889,14 @@ html { color: var(--fallback-ac,oklch(var(--ac)/var(--tw-text-opacity))); } +.badge-info { + border-color: transparent; + --tw-bg-opacity: 1; + background-color: var(--fallback-in,oklch(var(--in)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-inc,oklch(var(--inc)/var(--tw-text-opacity))); +} + .badge-success { border-color: transparent; --tw-bg-opacity: 1; @@ -1913,16 +1921,38 @@ html { color: var(--fallback-erc,oklch(var(--erc)/var(--tw-text-opacity))); } +.badge-outline { + border-color: currentColor; + --tw-border-opacity: 0.5; + background-color: transparent; + color: currentColor; +} + .badge-outline.badge-neutral { --tw-text-opacity: 1; color: var(--fallback-n,oklch(var(--n)/var(--tw-text-opacity))); } +.badge-outline.badge-primary { + --tw-text-opacity: 1; + color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); +} + +.badge-outline.badge-secondary { + --tw-text-opacity: 1; + color: var(--fallback-s,oklch(var(--s)/var(--tw-text-opacity))); +} + .badge-outline.badge-accent { --tw-text-opacity: 1; color: var(--fallback-a,oklch(var(--a)/var(--tw-text-opacity))); } +.badge-outline.badge-info { + --tw-text-opacity: 1; + color: var(--fallback-in,oklch(var(--in)/var(--tw-text-opacity))); +} + .badge-outline.badge-success { --tw-text-opacity: 1; color: var(--fallback-su,oklch(var(--su)/var(--tw-text-opacity))); @@ -3292,6 +3322,16 @@ html:has(.drawer-toggle:checked) { margin-bottom: 1rem; } +.my-1 { + margin-top: 0.25rem; + margin-bottom: 0.25rem; +} + +.my-2 { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + .mb-1 { margin-bottom: 0.25rem; }