From 42e9de6942e2bce861a41f67bf903610cae12a6a Mon Sep 17 00:00:00 2001 From: Kumi Date: Wed, 27 Mar 2024 09:22:04 +0100 Subject: [PATCH] feat: enhance UI and extend form functionality This update brings several enhancements to the user interface and extends the forms functionality across the application. Specifically, the changes include the introduction of `django-crispy-forms` and `crispy-bootstrap5` to improve form aesthetics and usability significantly. Additionally, the admin area now features enhanced link color for better visibility, and user interface improvements have been made across various templates, like adding direct edit and delete category links and more intuitive navigation options for better user experience. Moreover, the inclusion of new forms for Scene, Category, and Original Media creation aligns with the app's need for structured data entry and complements the existing models by ensuring a more user-friendly interaction with the database. Key changes include: - Introduction of `django-crispy-forms` and `crispy-bootstrap5` for better form rendering. - UI enhancements for clarity and ease of use in the admin area and templates. - New forms for Scene, Category, and Original Media to streamline content creation processes. These changes aim to improve both the appearance and functionality of the application, making it more appealing and accessible to users while facilitating easier content management. --- assets/css/userarea.css | 4 +++ pyproject.toml | 2 ++ quackscape/settings.py | 11 ++++++-- quackscape/tours/forms.py | 21 +++++++++++++++ .../migrations/0018_category_description.py | 18 +++++++++++++ quackscape/tours/models.py | 1 + quackscape/users/templates/users/base.html | 3 ++- .../users/templates/users/categories.html | 2 +- .../users/templates/users/category.html | 15 ++++++++--- .../users/templates/users/generic_form.html | 27 +++++++++++++++++++ quackscape/users/templates/users/login.html | 2 +- quackscape/users/urls.py | 3 ++- quackscape/users/views.py | 26 +++++++++++++++--- 13 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 quackscape/tours/forms.py create mode 100644 quackscape/tours/migrations/0018_category_description.py create mode 100644 quackscape/users/templates/users/generic_form.html diff --git a/assets/css/userarea.css b/assets/css/userarea.css index 68b79d5..83be2c8 100644 --- a/assets/css/userarea.css +++ b/assets/css/userarea.css @@ -227,3 +227,7 @@ table.dataTable { .error-message { color: red; } + +.admin-header a { + color: #9cdcfe; +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index e38f349..461d9ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,8 @@ boto3 = "*" argon2-cffi = "*" django-csp = "*" django-rest-polymorphic = "*" +django-crispy-forms = "*" +crispy-bootstrap5 = "*" [tool.poetry.group.mysql.dependencies] mysqlclient = "*" diff --git a/quackscape/settings.py b/quackscape/settings.py index 2519556..bf1fa22 100644 --- a/quackscape/settings.py +++ b/quackscape/settings.py @@ -39,6 +39,8 @@ INSTALLED_APPS = [ "django_celery_results", "drf_spectacular", "drf_spectacular_sidecar", + "crispy_forms", + "crispy_bootstrap5", "quackscape", "quackscape.users", "quackscape.tours", @@ -218,7 +220,7 @@ QUACKSCAPE_CONTENT_RESOLUTIONS = [ (65536, 32768), ] -MAX_IMAGE_PIXELS = 34359738368 # 262144x131072 - should be enough, no? +MAX_IMAGE_PIXELS = 34359738368 # 262144x131072 - should be enough, no? # ffmpeg settings @@ -248,4 +250,9 @@ FFMPEG_DEFAULT_OPTION = ASK.config.get("ffmpeg", "DefaultOption", fallback="defa LOGIN_URL = reverse_lazy("quackscape.users:login") LOGIN_REDIRECT_URL = reverse_lazy("quackscape.users:categories") -LOGOUT_REDIRECT_URL = reverse_lazy("quackscape.users:login") \ No newline at end of file +LOGOUT_REDIRECT_URL = reverse_lazy("quackscape.users:login") + +# Crispy forms settings + +CRISPY_ALLOWED_TEMPLATE_PACKS = {"bootstrap5"} +CRISPY_TEMPLATE_PACK = "bootstrap5" \ No newline at end of file diff --git a/quackscape/tours/forms.py b/quackscape/tours/forms.py new file mode 100644 index 0000000..4dcc03c --- /dev/null +++ b/quackscape/tours/forms.py @@ -0,0 +1,21 @@ +from django.forms import ModelForm + +from .models import Scene, Element, Category, OriginalMedia + + +class SceneForm(ModelForm): + class Meta: + model = Scene + fields = ("title", "description") + + +class CategoryForm(ModelForm): + class Meta: + model = Category + fields = ("title", "description") + + +class OriginalMediaForm(ModelForm): + class Meta: + model = OriginalMedia + fields = ("title", "description") diff --git a/quackscape/tours/migrations/0018_category_description.py b/quackscape/tours/migrations/0018_category_description.py new file mode 100644 index 0000000..3369996 --- /dev/null +++ b/quackscape/tours/migrations/0018_category_description.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.3 on 2024-03-27 07:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tours', '0017_originalvideo_thumbnail'), + ] + + operations = [ + migrations.AddField( + model_name='category', + name='description', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/quackscape/tours/models.py b/quackscape/tours/models.py index 42f7e76..b8cc5a3 100644 --- a/quackscape/tours/models.py +++ b/quackscape/tours/models.py @@ -22,6 +22,7 @@ def upload_to(instance, filename): class Category(models.Model): id = models.UUIDField(default=uuid.uuid4, primary_key=True) title = models.CharField(max_length=100) + description = models.TextField(null=True, blank=True) owner = models.ForeignKey( get_user_model(), related_name="owned_categories", diff --git a/quackscape/users/templates/users/base.html b/quackscape/users/templates/users/base.html index efa6be2..b7a98c5 100644 --- a/quackscape/users/templates/users/base.html +++ b/quackscape/users/templates/users/base.html @@ -11,7 +11,8 @@

Quackscape

{% if user.is_authenticated %} -

Logged in as {{ user.email }}

+

Logged in as {{ user.email }} – + Logout

{% else %}

Not logged in

{% endif %} diff --git a/quackscape/users/templates/users/categories.html b/quackscape/users/templates/users/categories.html index aad002c..b64d0b6 100644 --- a/quackscape/users/templates/users/categories.html +++ b/quackscape/users/templates/users/categories.html @@ -15,7 +15,7 @@

Available categories

- + Create category
    {% for category in categories %} diff --git a/quackscape/users/templates/users/category.html b/quackscape/users/templates/users/category.html index 763f2bf..41497e6 100644 --- a/quackscape/users/templates/users/category.html +++ b/quackscape/users/templates/users/category.html @@ -3,9 +3,16 @@

    {{ category.title }}

    {% if category in request.user.category_memberships %} - {% endif %} + {% endif %} {% if request.user.is_superuser or request.user == category.owner %} - +
    + Edit category + Delete category +
    {% endif %}
@@ -142,7 +149,9 @@
User permissions
- +
diff --git a/quackscape/users/templates/users/generic_form.html b/quackscape/users/templates/users/generic_form.html new file mode 100644 index 0000000..fff161d --- /dev/null +++ b/quackscape/users/templates/users/generic_form.html @@ -0,0 +1,27 @@ +{% extends "users/base.html" %} +{% load crispy_forms_tags %} + +{% block content %} +
+
+
+
+
+

{{ title }}

+
+
+
+ {% csrf_token %} + {{ form|crispy }} {% if form.non_field_errors %} +
+ {% for error in form.non_field_errors %} {{ error }} {% endfor %} +
+ {% endif %} + + +
+
+
+
+
+{% endblock %} diff --git a/quackscape/users/templates/users/login.html b/quackscape/users/templates/users/login.html index 42cf1d7..5da7641 100644 --- a/quackscape/users/templates/users/login.html +++ b/quackscape/users/templates/users/login.html @@ -6,7 +6,7 @@
-

Login

+

{{ title }}

diff --git a/quackscape/users/urls.py b/quackscape/users/urls.py index a0a0690..0fdfe45 100644 --- a/quackscape/users/urls.py +++ b/quackscape/users/urls.py @@ -1,4 +1,4 @@ -from .views import UserAreaMainView, CategoriesView, CategoryView, FileUploadView, Login, Logout +from .views import UserAreaMainView, CategoriesView, CategoryView, FileUploadView, Login, Logout, CategoryCreateView from django.urls import path @@ -7,6 +7,7 @@ app_name = 'quackscape.users' urlpatterns = [ path('', UserAreaMainView.as_view(), name='user-area-main'), path('categories/', CategoriesView.as_view(), name='categories'), + path('category/create/', CategoryCreateView.as_view(), name='category-create'), path('category//', CategoryView.as_view(), name='category'), path('category//upload/', FileUploadView.as_view(), name='media-upload'), path('login/', Login.as_view(), name='login'), diff --git a/quackscape/users/views.py b/quackscape/users/views.py index 2f34c7f..f1b5419 100644 --- a/quackscape/users/views.py +++ b/quackscape/users/views.py @@ -1,7 +1,15 @@ -from django.views.generic import TemplateView, ListView, DetailView +from django.views.generic import ( + TemplateView, + ListView, + DetailView, + CreateView, + UpdateView, + DeleteView, +) from django.http import Http404 from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.views import LoginView, LogoutView +from django.urls import reverse_lazy from rest_framework.parsers import MultiPartParser, FormParser from rest_framework.response import Response @@ -14,6 +22,7 @@ from quackscape.tours.serializers import ( OriginalImageSerializer, OriginalVideoSerializer, ) +from quackscape.tours.forms import CategoryForm class TitleMixin: @@ -63,9 +72,20 @@ class MediaUploadView(LoginRequiredMixin, TitleMixin, TemplateView): title = "Upload Media" -class CategoryCreateView(LoginRequiredMixin, TitleMixin, TemplateView): - template_name = "users/category_create.html" +class CategoryCreateView(LoginRequiredMixin, TitleMixin, CreateView): + template_name = "users/generic_form.html" title = "Create Category" + form_class = CategoryForm + model = Category + + def form_valid(self, form): + form.instance.owner = self.request.user + return super().form_valid(form) + + def get_success_url(self): + return reverse_lazy( + "quackscape.users:category", kwargs={"category": self.object.id} + ) class FileUploadView(LoginRequiredMixin, GenericAPIView):