From d9a0e808b0d68115fec3aee70b1bdd90f46741e5 Mon Sep 17 00:00:00 2001 From: Pascal Repond Date: Tue, 6 Jan 2026 15:30:19 +0100 Subject: [PATCH] refactor: reorganize templates into subdirectories - Enhance template directories structure for better organization. - Remove useless tests. Co-Authored-by: Pascal Repond --- src/core/templatetags/media_tags.py | 2 +- src/core/views.py | 22 +++---- src/templates/accounts/profile_edit.html | 10 ++-- src/templates/{ => base}/backup_manage.html | 4 +- src/templates/{ => base}/base.html | 2 +- src/templates/{ => base}/base_auth.html | 0 src/templates/{ => base}/media_detail.html | 10 ++-- src/templates/{ => base}/media_edit.html | 26 ++++----- .../{media.html => base/media_index.html} | 8 +-- .../confirm_modal.html} | 0 .../partials/{ => common}/field_label.html | 0 .../load_more_trigger.html} | 4 +- .../partials/{ => common}/spinner.html | 0 .../contributor_chip.html} | 0 .../contributors_suggestions.html} | 0 src/templates/partials/media-items-page.html | 4 -- .../media_contributors.html} | 0 .../media_cover.html} | 0 .../media_edit_button.html} | 0 .../media_icon.html} | 0 .../media_item.html} | 32 +++++----- .../media_list.html} | 4 +- .../partials/media_items/media_list_page.html | 4 ++ .../media_review_clamped.html} | 0 .../media_review_full.html} | 0 .../media_status_badge.html} | 0 .../score/media_score_badge.html} | 0 .../score/media_score_stars.html} | 6 +- .../score/media_score_stars_inner.html} | 2 +- .../filters_drawer.html} | 0 .../sidebar_nav.html} | 0 .../view_mode_toggle.html} | 0 src/templates/registration/login.html | 2 +- src/tests/accounts/test_views.py | 58 ------------------- src/tests/core/test_views.py | 21 ++----- 35 files changed, 80 insertions(+), 141 deletions(-) rename src/templates/{ => base}/backup_manage.html (95%) rename src/templates/{ => base}/base.html (97%) rename src/templates/{ => base}/base_auth.html (100%) rename src/templates/{ => base}/media_detail.html (92%) rename src/templates/{ => base}/media_edit.html (84%) rename src/templates/{media.html => base/media_index.html} (97%) rename src/templates/partials/{confirm-modal.html => common/confirm_modal.html} (100%) rename src/templates/partials/{ => common}/field_label.html (100%) rename src/templates/partials/{load-more-trigger.html => common/load_more_trigger.html} (76%) rename src/templates/partials/{ => common}/spinner.html (100%) rename src/templates/partials/{contributor-chip.html => contributors/contributor_chip.html} (100%) rename src/templates/partials/{contributors-suggestions.html => contributors/contributors_suggestions.html} (100%) delete mode 100644 src/templates/partials/media-items-page.html rename src/templates/partials/{media-contributors.html => media_items/media_contributors.html} (100%) rename src/templates/partials/{media-cover.html => media_items/media_cover.html} (100%) rename src/templates/partials/{media-edit-button.html => media_items/media_edit_button.html} (100%) rename src/templates/partials/{media-icon.html => media_items/media_icon.html} (100%) rename src/templates/partials/{media-items.html => media_items/media_item.html} (76%) rename src/templates/partials/{media-list.html => media_items/media_list.html} (87%) create mode 100644 src/templates/partials/media_items/media_list_page.html rename src/templates/partials/{media-review-clamped.html => media_items/media_review_clamped.html} (100%) rename src/templates/partials/{media-review-full.html => media_items/media_review_full.html} (100%) rename src/templates/partials/{media-status-badge.html => media_items/media_status_badge.html} (100%) rename src/templates/partials/{media-score-badge.html => media_items/score/media_score_badge.html} (100%) rename src/templates/partials/{media-score-stars.html => media_items/score/media_score_stars.html} (70%) rename src/templates/partials/{media-score-stars-inner.html => media_items/score/media_score_stars_inner.html} (83%) rename src/templates/partials/{filters-drawer.html => navigation/filters_drawer.html} (100%) rename src/templates/partials/{sidebar-nav.html => navigation/sidebar_nav.html} (100%) rename src/templates/partials/{view-mode-toggle.html => navigation/view_mode_toggle.html} (100%) diff --git a/src/core/templatetags/media_tags.py b/src/core/templatetags/media_tags.py index e888d5f..32afd12 100644 --- a/src/core/templatetags/media_tags.py +++ b/src/core/templatetags/media_tags.py @@ -31,7 +31,7 @@ } -@register.inclusion_tag("partials/media-icon.html") +@register.inclusion_tag("partials/media_items/media_icon.html") def media_icon(media_type, size="sm"): """ Render a heroicon based on media type. diff --git a/src/core/views.py b/src/core/views.py index 3e7e229..33bebac 100644 --- a/src/core/views.py +++ b/src/core/views.py @@ -20,7 +20,7 @@ def index(request): """Main view for displaying media list.""" context = build_media_context(request) - return render(request, "media.html", context) + return render(request, "base/media_index.html", context) @login_required @@ -28,7 +28,7 @@ def media_detail(request, pk): """Display detailed view of a single media item.""" media = get_object_or_404(Media, pk=pk) context = {"media": media} - return render(request, "media_detail.html", context) + return render(request, "base/media_detail.html", context) @login_required @@ -67,7 +67,7 @@ def media_edit(request, pk=None): else: form = MediaForm(instance=media) context = {"media": media, "form": form} - return render(request, "media_edit.html", context) + return render(request, "base/media_edit.html", context) @login_required @@ -88,14 +88,14 @@ def load_more_media(request): context = build_media_context(request) # Return only the items + load more button - return render(request, "partials/media-items-page.html", context) + return render(request, "partials/media_items/media_list_page.html", context) @login_required def agent_search_htmx(request): query = request.GET.get("q", "").strip() agents = Agent.objects.filter(name__icontains=query).order_by("name")[:12] if query else [] - return render(request, "partials/contributors-suggestions.html", {"agents": agents}) + return render(request, "partials/contributors/contributors_suggestions.html", {"agents": agents}) @login_required @@ -104,23 +104,25 @@ def agent_select_htmx(request): agent_id = request.POST.get("id") try: agent = Agent.objects.get(pk=agent_id) - return render(request, "partials/contributor-chip.html", {"agent": agent}) + return render(request, "partials/contributors/contributor_chip.html", {"agent": agent}) except Agent.DoesNotExist: - return render(request, "partials/contributor-chip.html", {"agent": None, "error": "Agent not found"}) + return render( + request, "partials/contributors/contributor_chip.html", {"agent": None, "error": "Agent not found"} + ) @login_required def media_review_clamped_htmx(request, pk): """HTMX view: return clamped review for a media item (for table cell collapse).""" media = get_object_or_404(Media, pk=pk) - return render(request, "partials/media-review-clamped.html", {"media": media}) + return render(request, "partials/media_items/media_review_clamped.html", {"media": media}) @login_required def media_review_full_htmx(request, pk): """HTMX view: return full review for a media item (for table cell expansion).""" media = get_object_or_404(Media, pk=pk) - return render(request, "partials/media-review-full.html", {"media": media}) + return render(request, "partials/media_items/media_review_full.html", {"media": media}) @login_required @@ -186,4 +188,4 @@ def backup_import(request): @login_required def backup_manage(request): """Display backup management page.""" - return render(request, "backup_manage.html") + return render(request, "base/backup_manage.html") diff --git a/src/templates/accounts/profile_edit.html b/src/templates/accounts/profile_edit.html index bb231af..2aa3b14 100644 --- a/src/templates/accounts/profile_edit.html +++ b/src/templates/accounts/profile_edit.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% load i18n %} {% block title %} {% translate "Edit Profile" %} - Datakult @@ -22,7 +22,7 @@

{% translate "Profile Information" %}

{{ profile_form.username }}
{{ profile_form.email }}
{{ profile_form.first_name }}
{{ profile_form.last_name }}
{# Confirmation modal for backup import #} - {% include "partials/confirm-modal.html" with modal_id="confirm-import-modal" title=_("⚠️ WARNING: Destructive Action") message=_("This action will DELETE ALL your current data and replace it with the backup data. Are you absolutely sure you want to continue?") confirm_text=_("Yes, import backup") is_danger=True form_id="import-backup-form" %} + {% include "partials/common/confirm_modal.html" with modal_id="confirm-import-modal" title=_("⚠️ WARNING: Destructive Action") message=_("This action will DELETE ALL your current data and replace it with the backup data. Are you absolutely sure you want to continue?") confirm_text=_("Yes, import backup") is_danger=True form_id="import-backup-form" %} diff --git a/src/templates/base_auth.html b/src/templates/base/base_auth.html similarity index 100% rename from src/templates/base_auth.html rename to src/templates/base/base_auth.html diff --git a/src/templates/media_detail.html b/src/templates/base/media_detail.html similarity index 92% rename from src/templates/media_detail.html rename to src/templates/base/media_detail.html index 028a5c7..8dab52d 100644 --- a/src/templates/media_detail.html +++ b/src/templates/base/media_detail.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% load i18n %} {% load media_tags %} {% block title %} @@ -51,7 +51,9 @@

{% if media.contributors.all %}
{% heroicon_mini "user" class="opacity-50 shrink-0" %} -
{% include "partials/media-contributors.html" with use_htmx=False %}
+
+ {% include "partials/media_items/media_contributors.html" with use_htmx=False %} +
{% endif %} {# External link #} @@ -75,11 +77,11 @@

{# Score with stars #} {% if media.score %} -
{% include "partials/media-score-stars.html" %}
+
{% include "partials/media_items/score/media_score_stars.html" %}
{% endif %} {# Status and Review date #}
-
{% include "partials/media-status-badge.html" %}
+
{% include "partials/media_items/media_status_badge.html" %}
{% if media.review_date %}
{% heroicon_mini "calendar" %} diff --git a/src/templates/media_edit.html b/src/templates/base/media_edit.html similarity index 84% rename from src/templates/media_edit.html rename to src/templates/base/media_edit.html index af06331..103f456 100644 --- a/src/templates/media_edit.html +++ b/src/templates/base/media_edit.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "base/base.html" %} {% load i18n %} {% load static %} {% block title %} @@ -51,7 +51,7 @@

{{ form.cover }}
@@ -53,7 +55,7 @@

{{ media.title }}

{# Cover with media type badge #} - {% include "partials/media-cover.html" %} + {% include "partials/media_items/media_cover.html" %} {# Title, contributors #} @@ -67,11 +69,11 @@

{% if media.contributors.all %} -
{% include "partials/media-contributors.html" %}
+
{% include "partials/media_items/media_contributors.html" %}
{% endif %} {# Show badge on small screens only (hidden from md+) #} {% if media.score %} -
{% include "partials/media-score-badge.html" with size="sm" %}
+
{% include "partials/media_items/score/media_score_badge.html" with size="sm" %}
{% endif %}

@@ -79,17 +81,19 @@

{% if media.score %} {# Show only badge on medium screens, stars + badge on large screens #} - -
{% include "partials/media-score-badge.html" with size="sm" %}
+ +
{% include "partials/media_items/score/media_score_badge.html" with size="sm" %}
{% endif %} {% if media.review %}
- {% include "partials/media-review-clamped.html" %} + {% include "partials/media_items/media_review_clamped.html" %}
{% endif %} {# Status #} - {% include "partials/media-status-badge.html" %} + {% include "partials/media_items/media_status_badge.html" %} {# Review date #} {% if media.review_date %} @@ -99,7 +103,7 @@

{% endif %} {# Actions #} - {% include "partials/media-edit-button.html" %} + {% include "partials/media_items/media_edit_button.html" %} {% endfor %} {% endif %} diff --git a/src/templates/partials/media-list.html b/src/templates/partials/media_items/media_list.html similarity index 87% rename from src/templates/partials/media-list.html rename to src/templates/partials/media_items/media_list.html index 6c837cd..6ee10a4 100644 --- a/src/templates/partials/media-list.html +++ b/src/templates/partials/media_items/media_list.html @@ -4,7 +4,7 @@ {% if view_mode == 'grid' %} {# Grid view - Cards layout #}
{% include "partials/media-items-page.html" %}
+ id="media-container">{% include "partials/media_items/media_list_page.html" %}

{% else %} {# List view - Table layout #}
@@ -20,7 +20,7 @@ - {% include "partials/media-items-page.html" %} + {% include "partials/media_items/media_list_page.html" %}
diff --git a/src/templates/partials/media_items/media_list_page.html b/src/templates/partials/media_items/media_list_page.html new file mode 100644 index 0000000..aaad28d --- /dev/null +++ b/src/templates/partials/media_items/media_list_page.html @@ -0,0 +1,4 @@ +{% load i18n %} +{# This partial renders media items for a single page + the load more trigger #} +{% include "partials/media_items/media_item.html" %} +{% include "partials/common/load_more_trigger.html" %} diff --git a/src/templates/partials/media-review-clamped.html b/src/templates/partials/media_items/media_review_clamped.html similarity index 100% rename from src/templates/partials/media-review-clamped.html rename to src/templates/partials/media_items/media_review_clamped.html diff --git a/src/templates/partials/media-review-full.html b/src/templates/partials/media_items/media_review_full.html similarity index 100% rename from src/templates/partials/media-review-full.html rename to src/templates/partials/media_items/media_review_full.html diff --git a/src/templates/partials/media-status-badge.html b/src/templates/partials/media_items/media_status_badge.html similarity index 100% rename from src/templates/partials/media-status-badge.html rename to src/templates/partials/media_items/media_status_badge.html diff --git a/src/templates/partials/media-score-badge.html b/src/templates/partials/media_items/score/media_score_badge.html similarity index 100% rename from src/templates/partials/media-score-badge.html rename to src/templates/partials/media_items/score/media_score_badge.html diff --git a/src/templates/partials/media-score-stars.html b/src/templates/partials/media_items/score/media_score_stars.html similarity index 70% rename from src/templates/partials/media-score-stars.html rename to src/templates/partials/media_items/score/media_score_stars.html index 168759a..fe5815c 100644 --- a/src/templates/partials/media-score-stars.html +++ b/src/templates/partials/media_items/score/media_score_stars.html @@ -4,15 +4,15 @@ {# Parameters: show_badge (optional) - show score badge, default True #} {% if size == 'sm' %} {% with rating_size="rating-sm" badge_size="sm" gap_size="gap-2" %} - {% include "partials/media-score-stars-inner.html" %} + {% include "partials/media_items/score/media_score_stars_inner.html" %} {% endwith %} {% elif size == 'lg' %} {% with rating_size="rating-lg" badge_size="lg" gap_size="gap-3" %} - {% include "partials/media-score-stars-inner.html" %} + {% include "partials/media_items/score/media_score_stars_inner.html" %} {% endwith %} {% else %} {# Default: medium size #} {% with rating_size="rating-md" badge_size="md" gap_size="gap-2" %} - {% include "partials/media-score-stars-inner.html" %} + {% include "partials/media_items/score/media_score_stars_inner.html" %} {% endwith %} {% endif %} diff --git a/src/templates/partials/media-score-stars-inner.html b/src/templates/partials/media_items/score/media_score_stars_inner.html similarity index 83% rename from src/templates/partials/media-score-stars-inner.html rename to src/templates/partials/media_items/score/media_score_stars_inner.html index 6b203ea..37a2e1e 100644 --- a/src/templates/partials/media-score-stars-inner.html +++ b/src/templates/partials/media_items/score/media_score_stars_inner.html @@ -12,6 +12,6 @@
{# Score badge with label (optional) #} {% if show_badge != False %} -
{% include "partials/media-score-badge.html" with size=badge_size %}
+ {% include "partials/media_items/score/media_score_badge.html" with size=badge_size %} {% endif %} diff --git a/src/templates/partials/filters-drawer.html b/src/templates/partials/navigation/filters_drawer.html similarity index 100% rename from src/templates/partials/filters-drawer.html rename to src/templates/partials/navigation/filters_drawer.html diff --git a/src/templates/partials/sidebar-nav.html b/src/templates/partials/navigation/sidebar_nav.html similarity index 100% rename from src/templates/partials/sidebar-nav.html rename to src/templates/partials/navigation/sidebar_nav.html diff --git a/src/templates/partials/view-mode-toggle.html b/src/templates/partials/navigation/view_mode_toggle.html similarity index 100% rename from src/templates/partials/view-mode-toggle.html rename to src/templates/partials/navigation/view_mode_toggle.html diff --git a/src/templates/registration/login.html b/src/templates/registration/login.html index 593c17f..c4b23f0 100644 --- a/src/templates/registration/login.html +++ b/src/templates/registration/login.html @@ -1,4 +1,4 @@ -{% extends "base_auth.html" %} +{% extends "base/base_auth.html" %} {% load i18n %} {% block title %} {% translate "Log in" %} - Datakult diff --git a/src/tests/accounts/test_views.py b/src/tests/accounts/test_views.py index 18a96a2..2a6521d 100644 --- a/src/tests/accounts/test_views.py +++ b/src/tests/accounts/test_views.py @@ -52,28 +52,6 @@ def test_update_profile_success(self, logged_in_client, user): assert user.first_name == "New" assert user.last_name == "Name" - def test_update_profile_invalid_username(self, logged_in_client, user, django_user_model): - """Submitting a duplicate username shows an error.""" - # Create another user with the username we'll try to use - django_user_model.objects.create_user( - username="existinguser", - password="pass123", - ) - - url = reverse("accounts:profile_edit") - data = { - "username": "existinguser", - "email": user.email, - "first_name": user.first_name, - "last_name": user.last_name, - "update_profile": "", - } - - response = logged_in_client.post(url, data) - - assert response.status_code == 200 # Form re-displayed with errors - assert response.context["profile_form"].errors - def test_change_password_success(self, logged_in_client, user): """Submitting valid password data changes the password.""" url = reverse("accounts:profile_edit") @@ -90,37 +68,11 @@ def test_change_password_success(self, logged_in_client, user): user.refresh_from_db() assert user.check_password("newSecurePass456!") - def test_change_password_wrong_old_password(self, logged_in_client): - """Submitting wrong old password shows an error.""" - url = reverse("accounts:profile_edit") - data = { - "old_password": "wrongpassword", - "new_password1": "newSecurePass456!", - "new_password2": "newSecurePass456!", - "change_password": "", - } - response = logged_in_client.post(url, data) assert response.status_code == 200 # Form re-displayed with errors assert response.context["password_form"].errors - def test_user_stays_logged_in_after_password_change(self, logged_in_client): - """User remains logged in after changing password.""" - url = reverse("accounts:profile_edit") - data = { - "old_password": "testpass123", - "new_password1": "newSecurePass456!", - "new_password2": "newSecurePass456!", - "change_password": "", - } - - logged_in_client.post(url, data) - - # Check user is still authenticated by accessing a protected page - response = logged_in_client.get(reverse("accounts:profile_edit")) - assert response.status_code == 200 - class TestSetLanguageView: """Tests for the set_language view.""" @@ -132,13 +84,3 @@ def test_set_valid_language(self, logged_in_client): assert response.status_code == 302 assert logged_in_client.session["django_language"] == "fr" - - def test_set_invalid_language(self, logged_in_client): - """Setting an invalid language is rejected.""" - url = reverse("accounts:set_language") - logged_in_client.post(url, {"language": "invalid"}) - - assert ( - "django_language" not in logged_in_client.session - or logged_in_client.session["django_language"] != "invalid" - ) diff --git a/src/tests/core/test_views.py b/src/tests/core/test_views.py index 6daf0b9..0e416a7 100644 --- a/src/tests/core/test_views.py +++ b/src/tests/core/test_views.py @@ -30,17 +30,6 @@ def test_index_displays_media_list(self, logged_in_client, media): assert response.status_code == 200 assert "media_list" in response.context - def test_index_htmx_request_returns_partial(self, logged_in_client, media): - """HTMX requests return the partial template.""" - response = logged_in_client.get( - reverse("home"), - HTTP_HX_REQUEST="true", - ) - - assert response.status_code == 200 - # Should use the partial template, not the full page - assert "partials/media-list.html" in [t.name for t in response.templates] - class TestMediaEditView: """Tests for the media_edit view.""" @@ -353,7 +342,7 @@ def test_media_review_clamped_returns_partial(self, logged_in_client, db): response = logged_in_client.get(reverse("media_review_clamped_htmx", kwargs={"pk": media.pk})) assert response.status_code == 200 - assert "partials/media-review-clamped.html" in [t.name for t in response.templates] + assert "partials/media_items/media_review_clamped.html" in [t.name for t in response.templates] assert "media" in response.context assert response.context["media"] == media @@ -394,7 +383,7 @@ def test_media_review_full_returns_partial(self, logged_in_client, db): response = logged_in_client.get(reverse("media_review_full_htmx", kwargs={"pk": media.pk})) assert response.status_code == 200 - assert "partials/media-review-full.html" in [t.name for t in response.templates] + assert "partials/media_items/media_review_full.html" in [t.name for t in response.templates] assert "media" in response.context assert response.context["media"] == media @@ -443,7 +432,7 @@ def test_backup_manage_displays_page(self, logged_in_client): response = logged_in_client.get(reverse("backup_manage")) assert response.status_code == 200 - assert "backup_manage.html" in [t.name for t in response.templates] + assert "base/backup_manage.html" in [t.name for t in response.templates] class TestBackupExportView: @@ -601,7 +590,7 @@ def test_load_more_returns_partial_template(self, logged_in_client, media_factor response = logged_in_client.get(reverse("load_more_media"), {"page": 2}) assert response.status_code == 200 - assert "partials/media-items-page.html" in [t.name for t in response.templates] + assert "partials/media_items/media_list_page.html" in [t.name for t in response.templates] def test_load_more_returns_next_page_items(self, logged_in_client, media_factory): """Load more view returns items for the requested page.""" @@ -705,7 +694,7 @@ def test_media_detail_displays_correct_template(self, logged_in_client, media): response = logged_in_client.get(reverse("media_detail", kwargs={"pk": media.pk})) assert response.status_code == 200 - assert "media_detail.html" in [t.name for t in response.templates] + assert "base/media_detail.html" in [t.name for t in response.templates] def test_media_detail_nonexistent_returns_404(self, logged_in_client): """Accessing detail view with nonexistent media returns 404."""