Skip to content

feat: add a navigation sidebar#39

Merged
PascalRepond merged 1 commit intomainfrom
rep-dev
Jan 4, 2026
Merged

feat: add a navigation sidebar#39
PascalRepond merged 1 commit intomainfrom
rep-dev

Conversation

@PascalRepond
Copy link
Copy Markdown
Owner

  • Introduce a new sidebar navigation component to enhance user experience and accessibility across the application.
  • Add logo and favicon to the app from Freepik on Flaticon.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 4, 2026

📝 Walkthrough

Walkthrough

Adds a responsive sidebar/drawer layout and new auth base template, shifts default media sorting from created_atreview_date and default view mode from listgrid, compiles translations during Docker builds, updates French translations, and applies assorted template/JS markup tweaks.

Changes

Cohort / File(s) Summary
Docker Build
Dockerfile
Add gettext to apt installs and invoke ./src/manage.py compilemessages during the Python build stage to compile translations in-image.
Core Views & Tests
src/core/views.py, src/tests/core/test_views.py
Default sort field changed from created_atreview_date; default view_mode changed from listgrid in index, load_more_media, and search_media. Tests updated for new defaults and invalid-sort fallback.
Base/Layout Templates
src/templates/base.html, src/templates/base_auth.html, src/templates/registration/login.html, src/templates/partials/sidebar-nav.html
Replace top navbar with responsive drawer/sidebar; add base_auth.html; login.html now extends base_auth.html; add partials/sidebar-nav.html implementing navigation, status filters, theme radio group (theme-sidebar), settings and auth controls.
Media Templates & Partials
src/templates/media.html, src/templates/partials/view-mode-toggle.html, src/templates/partials/*
Default hidden sort/input values and UI baseline moved to -review_date/grid; view-mode toggle reorders Grid before List and adds ARIA labels; multiple partials receive presentational/whitespace/markup reflow and small class tweaks.
Frontend JS
src/static/js/base.js
Theme selector name changed from theme-dropdowntheme-sidebar; default URL params updated (view_modegrid, sort-review_date); related selector and comment updates.
Translations
src/locale/fr/LC_MESSAGES/django.po
POT timestamp bumped; navigation and view-mode strings reorganized under partials/sidebar-nav.html; French translations added/adjusted and some entries marked obsolete.
Small Template Edits
src/templates/partials/*, src/templates/accounts/profile_edit.html, src/templates/backup_manage.html, src/templates/media_detail.html, ...
Numerous formatting/whitespace and minor markup changes (single-line attributes, spacing, small class additions) with no behavior changes.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add a navigation sidebar' clearly and accurately reflects the main change in the PR, which introduces a new sidebar navigation component throughout the application.
Description check ✅ Passed The description is related to the changeset. It mentions introducing a sidebar navigation component and adding a logo/favicon, both of which are present in the actual changes.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/templates/base_auth.html (1)

1-4: Remove duplicate {% load static %} tag.

static is loaded twice (line 1 via {% load static tailwind_tags %} and again on line 4). The second load is redundant.

🔎 Proposed fix
 {% load static tailwind_tags %}
 {% load django_htmx %}
 {% load i18n %}
-{% load static %}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c1026b and 38ee842.

⛔ Files ignored due to path filters (1)
  • src/static/images/bookshelf.png is excluded by !**/*.png
📒 Files selected for processing (11)
  • Dockerfile
  • src/core/views.py
  • src/locale/fr/LC_MESSAGES/django.po
  • src/static/js/base.js
  • src/templates/base.html
  • src/templates/base_auth.html
  • src/templates/media.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/partials/view-mode-toggle.html
  • src/templates/registration/login.html
  • src/tests/core/test_views.py
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-26T15:18:46.932Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 21
File: src/templates/accounts/profile_edit.html:23-58
Timestamp: 2025-12-26T15:18:46.932Z
Learning: In Django projects, attributes added to a form field's widget via field.widget.attrs.update(...) in the form's __init__ are rendered when using {{ form.field }} in templates. No explicit attribute definitions are needed in the template. This applies to templates under src/templates in Django apps; ensure you update attrs in __init__ for consistent HTMX behavior.

Applied to files:

  • src/templates/registration/login.html
  • src/templates/base.html
  • src/templates/base_auth.html
  • src/templates/media.html
  • src/templates/partials/view-mode-toggle.html
  • src/templates/partials/sidebar-nav.html
📚 Learning: 2026-01-03T21:16:52.649Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 36
File: src/templates/partials/media-items.html:67-67
Timestamp: 2026-01-03T21:16:52.649Z
Learning: In HTML templates (e.g., src/templates/partials/media-items.html), Tailwind CSS v4.1.11 supports arbitrary numeric utilities (max-w-{n}, w-{n}, h-{n}) that map to calc(var(--spacing) * n). These should be considered valid Tailwind classes and not flagged as invalid. Ensure review tooling and linters treat such numeric utilities as allowed, and apply this guidance to other HTML/template files in the repository.

Applied to files:

  • src/templates/registration/login.html
  • src/templates/base.html
  • src/templates/base_auth.html
  • src/templates/media.html
  • src/templates/partials/view-mode-toggle.html
  • src/templates/partials/sidebar-nav.html
📚 Learning: 2026-01-04T08:55:43.091Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 36
File: src/templates/partials/media-score-badge.html:1-9
Timestamp: 2026-01-04T08:55:43.091Z
Learning: In Django projects, configuring heroicons in TEMPLATES builtins makes heroicons.templatetags.heroicons available in all templates without needing {% load heroicons %}. For template reviews in this codebase, assume heroicon tags work globally in HTML templates under templates/, so avoid requiring explicit loads. If a template still uses {% load heroicons %}, assess whether the load is unnecessary and can be removed; ensure no conflicts arise from global tags.

Applied to files:

  • src/templates/registration/login.html
  • src/templates/base.html
  • src/templates/base_auth.html
  • src/templates/media.html
  • src/templates/partials/view-mode-toggle.html
  • src/templates/partials/sidebar-nav.html
🧬 Code graph analysis (2)
src/core/views.py (2)
src/core/migrations/0003_media_review_media_review_date_media_score_and_more.py (1)
  • Migration (9-41)
src/core/migrations/0009_media_external_uri_alter_media_review_date.py (1)
  • Migration (7-24)
src/tests/core/test_views.py (4)
src/tests/conftest.py (1)
  • logged_in_client (65-68)
src/core/migrations/0003_media_review_media_review_date_media_score_and_more.py (1)
  • Migration (9-41)
src/core/migrations/0009_media_external_uri_alter_media_review_date.py (1)
  • Migration (7-24)
src/core/migrations/0008_media_review_rendered_alter_media_review.py (1)
  • Migration (24-43)
🔇 Additional comments (27)
Dockerfile (2)

32-36: LGTM! Correct addition of gettext for translation support.

The addition of gettext as a system dependency is correctly placed before the translation compilation step and follows Docker best practices with proper cleanup.


72-73: Good addition of translation compilation to the build process.

The placement is correct—after source code is copied and before static files are collected. The command pattern matches how collectstatic is invoked (line 78), which is consistent. Verification confirms the locale directory structure is properly configured with French translations present (src/locale/fr/LC_MESSAGES/django.po) and LOCALE_PATHS is set in Django settings, so the compilemessages command will work as expected.

src/templates/partials/view-mode-toggle.html (1)

6-21: LGTM! Default view mode updated to grid with improved accessibility.

The changes correctly update the default view mode from 'list' to 'grid' and add ARIA labels for better accessibility. Button logic properly toggles active states and updates the hidden input value on each interaction.

src/templates/media.html (5)

17-17: LGTM! Button styling simplified.

Removal of btn-square class is a minor styling adjustment.


22-27: LGTM! Sort defaults updated to review_date.

The default sort field change from created_at to review_date aligns with the broader PR objectives. Icon rendering updated consistently.


34-34: LGTM! Hidden input default aligned with new sort semantics.


40-42: LGTM! Select styling and handler updated.

The removal of select-md and update of the onchange handler default to '-review_date' are consistent with the new defaults.


50-54: LGTM! Search input layout improvement.

Added comment and grow class for better layout control.

src/templates/registration/login.html (1)

1-1: LGTM! Login now uses dedicated auth base template.

Switching from base.html to base_auth.html aligns with the PR's introduction of a sidebar navigation component. Authentication pages likely require a simpler layout without the main app sidebar.

src/static/js/base.js (3)

4-4: LGTM! Theme selector updated for sidebar navigation.

The selector name change from theme-dropdown to theme-sidebar reflects the new sidebar-based UI structure introduced in this PR.

Also applies to: 21-21


16-32: LGTM! Comment style improvements.

Minor capitalization updates to comments without logic changes.


38-39: LGTM! Default parameters aligned with new UI defaults.

The change from 'list' to 'grid' for view_mode and '-created_at' to '-review_date' for sort are consistent with the broader PR changes across templates and views.

src/core/views.py (4)

22-22: LGTM! Default sort field updated to review_date.

The change from created_at to review_date as the default sort field is consistent with the frontend changes in templates and JavaScript.


103-103: LGTM! Default view mode updated to grid in index view.


196-196: LGTM! Default view mode updated to grid in load_more_media view.


231-231: LGTM! Default view mode updated to grid in search_media view.

All three HTMX views now consistently default to grid view mode, aligning with the broader PR changes.

src/tests/core/test_views.py (3)

251-256: LGTM!

The test correctly reflects the new default sorting behavior (review_date instead of created_at). The docstring and assertions are aligned with the expected view changes.


272-276: LGTM!

The fallback behavior test correctly verifies that invalid sort fields now default to review_date.


666-676: LGTM!

Good addition of response_default to verify the new default view_mode of "grid" for load_more_media. The test now comprehensively covers default, explicit list, and explicit grid scenarios.

src/templates/base_auth.html (1)

5-32: LGTM!

The template establishes a clean base structure for authentication pages with proper meta tags, HTMX CSRF setup, and extensible blocks. The simplified layout without the sidebar drawer is appropriate for login/registration flows.

src/templates/base.html (2)

19-19: LGTM!

Good addition of the favicon link for branding consistency.


24-60: LGTM!

The drawer-based layout is well-structured with proper accessibility considerations:

  • Appropriate aria-label attributes on hamburger and overlay
  • Responsive behavior with lg:drawer-open for desktop
  • Clean separation between mobile navbar and sidebar drawer
  • Proper z-index handling for the drawer side

Based on learnings, heroicon tags work globally without explicit loading.

src/templates/partials/sidebar-nav.html (3)

83-121: LGTM!

The theme selector dropdown is well-implemented with proper accessibility (aria-label), radio inputs with shared name="theme-sidebar", and consistent styling using DaisyUI classes.


122-138: LGTM!

The authentication section properly handles both authenticated (logout form with CSRF) and unauthenticated (login link) states. The mt-auto class correctly pushes this section to the bottom of the sidebar.


24-63: The #status select element is properly available.

The element with id="status" exists in src/templates/partials/filters.html and is included in media.html, which extends base.html. Since sidebar-nav.html is also included in base.html, both partials render together in the final DOM, making the document.getElementById('status') reference valid and functional.

src/locale/fr/LC_MESSAGES/django.po (2)

637-683: LGTM!

All new sidebar navigation strings have appropriate French translations:

  • "All" → "Tout"
  • "By status" → "Par statut"
  • "Settings" → "Paramètres"
  • Theme options and auth strings properly translated

The translations are consistent with the existing localization style.


706-707: LGTM!

The obsolete "Log Out" entry is correctly marked with #~ as it's been replaced by "Logout" in the new sidebar navigation.

Comment thread src/templates/media.html Outdated
Comment thread src/templates/partials/sidebar-nav.html Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/templates/partials/contributor-chip.html (1)

3-6: LGTM!

The HTML consolidation improves readability while preserving functionality.

Consider adding an aria-label to the remove button for better screen reader support:

-    <button type="button" class="btn btn-ghost btn-xs btn-circle" data-action="remove-chip">✕</button>
+    <button type="button" class="btn btn-ghost btn-xs btn-circle" data-action="remove-chip" aria-label="Remove {{ agent.name }}">✕</button>
src/templates/backup_manage.html (1)

14-16: Minor: Extra whitespace in class attribute.

The conditional class rendering introduces extra spaces (e.g., alert alert-error mb-2). While browsers normalize this, you could tidy it up:

🔎 Suggested cleanup
          <div role="alert"
-               class="alert {% if message.tags == 'error' %} alert-error {% elif message.tags == 'success' %} alert-success {% else %} alert-info {% endif %} mb-2">
+               class="alert {% if message.tags == 'error' %}alert-error{% elif message.tags == 'success' %}alert-success{% else %}alert-info{% endif %} mb-2">
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38ee842 and 48c0938.

⛔ Files ignored due to path filters (1)
  • src/static/images/bookshelf.png is excluded by !**/*.png
📒 Files selected for processing (20)
  • Dockerfile
  • src/core/views.py
  • src/locale/fr/LC_MESSAGES/django.po
  • src/static/js/base.js
  • src/templates/accounts/profile_edit.html
  • src/templates/backup_manage.html
  • src/templates/base.html
  • src/templates/base_auth.html
  • src/templates/media.html
  • src/templates/partials/confirm-modal.html
  • src/templates/partials/contributor-chip.html
  • src/templates/partials/filters.html
  • src/templates/partials/media-contributors.html
  • src/templates/partials/media-cover.html
  • src/templates/partials/media-items.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/partials/spinner.html
  • src/templates/partials/view-mode-toggle.html
  • src/templates/registration/login.html
  • src/tests/core/test_views.py
✅ Files skipped from review due to trivial changes (5)
  • src/templates/partials/media-contributors.html
  • src/templates/partials/confirm-modal.html
  • src/templates/partials/media-cover.html
  • src/templates/partials/media-items.html
  • src/templates/partials/spinner.html
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/static/js/base.js
  • src/templates/partials/view-mode-toggle.html
  • Dockerfile
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-26T15:18:46.932Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 21
File: src/templates/accounts/profile_edit.html:23-58
Timestamp: 2025-12-26T15:18:46.932Z
Learning: In Django projects, attributes added to a form field's widget via field.widget.attrs.update(...) in the form's __init__ are rendered when using {{ form.field }} in templates. No explicit attribute definitions are needed in the template. This applies to templates under src/templates in Django apps; ensure you update attrs in __init__ for consistent HTMX behavior.

Applied to files:

  • src/templates/partials/contributor-chip.html
  • src/templates/base.html
  • src/templates/accounts/profile_edit.html
  • src/templates/partials/filters.html
  • src/templates/backup_manage.html
  • src/templates/registration/login.html
  • src/templates/media.html
  • src/templates/base_auth.html
  • src/templates/partials/sidebar-nav.html
📚 Learning: 2026-01-03T21:16:52.649Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 36
File: src/templates/partials/media-items.html:67-67
Timestamp: 2026-01-03T21:16:52.649Z
Learning: In HTML templates (e.g., src/templates/partials/media-items.html), Tailwind CSS v4.1.11 supports arbitrary numeric utilities (max-w-{n}, w-{n}, h-{n}) that map to calc(var(--spacing) * n). These should be considered valid Tailwind classes and not flagged as invalid. Ensure review tooling and linters treat such numeric utilities as allowed, and apply this guidance to other HTML/template files in the repository.

Applied to files:

  • src/templates/partials/contributor-chip.html
  • src/templates/base.html
  • src/templates/accounts/profile_edit.html
  • src/templates/partials/filters.html
  • src/templates/backup_manage.html
  • src/templates/registration/login.html
  • src/templates/media.html
  • src/templates/base_auth.html
  • src/templates/partials/sidebar-nav.html
📚 Learning: 2026-01-04T08:55:43.091Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 36
File: src/templates/partials/media-score-badge.html:1-9
Timestamp: 2026-01-04T08:55:43.091Z
Learning: In Django projects, configuring heroicons in TEMPLATES builtins makes heroicons.templatetags.heroicons available in all templates without needing {% load heroicons %}. For template reviews in this codebase, assume heroicon tags work globally in HTML templates under templates/, so avoid requiring explicit loads. If a template still uses {% load heroicons %}, assess whether the load is unnecessary and can be removed; ensure no conflicts arise from global tags.

Applied to files:

  • src/templates/partials/contributor-chip.html
  • src/templates/base.html
  • src/templates/accounts/profile_edit.html
  • src/templates/partials/filters.html
  • src/templates/backup_manage.html
  • src/templates/registration/login.html
  • src/templates/media.html
  • src/templates/base_auth.html
  • src/templates/partials/sidebar-nav.html
🧬 Code graph analysis (1)
src/tests/core/test_views.py (1)
src/tests/conftest.py (1)
  • logged_in_client (65-68)
🔇 Additional comments (20)
src/templates/accounts/profile_edit.html (2)

40-42: LGTM!

The email error rendering now follows the same multi-line formatting pattern used for other form fields (username, first_name, last_name), improving consistency within the template.


85-85: LGTM!

The language option tag consolidation is a minor formatting improvement with no behavioral change.

src/templates/partials/filters.html (2)

27-27: LGTM!

The status filter option consolidation maintains functionality while improving consistency.


40-42: LGTM!

The score filter option reflow is a minor formatting adjustment with no behavioral impact.

src/core/views.py (3)

22-22: Behavioral change: default sort field now review_date.

This changes the default sorting from created_at to review_date. Users visiting the home page without explicit sort parameters will now see items sorted by review date. This is a reasonable UX improvement for a media tracking application.


103-103: Behavioral change: default view mode now grid.

The default presentation switches from list to grid view. This change is consistent across index, load_more_media, and search_media views, and the tests have been updated accordingly.


196-196: LGTM!

The load_more_media and search_media views now consistently default to grid view, matching the index view behavior.

Also applies to: 231-231

src/tests/core/test_views.py (3)

251-256: LGTM!

Test correctly updated to verify the new default sorting behavior (review_date descending).


272-276: LGTM!

Invalid sort field fallback test correctly updated to expect review_date as the default.


670-676: LGTM!

Good addition testing the default view_mode behavior. The test now covers three scenarios: no view_mode specified (defaults to grid), explicit list, and explicit grid.

src/templates/registration/login.html (1)

1-1: base_auth.html provides all required template blocks.

The change to extend base_auth.html is properly supported. The template defines both the title block (lines 8-10) and content block (lines 26-27), along with the CSRF token setup via hx-headers attribute on the body tag and HTMX script injection that support the authentication template restructuring.

src/templates/base_auth.html (1)

1-33: LGTM! Clean authentication template structure.

The base_auth.html template provides a clean, minimal layout for authentication pages. The HTMX headers are properly configured with CSRF token, and the block structure allows appropriate customization.

src/templates/partials/sidebar-nav.html (2)

86-124: LGTM! Theme selector properly implemented.

The theme selector dropdown is well-structured with proper accessibility attributes and aligns with the JavaScript handling in base.js (which targets input[name="theme-sidebar"]).


27-66: Verify the 'status' select element exists in the parent template.

The status filter buttons (lines 28-65) reference document.getElementById('status') to manipulate a hidden select element and trigger HTMX updates. This element is not defined in this partial, so it must exist in the parent template or included partials.

Run the following script to verify that the 'status' element is properly defined:

#!/bin/bash
# Description: Find the hidden select/input element with id="status" in templates

# Search for elements with id="status" in template files
rg -n --type=html 'id=["'\'']status["'\'']' src/templates/

# Also check for the status select/input definition
ast-grep --pattern 'id="status"'
src/templates/base.html (2)

18-20: LGTM! Favicon properly added.

The favicon link is correctly configured to use the static bookshelf.png image.


27-67: LGTM! Drawer layout properly implemented.

The drawer structure is well-implemented with:

  • Hidden checkbox toggle for state management (line 30)
  • Responsive behavior (desktop sidebar always open via lg:drawer-open, mobile hamburger via lg:hidden)
  • Proper overlay for closing on mobile (lines 62-64)
  • Clean separation of mobile navbar and sidebar navigation
src/templates/media.html (2)

22-22: Verify consistency: default sort changed from created_at to review_date.

The default sort value has been updated from -created_at to -review_date in multiple places (lines 22, 34, 42). This aligns with the PR's goal to change default sorting behavior.

Also applies to: 34-34, 42-42


43-48: No action needed—'created_at' is a fully supported sort field.

The backend explicitly defines created_at as a valid sort field in the valid_fields set, and the Media model includes the created_at field. The template option at line 43 is correct and should remain.

Likely an incorrect or invalid review comment.

src/locale/fr/LC_MESSAGES/django.po (2)

127-145: LGTM! Status string references properly updated.

The status-related translations now correctly reference both src/core/models.py and src/templates/partials/sidebar-nav.html, reflecting that these strings are used in both the model definitions and the new sidebar navigation.


637-683: LGTM! New sidebar navigation translations added.

All new sidebar navigation strings are properly translated to French, including:

  • Navigation items (All, By status, Settings, Profile, Backups)
  • Theme selector options (System, Light, Dark)
  • Authentication actions (Login, Logout)

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/templates/partials/contributor-chip.html (1)

3-7: LGTM! Formatting changes look clean.

The inline consolidation improves readability without affecting functionality. The component correctly maintains the relationship between the span's data-input-id and the hidden input's id for JavaScript-based removal.

Optional: Consider adding accessible text to the remove button.

The remove button currently uses only the "✕" character. Adding an aria-label would improve accessibility for screen readers:

🔎 Suggested accessibility enhancement
-    <button type="button" class="btn btn-ghost btn-xs btn-circle" data-action="remove-chip">✕</button>
+    <button type="button" class="btn btn-ghost btn-xs btn-circle" data-action="remove-chip" aria-label="{% trans 'Remove contributor' %}">✕</button>
src/templates/base_auth.html (2)

1-22: Consider adding favicon reference.

The template is missing a favicon link. If the main application has a favicon (as mentioned in the PR description about adding a bookcase icon from Flaticon), consider including it here for consistency across authentication pages.

🔎 Suggested addition

Add this line in the <head> section, typically after the viewport meta tag:

 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
+<link rel="icon" type="image/x-icon" href="{% static 'favicon.ico' %}">
 {% tailwind_css %}

24-24: Clarify comment wording.

The comment states "Simple layout for non-authenticated users" but the template is named base_auth.html and is used for authentication pages (login, etc.). Consider updating the comment to avoid confusion.

🔎 Suggested clarification
-    <!-- Simple layout for non-authenticated users -->
+    <!-- Simple layout for authentication pages (login, registration, etc.) -->
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48c0938 and 18bc080.

⛔ Files ignored due to path filters (1)
  • src/static/images/bookshelf.png is excluded by !**/*.png
📒 Files selected for processing (21)
  • Dockerfile
  • src/core/views.py
  • src/locale/fr/LC_MESSAGES/django.po
  • src/static/js/base.js
  • src/templates/accounts/profile_edit.html
  • src/templates/backup_manage.html
  • src/templates/base.html
  • src/templates/base_auth.html
  • src/templates/media.html
  • src/templates/media_detail.html
  • src/templates/partials/confirm-modal.html
  • src/templates/partials/contributor-chip.html
  • src/templates/partials/filters.html
  • src/templates/partials/media-contributors.html
  • src/templates/partials/media-cover.html
  • src/templates/partials/media-items.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/partials/spinner.html
  • src/templates/partials/view-mode-toggle.html
  • src/templates/registration/login.html
  • src/tests/core/test_views.py
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/templates/registration/login.html
  • src/templates/partials/confirm-modal.html
  • src/templates/partials/media-cover.html
  • src/static/js/base.js
  • src/templates/partials/spinner.html
  • src/templates/partials/media-items.html
  • Dockerfile
  • src/templates/partials/view-mode-toggle.html
  • src/core/views.py
  • src/templates/partials/media-contributors.html
  • src/templates/backup_manage.html
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-26T15:18:46.932Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 21
File: src/templates/accounts/profile_edit.html:23-58
Timestamp: 2025-12-26T15:18:46.932Z
Learning: In Django projects, attributes added to a form field's widget via field.widget.attrs.update(...) in the form's __init__ are rendered when using {{ form.field }} in templates. No explicit attribute definitions are needed in the template. This applies to templates under src/templates in Django apps; ensure you update attrs in __init__ for consistent HTMX behavior.

Applied to files:

  • src/templates/media_detail.html
  • src/templates/base.html
  • src/templates/media.html
  • src/templates/partials/contributor-chip.html
  • src/templates/accounts/profile_edit.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/base_auth.html
  • src/templates/partials/filters.html
📚 Learning: 2026-01-03T21:16:52.649Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 36
File: src/templates/partials/media-items.html:67-67
Timestamp: 2026-01-03T21:16:52.649Z
Learning: In HTML templates (e.g., src/templates/partials/media-items.html), Tailwind CSS v4.1.11 supports arbitrary numeric utilities (max-w-{n}, w-{n}, h-{n}) that map to calc(var(--spacing) * n). These should be considered valid Tailwind classes and not flagged as invalid. Ensure review tooling and linters treat such numeric utilities as allowed, and apply this guidance to other HTML/template files in the repository.

Applied to files:

  • src/templates/media_detail.html
  • src/templates/base.html
  • src/templates/media.html
  • src/templates/partials/contributor-chip.html
  • src/templates/accounts/profile_edit.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/base_auth.html
  • src/templates/partials/filters.html
📚 Learning: 2026-01-04T08:55:43.091Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 36
File: src/templates/partials/media-score-badge.html:1-9
Timestamp: 2026-01-04T08:55:43.091Z
Learning: In Django projects, configuring heroicons in TEMPLATES builtins makes heroicons.templatetags.heroicons available in all templates without needing {% load heroicons %}. For template reviews in this codebase, assume heroicon tags work globally in HTML templates under templates/, so avoid requiring explicit loads. If a template still uses {% load heroicons %}, assess whether the load is unnecessary and can be removed; ensure no conflicts arise from global tags.

Applied to files:

  • src/templates/media_detail.html
  • src/templates/base.html
  • src/templates/media.html
  • src/templates/partials/contributor-chip.html
  • src/templates/accounts/profile_edit.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/base_auth.html
  • src/templates/partials/filters.html
🧬 Code graph analysis (1)
src/tests/core/test_views.py (2)
src/tests/conftest.py (1)
  • logged_in_client (65-68)
src/core/migrations/0003_media_review_media_review_date_media_score_and_more.py (1)
  • Migration (9-41)
🔇 Additional comments (10)
src/templates/partials/filters.html (1)

27-27: LGTM! Formatting changes only.

The changes to the status and score filter options are purely cosmetic, consolidating the opening tags to single lines while preserving all functionality.

Also applies to: 40-42

src/tests/core/test_views.py (2)

252-256: LGTM! Tests correctly updated for new default sorting.

The test assertions have been properly updated to reflect the change in default sorting from created_at to review_date. Both the default behavior and invalid sort field fallback are correctly verified.

Also applies to: 276-276


670-676: LGTM! Test correctly verifies new default view mode.

The test has been appropriately extended to verify that the default view_mode is now "grid" when not specified, while maintaining existing tests for explicit view mode parameters.

src/templates/media_detail.html (1)

37-37: LGTM! Appropriate CSS refinement.

Adding overflow-hidden to the cover card ensures clean visual presentation by clipping any overflow content, which is a standard pattern for card-based layouts.

src/templates/accounts/profile_edit.html (1)

40-42: LGTM! Formatting changes only.

The changes to error rendering and option tag formatting are purely cosmetic, improving code consistency without any functional impact.

Also applies to: 85-85

src/templates/partials/sidebar-nav.html (2)

86-124: Well-implemented theme selector.

The theme dropdown correctly uses name="theme-sidebar" which aligns with the frontend theme handling referenced in the AI summary. The structure follows DaisyUI patterns with proper accessibility attributes.


126-141: LGTM! Proper authentication handling.

The authentication section correctly implements logout as a POST form with CSRF protection and uses mt-auto for bottom positioning. The conditional rendering based on authentication state is appropriate.

src/templates/base.html (1)

28-66: Excellent responsive drawer implementation.

The drawer layout is well-structured with proper responsive behavior:

  • Desktop: sidebar always visible (lg:drawer-open)
  • Mobile: hamburger menu with toggle via checkbox
  • Proper overlay for mobile drawer dismissal
  • Responsive content padding (p-4 → lg:p-12)

The integration of the sidebar navigation partial is clean and follows DaisyUI patterns.

src/templates/media.html (1)

22-22: Consistent default sort update to review_date.

The default sort field has been consistently updated from created_at to review_date across all three locations (toggle button onclick, hidden input value, and select change handler). This aligns with the PR's shift in default sorting semantics.

Also applies to: 34-34, 42-42

src/locale/fr/LC_MESSAGES/django.po (1)

637-683: Complete French translations for new sidebar navigation.

All new navigation strings from the sidebar component have been properly translated to French. The translations are accurate and consistent with existing terminology in the project.

Comment thread src/templates/partials/sidebar-nav.html Outdated
- Introduce a new sidebar navigation component to enhance user experience
and accessibility across the application.
- Add logo and favicon to the app from Freepik on [Flaticon](https://www.flaticon.com/free-icons/bookcase).
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/templates/media.html (1)

43-44: Option value/label/selection mismatch persists.

The first sort option still has inconsistent value (review_date), label ("Creation date"), and selection check (created_at), causing the same issues previously flagged: users see "Creation date" but selecting it sorts by review_date, and the option never appears selected.

🔎 Recommended fix

If creation date sorting should be supported:

-          <option value="review_date"
+          <option value="created_at"
                  {% if sort_field == 'created_at' %}selected{% endif %}>{% translate "Creation date" %}</option>

If creation date sorting is not needed, remove this option entirely:

-          <option value="review_date"
-                  {% if sort_field == 'created_at' %}selected{% endif %}>{% translate "Creation date" %}</option>
🧹 Nitpick comments (1)
src/templates/partials/spinner.html (1)

13-15: Consider adding ARIA attributes for screen reader accessibility.

The spinner could benefit from accessibility attributes to properly announce loading states to assistive technologies.

🔎 Suggested accessibility enhancement
 <div {% if id %}id="{{ id }}"{% endif %}
-     class="loading loading-spinner loading-{{ size|default:'md' }} text-{{ color|default:'primary' }} {% if htmx_indicator %}htmx-indicator{% endif %} {% if inline %}inline-block{% endif %}">
+     class="loading loading-spinner loading-{{ size|default:'md' }} text-{{ color|default:'primary' }} {% if htmx_indicator %}htmx-indicator{% endif %} {% if inline %}inline-block{% endif %}"
+     role="status"
+     aria-label="{{ text|default:_('Loading...') }}">
 </div>
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a28b7d3 and cbe4c89.

⛔ Files ignored due to path filters (1)
  • src/static/images/bookshelf.png is excluded by !**/*.png
📒 Files selected for processing (22)
  • Dockerfile
  • src/core/views.py
  • src/locale/fr/LC_MESSAGES/django.po
  • src/static/js/base.js
  • src/templates/accounts/profile_edit.html
  • src/templates/backup_manage.html
  • src/templates/base.html
  • src/templates/base_auth.html
  • src/templates/media.html
  • src/templates/media_detail.html
  • src/templates/media_edit.html
  • src/templates/partials/confirm-modal.html
  • src/templates/partials/contributor-chip.html
  • src/templates/partials/filters.html
  • src/templates/partials/media-contributors.html
  • src/templates/partials/media-cover.html
  • src/templates/partials/media-items.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/partials/spinner.html
  • src/templates/partials/view-mode-toggle.html
  • src/templates/registration/login.html
  • src/tests/core/test_views.py
✅ Files skipped from review due to trivial changes (3)
  • src/templates/partials/media-contributors.html
  • src/templates/partials/confirm-modal.html
  • src/templates/partials/filters.html
🚧 Files skipped from review as they are similar to previous changes (11)
  • src/templates/partials/media-items.html
  • src/templates/media_detail.html
  • src/static/js/base.js
  • src/templates/partials/media-cover.html
  • src/templates/base_auth.html
  • src/templates/registration/login.html
  • src/templates/partials/contributor-chip.html
  • src/templates/backup_manage.html
  • src/templates/accounts/profile_edit.html
  • src/core/views.py
  • src/templates/partials/view-mode-toggle.html
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-12-26T15:18:46.932Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 21
File: src/templates/accounts/profile_edit.html:23-58
Timestamp: 2025-12-26T15:18:46.932Z
Learning: In Django projects, attributes added to a form field's widget via field.widget.attrs.update(...) in the form's __init__ are rendered when using {{ form.field }} in templates. No explicit attribute definitions are needed in the template. This applies to templates under src/templates in Django apps; ensure you update attrs in __init__ for consistent HTMX behavior.

Applied to files:

  • src/templates/base.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/media.html
  • src/templates/media_edit.html
  • src/templates/partials/spinner.html
📚 Learning: 2026-01-03T21:16:52.649Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 36
File: src/templates/partials/media-items.html:67-67
Timestamp: 2026-01-03T21:16:52.649Z
Learning: In HTML templates (e.g., src/templates/partials/media-items.html), Tailwind CSS v4.1.11 supports arbitrary numeric utilities (max-w-{n}, w-{n}, h-{n}) that map to calc(var(--spacing) * n). These should be considered valid Tailwind classes and not flagged as invalid. Ensure review tooling and linters treat such numeric utilities as allowed, and apply this guidance to other HTML/template files in the repository.

Applied to files:

  • src/templates/base.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/media.html
  • src/templates/media_edit.html
  • src/templates/partials/spinner.html
📚 Learning: 2026-01-04T08:55:43.091Z
Learnt from: PascalRepond
Repo: PascalRepond/datakult PR: 36
File: src/templates/partials/media-score-badge.html:1-9
Timestamp: 2026-01-04T08:55:43.091Z
Learning: In Django projects, configuring heroicons in TEMPLATES builtins makes heroicons.templatetags.heroicons available in all templates without needing {% load heroicons %}. For template reviews in this codebase, assume heroicon tags work globally in HTML templates under templates/, so avoid requiring explicit loads. If a template still uses {% load heroicons %}, assess whether the load is unnecessary and can be removed; ensure no conflicts arise from global tags.

Applied to files:

  • src/templates/base.html
  • src/templates/partials/sidebar-nav.html
  • src/templates/media.html
  • src/templates/media_edit.html
  • src/templates/partials/spinner.html
🧬 Code graph analysis (1)
src/tests/core/test_views.py (1)
src/tests/conftest.py (1)
  • logged_in_client (65-68)
🔇 Additional comments (23)
src/templates/partials/spinner.html (2)

14-14: LGTM! Explicit spacing improves class separation.

The added spaces around the conditional blocks ensure proper CSS class separation regardless of which conditions evaluate, preventing potential class concatenation issues.


16-16: LGTM! Clean inline consolidation.

The single-line rendering maintains the same functionality while making the template more compact.

src/templates/media_edit.html (1)

6-6: LGTM: Consistent branding addition.

The " - Datakult" suffix aligns with the PR's branding consistency objectives and matches similar title changes across other templates.

Dockerfile (2)

32-36: LGTM: Proper i18n dependency addition.

Adding gettext to system dependencies is necessary for translation compilation support.


72-73: LGTM: Translation compilation at build time is best practice.

Compiling translations during the Docker build ensures .mo files are ready for production use and avoids runtime compilation overhead.

src/tests/core/test_views.py (3)

252-256: LGTM: Test expectations correctly updated for new default sort.

The test expectations now properly reflect the new default sorting by review_date descending.


276-276: LGTM: Fallback sort field correctly updated.

The invalid sort field test now correctly expects the new default of review_date as the fallback.


670-676: LGTM: View mode test expectations align with new grid default.

The test correctly expects grid as the default view mode, consistent with the PR's UI changes.

src/templates/media.html (4)

5-5: LGTM: Consistent branding update.

The title change to "Datakult" aligns with the PR's branding objectives.


17-42: LGTM: Sort UI updates align with new defaults.

The sort toggle and hidden input correctly default to -review_date, and the icon changes to heroicon_mini are appropriate.


50-66: LGTM: Search control enhancements.

The added comment improves readability, and the grow class ensures proper flex behavior.


74-90: LGTM: Filter badge display improvements.

Using _display helpers with fallbacks ensures proper localized display values for active filters.

src/locale/fr/LC_MESSAGES/django.po (6)

9-9: LGTM: POT-Creation-Date timestamp updated.

The timestamp reflects the latest translation extraction.


127-144: LGTM: Status translation references updated.

The status translations now correctly reference both core/models.py and the new sidebar-nav.html template.


464-465: LGTM: Backup warning message consolidation.

Removing line breaks creates a cleaner, more consistent message format.


637-683: LGTM: New sidebar navigation translations added.

All new sidebar navigation strings have appropriate French translations, supporting the main PR feature.


688-694: LGTM: View mode translations updated.

The Grid and List translations are correctly localized with updated template references.


706-707: LGTM: Obsolete translation entry marked for cleanup.

The "Log Out" entry is appropriately marked as obsolete, likely superseded by "Logout" in the sidebar navigation.

src/templates/partials/sidebar-nav.html (2)

27-61: Excellent refactor from JavaScript to standard navigation links.

The status filter links now use standard href attributes with query parameters instead of the onclick JavaScript handlers mentioned in previous reviews. This approach is cleaner, more maintainable, and eliminates the JavaScript errors that occurred on pages without the status select element.


1-137: Well-structured sidebar navigation implementation.

The sidebar provides clear navigation with proper sections (main nav, status filters, settings, theme, auth), uses appropriate icons, includes internationalization support, and follows DaisyUI patterns correctly. The authentication-gated features are properly implemented, and the theme selector structure aligns with the project's theme handling.

src/templates/base.html (3)

18-20: Good addition of favicon.

The favicon is properly linked to the bookshelf.png image added as part of this PR.


26-67: Well-implemented drawer layout with proper responsive behavior.

The drawer structure follows DaisyUI patterns correctly:

  • Uses lg:drawer-open for persistent sidebar on desktop
  • Hidden checkbox controls drawer state for mobile
  • Mobile navbar with hamburger is only visible on small screens (lg:hidden)
  • Drawer overlay has proper aria-label for accessibility
  • Content area and sidebar are properly separated

The HTMX headers on the body tag ensure CSRF tokens are included in all HTMX requests.


69-70: Useful addition of extra_js block.

This block provides a clean extension point for pages that need additional JavaScript without modifying the base template.

Comment on lines +63 to +79
<li class="menu-title mt-4">
<span>{% translate "Settings" %}</span>
</li>
{% if user.is_authenticated %}
<li>
<a href="{% url 'accounts:profile_edit' %}">
{% heroicon_outline "user-circle" class="w-5 h-5" %}
{% translate "Profile" %}
</a>
</li>
<li>
<a href="{% url 'backup_manage' %}">
{% heroicon_outline "arrow-down-tray" class="w-5 h-5" %}
{% translate "Backups" %}
</a>
</li>
{% endif %}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider conditionally rendering the Settings title.

The "Settings" menu title (lines 63-65) is always visible, but the actual settings items (Profile, Backups) are only shown for authenticated users (lines 66-79). This means unauthenticated users will see an empty Settings section, which is inconsistent UX.

🔎 Proposed fix
-  <!-- Separator for settings -->
-  <li class="menu-title mt-4">
-    <span>{% translate "Settings" %}</span>
-  </li>
   {% if user.is_authenticated %}
+    <!-- Separator for settings -->
+    <li class="menu-title mt-4">
+      <span>{% translate "Settings" %}</span>
+    </li>
     <li>
       <a href="{% url 'accounts:profile_edit' %}">
         {% heroicon_outline "user-circle" class="w-5 h-5" %}
         {% translate "Profile" %}
       </a>
     </li>
     <li>
       <a href="{% url 'backup_manage' %}">
         {% heroicon_outline "arrow-down-tray" class="w-5 h-5" %}
         {% translate "Backups" %}
       </a>
     </li>
   {% endif %}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/templates/partials/sidebar-nav.html around lines 63 to 79, the "Settings"
menu title is always rendered while its child items are only shown to
authenticated users; change the template so the title is rendered conditionally
along with its items (i.e., move the <li class="menu-title mt-4"> into the same
{% if user.is_authenticated %} block or wrap both the title and the following
<li> items in a single if) so the entire Settings section is hidden for
unauthenticated users.

<span>{% translate "Theme" %}</span>
</div>
<ul tabindex="0"
class="dropdown-content menu bg-base-100 rounded-box z-100 w-48 p-2 shadow-xl border border-base-300">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== Searching for z-100 or custom z-index config ==="
rg -n "z-100|z-index.*100" -i

echo -e "\n=== Check Tailwind config files ==="
find . -type f \( -name "tailwind.config.*" -o -name "tailwind.config.js" -o -name "tailwind.config.ts" \) | head -20

echo -e "\n=== Look for z-index customizations ==="
fd tailwind.config | xargs cat 2>/dev/null

Repository: PascalRepond/datakult

Length of output: 349


🏁 Script executed:

#!/bin/bash
echo "=== Check package.json for DaisyUI or Tailwind ==="
cat package.json | grep -A 5 -B 5 "daisyui\|tailwind" 2>/dev/null || echo "No matches in package.json"

echo -e "\n=== Search for z-100 in CSS files ==="
fd -e css -e scss | xargs grep -l "z-100" 2>/dev/null || echo "No CSS files contain z-100"

echo -e "\n=== Check HTML files for DaisyUI patterns ==="
rg "data-theme|daisyui" | head -5

echo -e "\n=== Look for CSS in output or dist directories ==="
find . -type f \( -name "*.css" \) 2>/dev/null | head -20

Repository: PascalRepond/datakult

Length of output: 864


🏁 Script executed:

#!/bin/bash
echo "=== Check styles.css for z-100 or z-index ==="
grep -n "z-100\|z-\[100\]\|z-index.*100" ./src/theme/static_src/src/styles.css 2>/dev/null || echo "Not found in styles.css"

echo -e "\n=== Show Tailwind config or theme setup ==="
find . -type f \( -name "postcss.config.*" -o -name "tailwind.config.*" \) 2>/dev/null

echo -e "\n=== Check for any z-index definitions in styles.css ==="
grep -n "z-index\|z-\[" ./src/theme/static_src/src/styles.css 2>/dev/null | head -20

echo -e "\n=== Check Python settings for static files/theme ==="
fd settings.py | xargs grep -l "TAILWIND\|DAISYUI\|STATIC" 2>/dev/null | head -5

echo -e "\n=== Check theme directory structure ==="
find ./src/theme -type f -name "*.py" -o -name "*.css" -o -name "*.js" | head -20

Repository: PascalRepond/datakult

Length of output: 518


🏁 Script executed:

#!/bin/bash
echo "=== Read postcss.config.js for Tailwind configuration ==="
cat ./src/theme/static_src/postcss.config.js

Repository: PascalRepond/datakult

Length of output: 249


🏁 Script executed:

#!/bin/bash
echo "=== Read full styles.css to check for config and z-index ==="
cat ./src/theme/static_src/src/styles.css

echo -e "\n=== Search for zIndex in entire theme directory ==="
rg "zIndex" ./src/theme/ 2>/dev/null || echo "No zIndex found"

echo -e "\n=== Search for any z-100 class or definition in entire repo ==="
rg "z-100" . 2>/dev/null

Repository: PascalRepond/datakult

Length of output: 2569


Use a valid Tailwind z-index utility for the dropdown menu.

z-100 is not a valid Tailwind CSS utility. Standard Tailwind provides z-index utilities only up to z-50, and this project has no custom z-index configuration. Use z-50 instead, or configure a custom z-index value in Tailwind's theme if a higher stacking order is needed.

🤖 Prompt for AI Agents
In src/templates/partials/sidebar-nav.html around line 87, the dropdown menu
uses an invalid Tailwind class "z-100"; replace it with the valid "z-50" class
(or, if a higher stacking context is required, add a custom z-index entry in
tailwind.config.js and use that custom utility) so the dropdown uses a supported
Tailwind z-index utility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant