From 3ae55bc8a7a6d432ee3309a72e0bfe8dbc034dc9 Mon Sep 17 00:00:00 2001 From: Pablo Silva Date: Thu, 2 Jul 2026 19:02:58 +0100 Subject: [PATCH 01/11] Replace the three icons for edit/delete/preview with a menu. --- src/core/admin.py | 10 +- .../jinja2/core/components/action-menu.jinja2 | 43 +++ .../jinja2/core/components/item-list.jinja2 | 22 +- .../core/templates/artwork_thumbnail.jinja2 | 65 +++-- .../core/templates/exhibit_thumbnail.jinja2 | 63 +++-- .../core/templates/marker_thumbnail.jinja2 | 37 +-- .../core/templates/object_thumbnail.jinja2 | 55 ++-- .../core/templates/sound_thumbnail.jinja2 | 15 ++ src/core/models.py | 155 +---------- src/core/static/css/action-menu.css | 172 ++++++++++++ src/core/static/css/marker-menu.css | 9 - src/core/static/css/repository-list.css | 49 +--- src/core/static/images/icons/delete.png | Bin 1934 -> 0 bytes src/core/static/images/icons/edit.png | Bin 1499 -> 0 bytes src/core/static/images/icons/preview.png | Bin 2434 -> 0 bytes src/core/static/js/action-menu.js | 126 +++++++++ .../tests/test_html_thumbnail_generators.py | 249 +++++++++--------- src/users/jinja2/users/profile.jinja2 | 3 +- 18 files changed, 630 insertions(+), 443 deletions(-) create mode 100644 src/core/jinja2/core/components/action-menu.jinja2 create mode 100644 src/core/jinja2/core/templates/sound_thumbnail.jinja2 create mode 100644 src/core/static/css/action-menu.css delete mode 100644 src/core/static/css/marker-menu.css delete mode 100644 src/core/static/images/icons/delete.png delete mode 100644 src/core/static/images/icons/edit.png delete mode 100644 src/core/static/images/icons/preview.png create mode 100644 src/core/static/js/action-menu.js diff --git a/src/core/admin.py b/src/core/admin.py index 98e072db5..761d3ff0c 100644 --- a/src/core/admin.py +++ b/src/core/admin.py @@ -131,7 +131,7 @@ class MarkerAdmin(BaseMarkerObjectAdmin): ] def image_preview(self, obj): - return format_html(obj.as_html_thumbnail(), "") + return format_html(obj.as_html(height=64, width=64, thumbnail=True), "") @admin.action(description="Generate spritesheets for selected GIF objects") @@ -215,7 +215,7 @@ class ObjectAdmin(BaseMarkerObjectAdmin): actions = [generate_spritesheets, "populate_dimensions"] def image_preview(self, obj): - return format_html(obj.as_html_thumbnail(), "") + return format_html(obj.as_html(height=64, width=64), "") def has_spritesheet(self, obj): return bool(obj.spritesheet_file and obj.spritesheet_metadata) @@ -294,13 +294,13 @@ def exhibits_count(self, obj): exhibits_count.short_description = "Exhibits Count" def marker_preview(self, obj): - return format_html(obj.marker.as_html_thumbnail(), "") + return format_html(obj.marker.as_html(height=64, width=64, thumbnail=True), "") marker_preview.short_description = "Marker" marker_preview.allow_tags = True def augmented_preview(self, obj): - return format_html(obj.augmented.as_html_thumbnail(), "") + return format_html(obj.augmented.as_html(height=64, width=64), "") augmented_preview.short_description = "Augmented Object" augmented_preview.allow_tags = True @@ -391,4 +391,4 @@ def _owner(self, obj): return format_html(HTML_LINK, link, obj.owner.user.username) def preview(self, obj): - return format_html(obj.as_html_thumbnail().replace(obj.title, ""), "") + return format_html(obj.as_html(), "") diff --git a/src/core/jinja2/core/components/action-menu.jinja2 b/src/core/jinja2/core/components/action-menu.jinja2 new file mode 100644 index 000000000..c2912350c --- /dev/null +++ b/src/core/jinja2/core/components/action-menu.jinja2 @@ -0,0 +1,43 @@ +{# Three-dot action menu component. + Variables: + - element: the model instance + - element_type: string like "marker", "object", "artwork", "sound", "ar-exhibit", "mr-exhibit" + - can_edit: boolean + - can_delete: boolean + - has_preview: boolean (only markers and artworks) + - edit_reason: string explaining why edit is disabled (optional) + - delete_reason: string explaining why delete is disabled (optional) +#} +
+ +
+ {% if element_type == "ar-exhibit" or element_type == "mr-exhibit" %} + {% set edit_url = url('edit-' ~ element_type) ~ '?id=' ~ element.id %} + {% else %} + {% set edit_url = url('edit-' ~ element_type) ~ '?id=' ~ element.id %} + {% endif %} + + {{ _("Edit") }} + + {% if has_preview %} + {% if element_type == "marker" %} + {% set preview_url = url('marker-preview') ~ '?id=' ~ element.id %} + {% elif element_type == "artwork" %} + {% set preview_url = url('artwork-preview') ~ '?id=' ~ element.id %} + {% endif %} + {{ _("Preview") }} + {% endif %} + {% set delete_url = url('delete-content') ~ '?content_type=' ~ element_type ~ '&id=' ~ element.id %} + + {{ _("Delete") }} + +
+
diff --git a/src/core/jinja2/core/components/item-list.jinja2 b/src/core/jinja2/core/components/item-list.jinja2 index 17c1b3b51..26d2e09a1 100644 --- a/src/core/jinja2/core/components/item-list.jinja2 +++ b/src/core/jinja2/core/components/item-list.jinja2 @@ -6,7 +6,27 @@ {% for element in repository_list %}
- {{ element.as_html_thumbnail(editable=editable) | safe }} + {% if element_type == "marker" %} + {% with marker=element %} + {% include "core/templates/marker_thumbnail.jinja2" %} + {% endwith %} + {% elif element_type == "object" %} + {% with object=element %} + {% include "core/templates/object_thumbnail.jinja2" %} + {% endwith %} + {% elif element_type == "sound" %} + {% with sound=element %} + {% include "core/templates/sound_thumbnail.jinja2" %} + {% endwith %} + {% elif element_type == "artwork" %} + {% with artwork=element %} + {% include "core/templates/artwork_thumbnail.jinja2" %} + {% endwith %} + {% elif element_type == "ar-exhibit" or element_type == "mr-exhibit" %} + {% with exhibit=element %} + {% include "core/templates/exhibit_thumbnail.jinja2" %} + {% endwith %} + {% endif %}
{% endfor %} diff --git a/src/core/jinja2/core/templates/artwork_thumbnail.jinja2 b/src/core/jinja2/core/templates/artwork_thumbnail.jinja2 index e9ef6e1a3..8045114c1 100644 --- a/src/core/jinja2/core/templates/artwork_thumbnail.jinja2 +++ b/src/core/jinja2/core/templates/artwork_thumbnail.jinja2 @@ -1,33 +1,48 @@ -
-
- -
-
-
diff --git a/src/core/jinja2/core/templates/exhibit_thumbnail.jinja2 b/src/core/jinja2/core/templates/exhibit_thumbnail.jinja2 index 17c495884..b4bf903a1 100644 --- a/src/core/jinja2/core/templates/exhibit_thumbnail.jinja2 +++ b/src/core/jinja2/core/templates/exhibit_thumbnail.jinja2 @@ -1,36 +1,33 @@ -
- -

{{ exhibit.name }}

-
-
-
-

- Created by {{ exhibit.owner.user.username }} -

-

{{ exhibit.creation_date }}

-
- {% if exhibit.type == "AR" %} -

- {{ exhibit.artworks_count }} Artwork(s) -

- {% elif exhibit.type == "MR" %} -

- {{ exhibit.augmenteds_count }} Object(s) -

-

- {{ exhibit.sounds_count }} Sound(s) -

- {% endif %} -
- See this Exhibition -
- {% if editable %}edit{% endif %} - {% if deletable %} - delete - {% endif %} -
+ +

{{ exhibit.name }}

+
+
+
+

+ {{ _("Created by ") }}{{ exhibit.owner.user.username }} +

+

{{ exhibit.date }}

+
+ {% if exhibit.exhibit_type == "AR" %} +

+ {{ exhibit.artworks_count }} {{ _("Artwork(s)") }} +

+ {% elif exhibit.exhibit_type == "MR" %} +

+ {{ exhibit.augmenteds_count }} {{ _("Object(s)") }} +

+

+ {{ exhibit.sounds_count }} {{ _("Sound(s)") }} +

+ {% endif %}
+ {{ _("See this Exhibition") }} + {% if editable %} + {% set can_edit = true %} + {% set can_delete = true %} + {% with element=exhibit, element_type=exhibit.content_type(), has_preview=false, can_edit=can_edit, can_delete=can_delete, edit_reason="", delete_reason="" %} + {% include "core/components/action-menu.jinja2" %} + {% endwith %} + {% endif %}
diff --git a/src/core/jinja2/core/templates/marker_thumbnail.jinja2 b/src/core/jinja2/core/templates/marker_thumbnail.jinja2 index fee5ccecb..a697ff627 100644 --- a/src/core/jinja2/core/templates/marker_thumbnail.jinja2 +++ b/src/core/jinja2/core/templates/marker_thumbnail.jinja2 @@ -1,23 +1,14 @@ -
- -
- {% if deletable %} - delete - {% endif %} - {# Disabled by now #} - {# {% if editable %} - edit - {% endif %} #} - {% if previewable %}preview{% endif %} -
-
+ +{% if editable %} + {% set can_edit = not marker.is_used_by_other_user() %} + {% set can_delete = not marker.in_use %} + {% set edit_reason = _("This marker is used by other users and cannot be edited.") %} + {% set delete_reason = _("This marker is used in artworks and cannot be deleted.") %} + {% with element=marker, element_type="marker", has_preview=true, can_edit=can_edit, can_delete=can_delete, edit_reason=edit_reason, delete_reason=delete_reason %} + {% include "core/components/action-menu.jinja2" %} + {% endwith %} +{% endif %} diff --git a/src/core/jinja2/core/templates/object_thumbnail.jinja2 b/src/core/jinja2/core/templates/object_thumbnail.jinja2 index 3d2a29ffa..576e80804 100644 --- a/src/core/jinja2/core/templates/object_thumbnail.jinja2 +++ b/src/core/jinja2/core/templates/object_thumbnail.jinja2 @@ -1,17 +1,40 @@ -
- - {% if editable %}edit{% endif %} - {% if deletable %} - delete +{% if object.is_video %} + +{% elif object.is_3d %} + {% if object.thumbnail %} + + {% else %} + + {% endif %} + {% else %} + + {% endif %} + {% if editable %} + {% set can_edit = not object.is_used_by_other_user() %} + {% set can_delete = not object.in_use %} + {% set edit_reason = _("This object is used by other users and cannot be edited.") %} + {% set delete_reason = _("This object is used in artworks and cannot be deleted.") %} + {% with element=object, element_type="object", has_preview=false, can_edit=can_edit, can_delete=can_delete, edit_reason=edit_reason, delete_reason=delete_reason %} + {% include "core/components/action-menu.jinja2" %} + {% endwith %} {% endif %} -
diff --git a/src/core/jinja2/core/templates/sound_thumbnail.jinja2 b/src/core/jinja2/core/templates/sound_thumbnail.jinja2 new file mode 100644 index 000000000..2cb881009 --- /dev/null +++ b/src/core/jinja2/core/templates/sound_thumbnail.jinja2 @@ -0,0 +1,15 @@ +{{ sound.title }} + +{% if editable %} + {% set can_edit = not sound.is_used_by_other_user() %} + {% set can_delete = not sound.in_use %} + {% set edit_reason = _("This sound is used by other users and cannot be edited.") %} + {% set delete_reason = _("This sound is in use and cannot be deleted.") %} + {% with element=sound, element_type="sound", has_preview=false, can_edit=can_edit, can_delete=can_delete, edit_reason=edit_reason, delete_reason=delete_reason %} + {% include "core/components/action-menu.jinja2" %} + {% endwith %} +{% endif %} diff --git a/src/core/models.py b/src/core/models.py index 309f2a7f1..cf490c6ef 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -8,7 +8,7 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ from django_extensions.db.models import TimeStampedModel -from fast_html import a, audio, b, div, h1, img, p, render, span, video +from fast_html import a, audio, img, render, video from core.marker_utils import delete_marker_files from users.models import Profile @@ -33,26 +33,6 @@ class ContentMixin: def content_type(self): return self.__class__.__name__.lower() - def _get_edit_button(self): - content_type = self.content_type() - return a( - _("edit"), - href=reverse(f"edit-{content_type}", query={"id": self.id}), - class_="edit", - ) - - def _get_delete_button(self): - content_type = self.content_type() - return a( - _("delete"), - href=reverse( - "delete-content", - query={"content_type": content_type, "id": self.id}, - ), - onclick=f"return confirm('{_('Are you sure you want to delete?')}')", - class_="delete", - ) - def used_in_html_string(self): used_in = "{} {} {} {} {} {}".format( USED_IN, @@ -170,19 +150,6 @@ def as_html(self): ) ) - def as_html_thumbnail(self, editable=False): - elements = [ - span(self.title, style="display:block;"), - self.as_html(), - ] - if editable and not self.is_used_by_other_user(): - elements.append(self._get_edit_button()) - - if editable and not self.in_use: - elements.append(self._get_delete_button()) - - return render(div(elements, style="margin: 10px auto;")) - class ExhibitTypes(models.TextChoices): AR = "AR", "Augmented Reality" @@ -249,29 +216,6 @@ def as_html( ) ) - def as_html_thumbnail(self, editable: bool = False): - height = DEFAULT_MARKER_THUMBNAIL_HEIGHT - width = DEFAULT_MARKER_THUMBNAIL_WIDTH - to_render = [self.as_html(height=height, width=width, thumbnail=True)] - if editable: - lower_menu_items = [] - if not self.in_use: - lower_menu_items.append(self._get_delete_button()) - - if not self.is_used_by_other_user(): - lower_menu_items.append(self._get_edit_button()) - - lower_menu_items.append( - a( - _("preview"), - href=reverse("marker-preview", query={"id": self.id}), - class_="preview", - ) - ) - lower_menu = div(lower_menu_items, class_="marker-menu") - to_render.append(lower_menu) - return render(to_render) - class ObjectExtensions(models.TextChoices): GIF = "gif", "GIF" @@ -529,18 +473,6 @@ def as_html(self, height: int = None, width: int = None): else: return render(img(**attributes)) - def as_html_thumbnail(self, editable=False): - height = DEFAULT_OBJECT_THUMBNAIL_HEIGHT - width = DEFAULT_OBJECT_THUMBNAIL_WIDTH - to_render = [self.as_html(height, width)] - if editable and not self.is_used_by_other_user(): - to_render.append(self._get_edit_button()) - - if editable and not self.in_use: - to_render.append(self._get_delete_button()) - - return render(to_render) - @pghistory.track() class Artwork(TimeStampedModel, ContentMixin): @@ -599,31 +531,6 @@ def used_in_html_string(self): ) return used_in - def as_html_thumbnail(self, editable=False): - elements = [ - self.marker.as_html_thumbnail(), - div(class_="separator"), - self.augmented.as_html_thumbnail(), - ] - if editable: - elements.extend( - [ - self._get_edit_button(), - ] - ) - if not self.in_use: - elements.append(self._get_delete_button()) - - if editable: - elements.extend( - a( - _("preview"), - href=reverse("artwork-preview", query={"id": self.id}), - class_="preview", - ) - ) - return render(div(elements, class_="artwork-elements flex")) - @pghistory.track() class Exhibit(TimeStampedModel, ContentMixin, models.Model): @@ -672,66 +579,6 @@ def content_type(self): else: raise ValueError("Invalid exhibit type") - def as_html_thumbnail(self, editable=False): - link_to_exhibit = reverse("exhibit-detail", query={"id": self.id}) - exhibit_title = a(h1(self.name, class_="exhibit-name"), href=link_to_exhibit) - media_stats = [] - if self.exhibit_type == ExhibitTypes.AR: - media_stats.append( - p( - a( - "{} {}".format(self.artworks_count, _("Artwork(s)")), - href=link_to_exhibit, - ), - class_="exhibit-about", - ) - ) - elif self.exhibit_type == ExhibitTypes.MR: - media_stats.append( - p( - a( - "{} {}".format(self.augmenteds_count, _("Object(s)")), - href=link_to_exhibit, - ), - class_="exhibit-about", - ) - ) - media_stats.append( - p( - a( - "{} {}".format(self.sounds_count, _("Sound(s)")), - href=link_to_exhibit, - ), - class_="exhibit-about", - ) - ) - exhibit_info = [ - p([{_("Created by ")}, b(self.owner.user.username)], class_="by"), - p(self.date, class_="exbDate"), - div(media_stats), - ] - - button_see_this_exhibit = a( - _("See this Exhibition"), - href=f"/{self.slug}/", - class_="gotoExb", - ) - - exhibit_card_elements = [ - exhibit_info, - button_see_this_exhibit, - ] - if editable: - exhibit_card_elements.extend( - [div([self._get_delete_button(), self._get_edit_button()])] - ) - exhibit_card = div(div(exhibit_card_elements, class_="exhibit-elements flex")) - elements = [ - exhibit_title, - exhibit_card, - ] - return render(elements) - @receiver(post_delete, sender=Object) @receiver(post_delete, sender=Marker) diff --git a/src/core/static/css/action-menu.css b/src/core/static/css/action-menu.css new file mode 100644 index 000000000..1b9704e57 --- /dev/null +++ b/src/core/static/css/action-menu.css @@ -0,0 +1,172 @@ +/* Action Menu - Three-dot dropdown */ +.action-menu-container { + position: absolute; + top: 0; + right: 0; + z-index: 10; +} + +.action-menu-trigger { + background: #ffffff; + border: 1px solid #000; + border-radius: 0; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + color: #000; + padding: 0; + transition: background 0.2s ease; +} + +.action-menu-trigger:hover { + background: #f5f5f5; + border-color: #858585; +} + +.action-menu-dropdown { + display: none; + position: absolute; + top: 100%; + right: 0; + margin-top: 4px; + background: #ffffff; + border: 1px solid #000; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + min-width: 130px; + overflow: hidden; + z-index: 100; +} + +.action-menu-dropdown.open { + display: block; +} + +/* When dropdown is open, elevate the entire container above sibling items */ +.action-menu-container:has(.action-menu-dropdown.open), +.action-menu-container.menu-active { + z-index: 1000; +} + +.action-menu-item { + display: block; + padding: 10px 16px; + font-size: 0.8em; + color: #333; + text-decoration: none; + white-space: nowrap; + transition: background 0.1s; +} + +.action-menu-item:visited { + color: #333; +} + +.action-menu-item:hover { + background: #f5f5f5; +} + +.action-menu-item.disabled { + color: #aaa; + cursor: not-allowed; +} + +.action-menu-item.disabled:hover { + background: #fafafa; +} + +.action-menu-item.action-menu-delete:not(.disabled) { + color: #d32f2f; +} + +.action-menu-item.action-menu-delete:not(.disabled):hover { + background: #fdecea; +} + +/* Confirmation/info modal */ +.action-modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + opacity: 0; + visibility: hidden; + transition: opacity 0.2s, visibility 0.2s; +} + +.action-modal-overlay.visible { + opacity: 1; + visibility: visible; +} + +.action-modal-box { + background: #fff; + border-radius: 8px; + padding: 24px 28px; + max-width: 400px; + width: 90%; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2); + text-align: center; +} + +.action-modal-box p { + margin: 0 0 20px; + font-size: 0.95em; + color: #333; + line-height: 1.4; +} + +.action-modal-buttons { + display: flex; + gap: 10px; + justify-content: center; +} + +.action-modal-btn { + padding: 8px 20px; + border: none; + border-radius: 4px; + font-size: 0.85em; + cursor: pointer; + transition: background 0.15s; +} + +.action-modal-btn-cancel { + background: #e0e0e0; + color: #333; +} + +.action-modal-btn-cancel:hover { + background: #d0d0d0; +} + +.action-modal-btn-confirm { + background: #d32f2f; + color: #fff; +} + +.action-modal-btn-confirm:hover { + background: #b71c1c; +} + +.action-modal-btn-ok { + background: #5b0058; + color: #fff; +} + +.action-modal-btn-ok:hover { + background: #3d003b; +} + +/* Ensure repository-item has relative positioning for action menu */ +.repository-item { + position: relative; +} diff --git a/src/core/static/css/marker-menu.css b/src/core/static/css/marker-menu.css deleted file mode 100644 index 160fdc303..000000000 --- a/src/core/static/css/marker-menu.css +++ /dev/null @@ -1,9 +0,0 @@ -.marker-menu{ - display: flex; - flex-direction: row-reverse; - max-width: 130px; -} -.marker-menu a{ - margin:0 !important; - position: relative !important; -} \ No newline at end of file diff --git a/src/core/static/css/repository-list.css b/src/core/static/css/repository-list.css index cbcf8fd5c..772a495c7 100644 --- a/src/core/static/css/repository-list.css +++ b/src/core/static/css/repository-list.css @@ -38,36 +38,6 @@ flex: 0 1 calc(50% - 10px); } -.repository-item a.edit, .repository-item a.delete, .repository-item a.preview { - float: right; - background-size: contain; - width: 20px; - height: 20px; - text-indent: -99999px; - border: 8px solid #fff; - opacity: 1; - display: block; - margin: 10px 10px -20px auto; -} - -.repository-item a.edit:hover, .repository-item a.delete:hover, .repository-item a.preview:hover { - opacity: 0.7; -} - -.repository-item a.edit { - background: url(../images/icons/edit.png) no-repeat center center; - background-size: contain; -} - -.repository-item a.delete{ - background: url(../images/icons/delete.png) no-repeat center center; - background-size: contain; -} - -.repository-item a.preview { - background: url(../images/icons/preview.png) no-repeat center center; - background-size: contain; -} #repo-artwork .repository-item { flex: 0 1 calc(33% - 10px); @@ -92,23 +62,6 @@ flex: 0 1 calc(25%); } -#repo-marker .repository-item a.delete, #repo-object .repository-item a.delete { - margin: -20px 0 0 85px; - position: absolute; -} - -#repo-marker .repository-item a.edit, #repo-object .repository-item a.edit { - margin: -20px 0 -30px 40px; - position: absolute; -} - -.artwork-elements a.edit, .artwork-elements a.delete, .artwork-elements a.preview { - margin: 10px 10px -40px auto; -} - -.artwork-elements a.delete { - margin: 10px 10px -40px 0; -} h1.exhibit-name { margin: 0 0 10px 0; @@ -129,7 +82,7 @@ h1.exhibit-name:hover { color: #af00a9; } -p.exhibit-slug, a.edit, a.delete{ +p.exhibit-slug { font-size: 0.65em; } diff --git a/src/core/static/images/icons/delete.png b/src/core/static/images/icons/delete.png deleted file mode 100644 index eb49f0bcae7168dcc69b66f71bd1ea2385c8868d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1934 zcmV;92XXj`P)%Q)d{*|EDdKt3WBxg3wMR7(>J<)gTukg4Y-o znAd9M+p=Wamn|__mYHlz{IF!VY>CMt;Dx9kG-|wpf&rZY6w#QJD4{Lbf(5F;O7CYM zYK?;JxxLVt@k{t}`kp_}^Y43J-t#^W)ZxR2Y5b27g*$+7GjBk+0|+^%^ zdtw3}yB+ePA|krPX23rN15Bq*!Rv4U07mZKB{mZrLqq7VtAl%D0st`F)P&*YW+K`| zX29grrx-YU7CyK8o$D`JTLF;>DW#>nwWll=7*Cvl*KU93`bc{_d`>5nn>Pa##amzS z44CNc#lV>}Gm;PR;?^yUw6*clc33Phy(jqqW1XECJbxav&&Nw25I6%~J${VmECLw5 zc@v;0$cu|vYfqWY{%?M2Ds&8w9)WhbkWo_uk%W6Xyk~&z{(TIdJIC7n;pS$Hb#$;A zYabYZp`n2>`2gd3JqAO}!cg!sV63A9=1Z3Vn%kSS<;oR|b#{gvV>g*#I(ZU4r<0TR z#M7rR^0<%loEd3ve`f_w1|Z`iaQQMMgM;04P0c6MKZ`OekSvVRc=W1j;EtPpmi z5r&2asI*#$V`5m{&&hxnEiJI#zCA-Al8XocPfyRsu_Do5Uw3zx2d@2EQu5nqd;9)K zjH`NLiH~QzpVcEk(=>*zU7L}-5GK33%YG^-Xaay=ii+w++uA-C zaZElKPOBCD$Bw~evoVfkHh`vKxpWDm_wJF1?F|%wiLS1SAJl5I?arNpr1#`5n+=Bg zdPWl%fA3glCu|QM5DV;WC;%RVLCJp`6Piew43fCGkYgAPNGd2m!m?$=f|@I0GBc62 zXAeXX5h2Gh8bFCe$k@IeiTU}&f}3q(GBc5-)j|}>ygajd^eB-C>D#s;artt=!Ob?& zN+q)P?uA$?Wi_7l+7mEArqS>YZnlY1D4;rU0OIIq*7tB)d?|{8V)JImR;}V3+-wt- zmc}`GfX7ytq9`afZGvp|YOc4=8dA9&s{Q*RVY&Nz8(wdN6h%R?aU&>-0toL9xFT7w z02w+R_ETi>r+K7S<*{Fiwz#J7#Zu=?{+R-+AdU!*cSYdUcY26Oz`@X*M55S z>KPJxA6{XEra=R-q~wUz1&-wA_Br(WMWpnCA@=!TxpCv@9~(DLSb%~x0Q)ASVCnZ9)1RLNzdGb$pUtg9`9j=JIUQC`lcgSrv3)10jK;V?f z_UxHTPy_;yc)fUW`ZPSRD@J_aWdKdn7`}d;_$d(|+~ecuudjboHscCs18ADY(ABH| ziR1&gMn_?6Xn<#OlJmWs44`QmLszb_E^y2x&XExqPn>`!NEs~{>CckP=KTrD~JX5HZ%aST5X=KW@Y!Q zS3!FV0x+)u6b0qhtw=5`Bo!9`fAaAK1mVLlHYv-x;);8R4#|QwiXeIi6O_a zI~J!XC^l?BYFQbH*gyk_TfY47ue!P_0pQEFwl9)POMfP1f;1%sYMl-d2?>m2IlT?0 zl$C*!NZu)rdjN!f%4QOmpWoxCuCD#Qx;i&-Z5%k7D3QoUZ{5<-9EzmVM@FZoBTK7g zeL%|jby!|p43Sg{%jL@efYH}KY*B96f=G=fA6~D1`e)-MC2*_Nyc!(07sQ?#@c9tYj>##AIedto|3Y0YH>e z>96!Kn2@k+88WJ>Sbq`I5quyLpPP%!UAuz1pITlHc?rLRn<#|>AJx_dEqcf_8Z6wg z1C)q=8bpr7;&XERjY-paE2qxv&rlLe0`rxg08;?oHEET4g2DDk_L*Q>6cG&S9}&T(3uJc{%f7jyOj~Ff}+x zv`>L(yFB1uyrPs!(y{>jGeIhs6V?3i3L}J>Hz3>rgqwK-!W}@knKvNZ0fd`>0S4S? U9dS4S`~Uy|07*qoM6N<$f|d1f0RR91 diff --git a/src/core/static/images/icons/edit.png b/src/core/static/images/icons/edit.png deleted file mode 100644 index 616c7ae148e2edced58057ad48e05e3dc4b8ae88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1499 zcmV<11tj{3P)uX_t0(cE0IOLVw5kX8w=gxAV*8iIp**(d1@tC|`Al~6NB#(nu^)*g<4WUuT0pOiaEfk{ngAeLXmBjnsZ@ez1@y=W zr(`$DaTThT;7I{JGQw&3|ICj^r3B9j_%%5+=4U?gu*-u2K60$0wGx-nSh<>ES zf|o9tNH7%SZ33cp4{AyLx#(N{?^QUQ`SC;x4UP?WvMaxoe9dRMzX}0+Ow;_`))8;6 z+d_`M1=Kb_iX914e+mRE%JCT|Q9bqJh!z?gk9Ar;KuxWHU?6}L=Mm&gMY`ca!}0-Y z>I9e-l}Nq_cpu3tU4UAZViv0Uhw!0_54Adg|f-KKGfG;+}&Yb%x@)f3xxg#K0u?;xsT6`P$ zZYJ2_Tf!W@;I`xHXD1kV3CRgO(0ooTL0!&L?$&cB+(&JZW?;Lgn)mr9Z5~wq0?fcp z;3M3+zqLVbbcDLx5@zXnmiwxisbF9?@G)L!J{G4g*PmANpg_P8adkVALr&wUATOGr z-nT66=E319a52$O*oeR7B!(*ThexQ-TgD9C-eX3_0xl-{X*0r^9I1un$1_I6}dTI9!W|YhYPSpGSXA07I^D_~)1e}Za@Pjdw zaWcgm#thXt4U)Ei=6DYsz8g;SDMEaR8QKFl{(n>|UFT@|wyBpO*hfS20XI{meHk2;P(6R8oAum+ z*h@p`pxaq&-3aNr&f&GSZcC6LcVjlc>t>QsY~Kp&y3XP9T51+N>_$Bjcn$O15jTS0 z4od;?cw5HL+sFiZSCt2yOr4RdaKbf}_7&v6Wl^>%_e* zXZ9*!m92vnk@g}O4u`3%tfalYJvE5X*!#q0US0B7+KGjPwX&Gy%uf3lH?VaT7#tjA z1DlDeke8-W6p4mIWXf#SyRTV8QEvW(jldxm)ZBA(k3nMzu&n)po^WfmSKr|Yq zy1JSxS5g-~O=ui=vSzFVDOnUT*fPUA&4a@aBe7VFnwlCeU%s3g_*My0viOFsy#l;hj7Znw8>Cz=MO~Wu!!#zE7Az}I`xoeiQN?pQIfZy*=D^N#A z$An`g!kf*52R>tGn`r^rE+E@X3&?f>+2)QH@GsKQyQQhTe8&I)002ovPDHLkV1mZb Bt#<$b diff --git a/src/core/static/images/icons/preview.png b/src/core/static/images/icons/preview.png deleted file mode 100644 index 1e9a93b92d983a4f9bd34eaab08cd2143e890187..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2434 zcmaKuc{E$w9>-6NDM`7mMwE-Vo|3Bwp@_Dom~O5Hy``bAT55=Ss3EARnV_xdDH>Bu zsT!-4wp1HZRYMIGQCcNDVlGYfzTVTd-e2#Jx7XSGxAxv^?X%C`-|y%AGKdZ~aEap* z006-4Y^|KYX!fH*#lW_~FZ4YaM8YlX&O^Zx2lYw^`$s}-FNTAG^`k=2rYtr0Q%j|%KUtEDWBv=_<8R5jZ_A) zxquPy>JF8P&GbNZw!R>y+m72NCL?L6P(i4t)r~JNm#(U?6y$6PBDZ~|&q@=631*Ic zO!M?i>FPSIOw~iD4(Q2u%b%(3a|w7x#`!KE--Zmwko(IYZ) zUv7ZQRrL1uc6~@bcA@E?aB*>QBOK0BMVhLTKHsqU2&rpqoRO=1wz9DiKL0d6$(}*F zpy==KA5EcL?N5bo?eC{%u+Kj8$Tn0lGU~*tsd+K2tIY9u(SzmT5+<2U*5WNM(-5Z& z3`C0wV&Ic#R$ge&aAvdF&S%b?K?5u6>+Nx0wknb^p2$FIc6RpiOq)-TyuAD^wwFna zh`EY}1~m3yBgIy2oIoIaJEp9BdA}|8Pj`2B#oU3zwbi$^%TJy>sa~_OwkCv!hYMn2 zV!nO)_`VqBj8&0x558m8m^PXU#qezMeC@6n49`6o$j$TwPtY4iJStR8k`=OHPOA<|g@& z)R-rSK%|zIDt-|NRx9fOFUS`;;CKGfZ0n@fRy{?N2R=U0hPqzsnnOba$sIpS^*U zrMEx=0|Ps+uOVt`Yd`Po^Jmlaecp_+oSBp9>FHE66*D4HI_DQ$>eSQ)d+po-%a|C! zcog&qpU>}%KQshI;X7~{_*IxQJL^ay*{=nenE^XHJ4ko{nVdN~N(6O;#bQ-(I0cIe zX*)Z+mDN?$_9b`skj?j}Ic;VxE^-o*k`?FC@~hQ>P z_=gW4+D!wYA`{)b(NXD0J}2d1b3X5@p1ywH;8gDEN7@3%;VlcNIztz;JPfSm*Ev;b7A3r8FnFVeM;*wKR zFdxZ_+lvE~!;+GcAKyh9kcnPi7S^2PQ(e|9{R^jHOv)%PI;xeM_K36VB7 zHg4}>x-{M4M6SQZmhnvjstL;0NP7*Jr3FYvlPw6t^75_`eL#n}O>sSx1n ztE;vlG$CUCjmOi&NxjzD22cItm+i%&j7QMllMjIOe-#&NV$@iBdsqsEvcTs*sVi$2 z0{0h&y|d$l!*wscs7NlJsf^M4F7UA%t`SS?Pwei-)t7lu>2zf-mkY~>#%5dR<S~6t_|aRO-_U5} zz`L&S6FogW5&LWBeZ0NJ>d(LWLd`2Ez>{a6#^c7{zLmf9OcsGabcXYeu5xvXh8&5+ zUX9eWG;uz90(W|Ue?L7pH?6*2lkXv=WZIi1rEJTubtrp2R%PSYG@bhJp~1%dv)qLR z52yKp-&}MJ4DbpdR#i}_#7NQc2j_x=gKZg395awvwsv+X#xRIF&`PI*xYQ)!c+uFk zsmAEtWq9P~`#=8e@8{Pi*yU+HAi><++|*8-=;v_m7WyAoQo8406BZIeZEQTr;cz1E z)h7x}xf2uldKd{9>_uOa_=oj%+RV&F7##i=y4lFs_{zw=)1A@Vs&C40;x;xm66b8skdetq zHjIt9ejR`Hs%jObPYBsa6@51|^FxqBMLHCEe`cnHk&zMcOh0EKtgBsT24tCEG(Q~N~g0&$Ho!|2M4bubk;GMNv>K`hlwv< zy^_FSFd(#CrwaX>A-&%at7~hLIyyRg2M6ckzW=9e$6Y&*eTz<)x~U?KMB4tT?gHrq zRG_4cVuHFQnT#GEAJ;WBtZ@d7W*!lNU6QA3(!OHF0FWlvhKGl@i?lCUxVRKKDCG`- zue2r*V96FJ?%dp@wzl@+k(<#(kd$p8Y)T9a47d#q=!wb6)wmfMk;Ii1-%`^UnVg&) z4K!NM`sub92(^NMnbv?)D#9F6%^V;JSKRdRW86U>L*T&&ClmnRG?rmm`6l}EO=A)j2)MkMMFzB zhnAj$#|Wy{wapt2{yi5yeQIw' + escapeHtml(reason) + '

' + + '
' + + '' + + '
'; + overlay.appendChild(box); + box.querySelector(".action-modal-btn-ok").addEventListener("click", function () { + destroyOverlay(overlay); + }); + } + + function showConfirmModal(message, href) { + var overlay = createOverlay(); + var box = document.createElement("div"); + box.className = "action-modal-box"; + box.innerHTML = + '

' + escapeHtml(message) + '

' + + '
' + + '' + + '' + + '
'; + overlay.appendChild(box); + box.querySelector(".action-modal-btn-cancel").addEventListener("click", function () { + destroyOverlay(overlay); + }); + box.querySelector(".action-modal-btn-confirm").addEventListener("click", function () { + window.location.href = href; + }); + } + + function escapeHtml(str) { + var div = document.createElement("div"); + div.appendChild(document.createTextNode(str)); + return div.innerHTML; + } +}); diff --git a/src/core/tests/test_html_thumbnail_generators.py b/src/core/tests/test_html_thumbnail_generators.py index 58aa40f88..75dabdd09 100644 --- a/src/core/tests/test_html_thumbnail_generators.py +++ b/src/core/tests/test_html_thumbnail_generators.py @@ -1,9 +1,8 @@ +from django.template import engines from django.test import TestCase from django.urls import reverse from core.models import ( - DEFAULT_MARKER_THUMBNAIL_HEIGHT, - DEFAULT_MARKER_THUMBNAIL_WIDTH, ExhibitTypes, ) from core.tests.factory import ( @@ -15,7 +14,14 @@ from core.tests.utils import get_example_object -class TestMarkerThumbnailGenerators(TestCase): +def render_template(template_name, context): + """Render a Jinja2 template with the given context.""" + jinja_engine = engines["jinja2"] + template = jinja_engine.get_template(template_name) + return template.render(context) + + +class TestMarkerThumbnailTemplates(TestCase): def setUp(self): self.marker = MarkerFactory(title="Test Marker", author="Test Author") @@ -29,33 +35,42 @@ def test_marker_as_html(self): assert " - + +
Date: Fri, 3 Jul 2026 12:37:15 +0100 Subject: [PATCH 02/11] Refactor profile page and modal pages CSS for new thumbnails --- .../jinja2/core/components/action-menu.jinja2 | 2 +- src/core/jinja2/core/exhibit_create_ar.jinja2 | 1 + src/core/jinja2/core/exhibit_create_mr.jinja2 | 1 + .../core/templates/artwork_thumbnail.jinja2 | 23 ++-- .../core/templates/marker_thumbnail.jinja2 | 5 +- .../core/templates/object_thumbnail.jinja2 | 19 +-- .../core/templates/sound_thumbnail.jinja2 | 2 +- src/core/jinja2/core/upload-artwork.jinja2 | 1 + src/core/static/css/action-menu.css | 10 +- src/core/static/css/generator.css | 2 +- src/core/static/css/marker-creation.css | 6 + src/core/static/css/repository-list.css | 115 ++++++++++++++++-- src/core/static/css/reset.css | 62 +++++----- src/users/static/css/profile.css | 24 ++-- 14 files changed, 192 insertions(+), 81 deletions(-) diff --git a/src/core/jinja2/core/components/action-menu.jinja2 b/src/core/jinja2/core/components/action-menu.jinja2 index c2912350c..c608eb873 100644 --- a/src/core/jinja2/core/components/action-menu.jinja2 +++ b/src/core/jinja2/core/components/action-menu.jinja2 @@ -12,7 +12,7 @@ diff --git a/src/core/jinja2/core/exhibit_create_ar.jinja2 b/src/core/jinja2/core/exhibit_create_ar.jinja2 index fc3b0a3c1..cdbc872ab 100644 --- a/src/core/jinja2/core/exhibit_create_ar.jinja2 +++ b/src/core/jinja2/core/exhibit_create_ar.jinja2 @@ -3,6 +3,7 @@
{# FIXME: maybe this can be improved #} + {# FIXME: maybe this can be improved #} + {{ artwork.title }}
+ height="80" + width="80">
{% if artwork.augmented.is_video %} {% elif artwork.augmented.is_3d %} {% if artwork.augmented.thumbnail %} + height="80" + width="80"> {% else %} + src="data:image/svg+xml;utf8,3D" + height="80" + width="80"> {% endif %} {% else %} + height="80" + width="80"> {% endif %} {% if editable %} {% set can_edit = true %} diff --git a/src/core/jinja2/core/templates/marker_thumbnail.jinja2 b/src/core/jinja2/core/templates/marker_thumbnail.jinja2 index a697ff627..245bf0de6 100644 --- a/src/core/jinja2/core/templates/marker_thumbnail.jinja2 +++ b/src/core/jinja2/core/templates/marker_thumbnail.jinja2 @@ -1,8 +1,9 @@ +{{ marker.title }} + height="128" + width="128"> {% if editable %} {% set can_edit = not marker.is_used_by_other_user() %} {% set can_delete = not marker.in_use %} diff --git a/src/core/jinja2/core/templates/object_thumbnail.jinja2 b/src/core/jinja2/core/templates/object_thumbnail.jinja2 index 576e80804..f34656d40 100644 --- a/src/core/jinja2/core/templates/object_thumbnail.jinja2 +++ b/src/core/jinja2/core/templates/object_thumbnail.jinja2 @@ -1,3 +1,4 @@ +{{ object.title }} {% if object.is_video %} {% elif object.is_3d %} {% if object.thumbnail %} + height="128" + width="128"> {% else %} + src="data:image/svg+xml;utf8,3D" + height="128" + width="128"> {% endif %} {% else %} + height="128" + width="128"> {% endif %} {% if editable %} {% set can_edit = not object.is_used_by_other_user() %} diff --git a/src/core/jinja2/core/templates/sound_thumbnail.jinja2 b/src/core/jinja2/core/templates/sound_thumbnail.jinja2 index 2cb881009..023ca0f62 100644 --- a/src/core/jinja2/core/templates/sound_thumbnail.jinja2 +++ b/src/core/jinja2/core/templates/sound_thumbnail.jinja2 @@ -1,4 +1,4 @@ -{{ sound.title }} +{{ sound.title }}
{% if request.user.is_authenticated %} -
-

- {{ _("Welcome, ") }}{{ request.user.username }} -

+
+ {{ _("Welcome,") }} + +
+ {{ _("My Creations") }} + {{ _("Edit profile") }} +
+ {{ csrf_input }} + +
+
{% else %}
+ diff --git a/src/core/static/css/header.css b/src/core/static/css/header.css index 2a8a6eea5..046f1be62 100644 --- a/src/core/static/css/header.css +++ b/src/core/static/css/header.css @@ -1,14 +1,97 @@ +.header .container { + display: flex; + justify-content: space-between; + align-items: center; + background-image: linear-gradient( + to top, + #77777766, + #77777766 50%, + transparent 50%, + transparent + ); + background-position: 100% 10%; + background-size: 100% 200%; +} + .aside { display: flex; justify-content: flex-end; align-items: flex-end; gap: 0px 5px; font-size: 1em; - height: 80px; + flex-shrink: 0; } .logo{ - height: 80px; display: flex; - flex-direction: column-reverse; + align-items: center; +} + +/* User menu dropdown */ +.user-menu-wrapper { + position: relative; + display: inline-flex; + align-items: flex-end; + gap: 5px; +} + +.welcome-text { + color: #777; + font-size: 1.2em; +} + +button#user-menu-btn.user-menu-button { + all: unset; + display: inline-flex; + align-items: center; + padding: 0; + background: none; + color: #03b595; + border: none; + cursor: pointer; + font-family: inherit; + font-size: 2em; + font-weight: normal; +} + +button#user-menu-btn.user-menu-button:hover { + color: #028a70; +} + +.user-menu-list { + position: absolute; + top: calc(100% + 4px); + right: 0; + left: auto; + min-width: 160px; + background-color: #ffffff; + border: 1px solid #000000; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + opacity: 0; + visibility: hidden; + transform: translateY(-10px); + transition: all 0.2s ease; + z-index: 1000; +} + +.user-menu-list.show { + opacity: 1; + visibility: visible; + transform: translateY(0); +} + +.user-menu-list .user-menu-option { + all: unset; + display: block; + width: 100%; + padding: 10px 16px; + font-size: 14px; + color: #000; + cursor: pointer; + box-sizing: border-box; + text-align: left; +} + +.user-menu-list .user-menu-option:hover { + background-color: #f5f5f5; } \ No newline at end of file From 3f039995c2df2d33a16dd8f4c1fde70fd0b12537 Mon Sep 17 00:00:00 2001 From: Pablo Silva Date: Fri, 3 Jul 2026 12:39:12 +0100 Subject: [PATCH 05/11] Refactor the user stats box for bigger and smaller screens --- .../jinja2/users/components/userbox.jinja2 | 26 ++---- src/users/static/css/profile.css | 87 +++++++++++-------- 2 files changed, 59 insertions(+), 54 deletions(-) diff --git a/src/users/jinja2/users/components/userbox.jinja2 b/src/users/jinja2/users/components/userbox.jinja2 index f2f295d06..b10813208 100644 --- a/src/users/jinja2/users/components/userbox.jinja2 +++ b/src/users/jinja2/users/components/userbox.jinja2 @@ -2,42 +2,30 @@ {% set objects_qty = objects|length %} {% set exhibits_qty = ar_exhibits|length + mr_exhibits|length %} {% set artworks_qty = artworks|length %} -
-
+
+
{% if markers_qty %} {{ markers_qty }} {{ _(" marker(s)") }} {% else %} {{ _("No uploads yet.") }} {% endif %}
-
+
{% if objects_qty %} {{ objects_qty }} {{ _(" Object(s)") }} {% else %} {{ _("No uploads yet.") }} {% endif %}
-
- {{ _("Edit profile") }} - - {{ _("Log out") }} -
{% if artworks_qty %} -
+
{{ _("Jandig Artist") }}
{{ artworks_qty }} {{ _(" Artworks") }} {% else %} -
+
{{ _("No Artworks yet.") }} @@ -46,14 +34,14 @@ {% endif %}
{% if exhibits_qty %} -
+
{{ _("Jandig Curator") }}
{{ exhibits_qty }} {{ _(" Exhibits") }} {% else %} -
+
{{ _("No Exhibitions yet.") }} diff --git a/src/users/static/css/profile.css b/src/users/static/css/profile.css index 08a3ed5eb..1704ba76e 100644 --- a/src/users/static/css/profile.css +++ b/src/users/static/css/profile.css @@ -1,60 +1,65 @@ .userbox { - margin-bottom: 20px; + margin-bottom: 35px; background: #f1f1f1; - padding: 20px; -} - -.countMrk, .countObj, .editProf { - height: 20px; - line-height: 20px; - margin: 0; - margin-bottom: 20px; + padding: 15px 10px; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 8px; } .countMrk, .countObj { - font-size: 0.75em; - line-height: 1.75em; - padding-left: 27px; + height: 28px; + line-height: 28px; + margin: 0; + flex: 1 1 calc(50% - 40px); + min-width: 0; + font-size: 0.8em; + padding-left: 32px; text-align: left; - flex-basis: calc(50% - 30px); + box-sizing: border-box; } .countMrk { background: url('../images/icons/icoMrk.png') no-repeat center left; - background-size: contain; + background-size: 24px 24px; } .countObj { background: url('../images/icons/icoObj.png') no-repeat center left; - background-size: contain; + background-size: 24px 24px; } .bdgArt, .bdgCur { - text-align: center; - height: 50px; - padding-top: 60px; - line-height: 25px; - font-size: 0.85em; + flex: 1 1 calc(50% - 40px); + min-width: 0; + text-align: left; + height: 28px; + padding-left: 32px; + line-height: 14px; + font-size: 0.8em; margin-bottom: 0; white-space: nowrap; + box-sizing: border-box; } .bdgArt { background: url('../images/icons/bdgArt.png') no-repeat; - background-position: center top; - background-size: auto; + background-position: left center; + background-size: 24px 24px; } .bdgCur { background: url('../images/icons/bdgCur.png') no-repeat; - background-position: center top; - background-size: auto; + background-position: left center; + background-size: 24px 24px; } .bdgArt.empty, .bdgCur.empty { background: url('../images/icons/icoEpt.png') no-repeat; - background-position: center top; - background-size: auto; + background-position: left center; + background-size: 24px 24px; } .profile-options-flex { @@ -126,29 +131,41 @@ /**** Media Queries *****/ @media all and (min-width: 800px) { - .countMrk, .countObj { - flex-basis: calc(30% - 20px); + .userbox { + flex-wrap: nowrap; + justify-content: space-evenly; + } + + .profile-section-title { + font-size: 1.2em; } - .countMrk, .countObj, .editProf { - margin-bottom: 20px; + .profile-section-title .icon { + width: 30px; + height: 30px; + } + + .countMrk, .countObj { + flex: none; + font-size: 0.85em; + padding-left: 32px; } .bdgArt, .bdgCur { + flex: none; text-align: left; - height: 50px; - padding-left: 65px; + height: 28px; + padding-left: 32px; padding-top: 0px; - line-height: 25px; + line-height: 14px; font-size: 0.85em; margin-bottom: 0; - flex-basis: calc(50% - 70px); white-space: nowrap; } .bdgArt,.bdgCur,.bdgArt.empty, .bdgCur.empty { background-position: left center; - background-size: contain; + background-size: 24px 24px; } .profile-options-flex .userbox .option-link { From b9df503240a30e24eec493b86fe4a060c3af3318 Mon Sep 17 00:00:00 2001 From: Pablo Silva Date: Fri, 3 Jul 2026 12:39:38 +0100 Subject: [PATCH 06/11] Reduce pagination size for objects and sounds --- src/blog/static/css/blog.css | 4 ++-- src/config/settings.py | 1 + src/core/static/css/home.css | 4 +++- src/core/static/css/warning.css | 2 +- src/core/views/views.py | 26 +++++++++++++------------- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/blog/static/css/blog.css b/src/blog/static/css/blog.css index e6580be2d..4e214f6a3 100644 --- a/src/blog/static/css/blog.css +++ b/src/blog/static/css/blog.css @@ -2,7 +2,7 @@ #blog-area, #clipping-area, #posts-area{ - max-width: 600px; + max-width: 100%; margin: 0 auto; } .post{ @@ -31,7 +31,7 @@ /* Post Detail Page */ #post, #post-area{ - max-width: 600px; + max-width: 100%; margin: 1.5em auto; } #post{ diff --git a/src/config/settings.py b/src/config/settings.py index 5fddec815..0c4ec83aa 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -154,6 +154,7 @@ def debug(_): PAGE_SIZE = 20 MODAL_PAGE_SIZE = 40 +OBJECT_MODAL_PAGE_SIZE = 16 REST_FRAMEWORK = { "DEFAULT_RENDERER_CLASSES": [ diff --git a/src/core/static/css/home.css b/src/core/static/css/home.css index f78bbb6ef..5725d8371 100644 --- a/src/core/static/css/home.css +++ b/src/core/static/css/home.css @@ -19,11 +19,13 @@ } .use-guide { - max-width: 300px; + width: 100%; + max-width: 100%; margin: 0 auto; margin-bottom: 20px; padding: 20px; background: url(../images/icons/fill.png), #e6e6e6; + box-sizing: border-box; } .use-guide blockquote:after, .use-guide blockquote:before { diff --git a/src/core/static/css/warning.css b/src/core/static/css/warning.css index 52927077c..877398e0f 100644 --- a/src/core/static/css/warning.css +++ b/src/core/static/css/warning.css @@ -6,7 +6,7 @@ padding: 16px 24px; margin-top: 10px; color: #111; - max-width: 600px; + max-width: 100%; border-radius: 4px; box-shadow: 0 0 4px rgba(0, 0, 0, 0.05); } diff --git a/src/core/views/views.py b/src/core/views/views.py index f28f40c0d..3366697d6 100644 --- a/src/core/views/views.py +++ b/src/core/views/views.py @@ -242,14 +242,14 @@ def object_upload(request): form = UploadObjectForm() sounds = Sound.objects.all().order_by("-created") - paginator_sounds = Paginator(sounds, settings.MODAL_PAGE_SIZE) + paginator_sounds = Paginator(sounds, settings.OBJECT_MODAL_PAGE_SIZE) return render( request, "core/upload-object.jinja2", { "form": form, "edit": False, - "sounds": sounds[: settings.MODAL_PAGE_SIZE], + "sounds": sounds[: settings.OBJECT_MODAL_PAGE_SIZE], "total_sound_pages": paginator_sounds.num_pages, }, ) @@ -395,7 +395,7 @@ def edit_object(request): form = UploadObjectForm(initial=model_data) sounds = Sound.objects.all().order_by("-created") - paginator_sounds = Paginator(sounds, settings.MODAL_PAGE_SIZE) + paginator_sounds = Paginator(sounds, settings.OBJECT_MODAL_PAGE_SIZE) return render( request, @@ -404,7 +404,7 @@ def edit_object(request): "form": form, "model": model, "edit": True, - "sounds": sounds[: settings.MODAL_PAGE_SIZE], + "sounds": sounds[: settings.OBJECT_MODAL_PAGE_SIZE], "selected_sound": model.sound.id if model.sound else None, "total_sound_pages": paginator_sounds.num_pages, }, @@ -438,14 +438,14 @@ def _get_artwork_context_data(form, artwork_instance=None): ) sound_list = Sound.objects.all().order_by("-created") paginator_marker = Paginator(marker_list, settings.MODAL_PAGE_SIZE) - paginator_object = Paginator(object_list, settings.MODAL_PAGE_SIZE) - paginator_sound = Paginator(sound_list, settings.MODAL_PAGE_SIZE) + paginator_object = Paginator(object_list, settings.OBJECT_MODAL_PAGE_SIZE) + paginator_sound = Paginator(sound_list, settings.OBJECT_MODAL_PAGE_SIZE) context = { "form": form, - "sound_list": sound_list[: settings.MODAL_PAGE_SIZE], + "sound_list": sound_list[: settings.OBJECT_MODAL_PAGE_SIZE], "marker_list": marker_list[: settings.MODAL_PAGE_SIZE], - "object_list": object_list[: settings.MODAL_PAGE_SIZE], + "object_list": object_list[: settings.OBJECT_MODAL_PAGE_SIZE], "total_marker_pages": paginator_marker.num_pages, "total_object_pages": paginator_object.num_pages, "total_sound_pages": paginator_sound.num_pages, @@ -513,7 +513,7 @@ def get_element(request): case _: raise Http404("Invalid element type") - paginator = Paginator(qs, settings.MODAL_PAGE_SIZE) + paginator = Paginator(qs, settings.MODAL_PAGE_SIZE if element_type != "object" and element_type != "sound" else settings.OBJECT_MODAL_PAGE_SIZE) if page > paginator.num_pages: page = paginator.num_pages @@ -564,13 +564,13 @@ def _get_mr_exhibit_context_data(form, edit=False): objects = Object.objects.all().order_by("-created") sounds = Sound.objects.all().order_by("-created") - paginator_objects = Paginator(objects, settings.MODAL_PAGE_SIZE) - paginator_sounds = Paginator(sounds, settings.MODAL_PAGE_SIZE) + paginator_objects = Paginator(objects, settings.OBJECT_MODAL_PAGE_SIZE) + paginator_sounds = Paginator(sounds, settings.OBJECT_MODAL_PAGE_SIZE) context = { "form": form, - "objects": objects[: settings.MODAL_PAGE_SIZE], - "sounds": sounds[: settings.MODAL_PAGE_SIZE], + "objects": objects[: settings.OBJECT_MODAL_PAGE_SIZE], + "sounds": sounds[: settings.OBJECT_MODAL_PAGE_SIZE], "total_object_pages": paginator_objects.num_pages, "total_sound_pages": paginator_sounds.num_pages, } From 10e0b4a39c52e44417811bfa1599906f84a1dda5 Mon Sep 17 00:00:00 2001 From: Pablo Silva Date: Fri, 3 Jul 2026 13:29:58 +0100 Subject: [PATCH 07/11] Improve modal spacing by joining title and navigation buttons --- src/core/jinja2/core/exhibit_create_ar.jinja2 | 1 - src/core/jinja2/core/exhibit_create_mr.jinja2 | 36 +++++++------------ .../core/templates/artwork_modal.jinja2 | 6 ++-- .../jinja2/core/templates/marker_modal.jinja2 | 3 +- .../jinja2/core/templates/object_modal.jinja2 | 3 +- .../jinja2/core/templates/sound_modal.jinja2 | 3 +- src/core/jinja2/core/upload-artwork.jinja2 | 36 +++++++------------ src/core/static/css/generic-modal.css | 18 ++++++++-- src/core/static/css/marker-creation.css | 27 ++++++++++++++ src/core/tests/test_get_elements.py | 2 +- 10 files changed, 74 insertions(+), 61 deletions(-) diff --git a/src/core/jinja2/core/exhibit_create_ar.jinja2 b/src/core/jinja2/core/exhibit_create_ar.jinja2 index 78f52ba3c..e3714269c 100644 --- a/src/core/jinja2/core/exhibit_create_ar.jinja2 +++ b/src/core/jinja2/core/exhibit_create_ar.jinja2 @@ -38,7 +38,6 @@
{% if artworks %} - {% with repository_list = artworks, element_type="artwork", htmx="false", selected=selected_artworks %} {% include "core/components/item-list.jinja2" %} {% endwith %} diff --git a/src/core/jinja2/core/exhibit_create_mr.jinja2 b/src/core/jinja2/core/exhibit_create_mr.jinja2 index 89daa026c..a2a2bbf00 100644 --- a/src/core/jinja2/core/exhibit_create_mr.jinja2 +++ b/src/core/jinja2/core/exhibit_create_mr.jinja2 @@ -39,14 +39,8 @@ {{ form.visible_fields()[4] .errors }} -

{{ ar_object.title }}

{{ "{} {}".format(_("Author:") ,ar_object.author)}}

{{ "{} {}{} {}".format(_("Uploaded by") , ar_object.owner.user.username,_(", in"), ar_object.created.strftime("%d/%m/%Y")) }} @@ -33,7 +33,6 @@ {% endif %}

-

Download file:

{{ "{} ({})".format(_("Download File") , ar_object.file_size | filesizeformat)}}

diff --git a/src/core/jinja2/core/templates/sound_modal.jinja2 b/src/core/jinja2/core/templates/sound_modal.jinja2 index 5c3a9b7f4..4616f4c08 100644 --- a/src/core/jinja2/core/templates/sound_modal.jinja2 +++ b/src/core/jinja2/core/templates/sound_modal.jinja2 @@ -11,14 +11,13 @@ < {% endif %} +

{{ sound.title }}

-

{{ sound.title }}

{{ "{} {}".format(_("Author:") ,sound.author)}}

{{ "{} {}{} {}".format(_("Uploaded by") , sound.owner.user.username,_(", in"), sound.created.strftime("%d/%m/%Y")) }}

{{ sound.used_in_html_string() | safe }}

{{ sound.as_html() | safe }} -

Download file:

{{ "{} ({})".format(_("Download File") , sound.file_size | filesizeformat)}}

diff --git a/src/core/jinja2/core/upload-artwork.jinja2 b/src/core/jinja2/core/upload-artwork.jinja2 index db9efc999..47a0b405d 100644 --- a/src/core/jinja2/core/upload-artwork.jinja2 +++ b/src/core/jinja2/core/upload-artwork.jinja2 @@ -52,14 +52,8 @@ {{ form.visible_fields()[5].errors }} #}
+
{% endblock %} {% include "core/footer.jinja2" %} diff --git a/src/core/jinja2/core/home_v2.jinja2 b/src/core/jinja2/core/home_v2.jinja2 deleted file mode 100644 index a27c7915c..000000000 --- a/src/core/jinja2/core/home_v2.jinja2 +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - {# #} - - - - - Jandig ARte - - - - - {% block extra_css %}{% endblock %} - - - - {% include "core/header.jinja2" %} - {% include "core/useful_links.jinja2" %} - {% block content %} -
-
-
-

{{ _("Welcome to Jandig") }}

-
- {{ _("Uma comunidade de código aberto de arte em Realidade Estendida, que viabiliza a produção de exposições e obras unindo arte e tecnologia.") }} -
-
-
-
-

{{ _("No vídeo abaixo, apresentamos o que é a Jandig, o conceito da plataforma e suas possibilidades.") }}

-
- -
-
-
-

- {{ _("Se você se identificou com a Jandig e acredita que a plataforma é útil para você criar seu projeto com realidade aumentada (seja ele uma exposição, cartaz, adesivo, mural, livro ou o que mais sua imaginação puder criar), deixamos para você um passo a passo de como utilizá-la. Ele explica o processo para subir seu conteúdo ou remixar o conteúdo já existente na plataforma.") }} -

-
- -
-

- {{ _("Para acompanhar o passo a passo e colocar em prática,") }} - {{ _("faça seu cadastro") }} - {{ _("antes de começar a assistir. Depois, volte aqui e dê play no vídeo. - Se você preferir, também pode acessar o") }} - {{ _("passo a passo por texto.") }} - {{ _("Além disso, no site da Jandig, na área de") }} - {{ _("Ajuda") }}, - {{ _("há outros tutoriais que podem ser úteis. Não deixe de consultá-los se sentir que é necessário.") }} -

-

- {{ _("Esperamos ver suas obras e exposições na Jandig! Se você criar algo, não deixe de compartilhar conosco. Temos um") }} - {{ _("canal no Telegram") }} - {{ _("voltado para a comunidade, onde é possível tirar dúvidas e compartilhar suas criações e experiências.") }} -

-
-
- {% endblock %} - {% include "core/footer.jinja2" %} - - diff --git a/src/core/static/css/home.css b/src/core/static/css/home.css index 5725d8371..c6e4eff2e 100644 --- a/src/core/static/css/home.css +++ b/src/core/static/css/home.css @@ -41,6 +41,65 @@ padding-top: 6%; } +.home-section { + padding: 20px; + max-width: 80%; + margin: 0 auto; +} + +.home-highlight { + max-width: 80%; + min-width: 30%; +} + +.home-highlight blockquote:after, .home-highlight blockquote:before { + background: #03b5951a; +} + +.stripped-bg { + background-color: #f8f8f8; + background-image: linear-gradient(45deg, #f8f8f8 25%, #f5f5f5 25%, #f5f5f5 50%, #f8f8f8 50%, #f8f8f8 75%, #f5f5f5 75%, #f5f5f5 100%); + background-size: 30px 30px; +} + +.home-container { + border-color: rgb(229 231 235 / var(--tw-border-opacity, 1)); + border-width: 1px; + --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + box-shadow: 0 0 #0000, 0 0 #0000, var(--tw-shadow); + border-style: solid; + width: 80%; + margin: 0 auto; + margin-bottom: 50px; + max-width: 1000px; +} + +.home-container p { + font-family: "Istok Web", serif; + text-align: justify; + font-size: 1.125rem; + color: rgb(55 65 81); +} + +.home-container a { + color: #03b595; +} + +/* https://stackoverflow.com/questions/35814653/automatic-height-when-embedding-a-youtube-video */ +.video-container { + position: relative; + padding-bottom: 56.25%; /* 16:9 */ + height: 0; +} + +.video-container iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + margin: 0 auto; +} /* Desktops */ @media(min-width: 800px) { @@ -48,5 +107,10 @@ max-width: 560px; margin-bottom: 30px; } + .home-highlight { + max-width: 60%; + min-width: 30%; + } +} } \ No newline at end of file diff --git a/src/core/static/css/home_v2.css b/src/core/static/css/home_v2.css deleted file mode 100644 index 732860898..000000000 --- a/src/core/static/css/home_v2.css +++ /dev/null @@ -1,97 +0,0 @@ -.header .signup-btn, .header .login-btn { - background-color: black; - text-align: center; - height: 40px; - line-height: 40px; - padding: 0px 8px; - max-width: 70px; - min-width: 60px; -} - -.header .login-btn { - background-color: #03b595; -} - -.header .signup-btn a, .header .login-btn a { - font-weight: bold; - color: white; - display: block; -} - - -.home-section { - padding: 20px; - max-width: 80%; - margin: 0 auto; - -} -.home-highlight { - max-width: 80%; - min-width: 30%; -} -.home-highlight blockquote:after, .home-highlight blockquote:before { - background: #03b5951a; -} - -.use-guide .authorize-camera { - background-color: black; - text-align: center; - max-width: 280px; - height: 35px; - margin: auto; - padding-top: 6%; -} - -.stripped-bg { - background-color: #f8f8f8; - background-image: linear-gradient(45deg, #f8f8f8 25%, #f5f5f5 25%, #f5f5f5 50%, #f8f8f8 50%, #f8f8f8 75%, #f5f5f5 75%, #f5f5f5 100%); - background-size: 30px 30px; -} - -.home-container { - border-color: rgb(229 231 235 / var(--tw-border-opacity, 1)); - border-width: 1px; - --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); - box-shadow: 0 0 #0000, 0 0 #0000, var(--tw-shadow); - border-style: solid; - width: 80%; - margin: 0 auto; - margin-bottom: 50px; - max-width: 1000px; -} - -.home-container p -{ - font-family: "Istok Web", serif; - text-align: justify; - font-size: 1.125rem; - color: rgb(55 65 81); -} - -.home-container a { - color: #03b595; -} - -/* https://stackoverflow.com/questions/35814653/automatic-height-when-embedding-a-youtube-video */ -.video-container { - position: relative; - padding-bottom: 56.25%; /* 16:9 */ - height: 0; -} - -.video-container iframe { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - margin: 0 auto; -} -/* Desktops */ -@media(min-width: 800px) { - .home-highlight { - max-width: 60%; - min-width: 30%; - } - -} \ No newline at end of file diff --git a/src/core/tests/test_html_thumbnail_generators.py b/src/core/tests/test_html_thumbnail_generators.py index 75dabdd09..35e270a62 100644 --- a/src/core/tests/test_html_thumbnail_generators.py +++ b/src/core/tests/test_html_thumbnail_generators.py @@ -51,8 +51,7 @@ def test_marker_thumbnail_editable(self): edit_url = reverse("edit-marker") + f"?id={self.marker.id}" preview_url = reverse("marker-preview") + f"?id={self.marker.id}" delete_url = ( - reverse("delete-content") - + f"?content_type=marker&id={self.marker.id}" + reverse("delete-content") + f"?content_type=marker&id={self.marker.id}" ) assert edit_url in html assert delete_url in html diff --git a/src/core/urls.py b/src/core/urls.py index bee1ddbe3..3fba75196 100644 --- a/src/core/urls.py +++ b/src/core/urls.py @@ -6,8 +6,7 @@ documentation, favicon, health_check, - home_new, - home_old, + home, manifest, me_hotsite, robots_txt, @@ -39,9 +38,8 @@ ) urlpatterns = [ - path("", home_new, name="home"), + path("", home, name="home"), path("ar/", ar_view, name="ar-view"), - path("arviewer/", home_old, name="home-old"), path("artwork/", artwork_preview, name="artwork-preview"), path("artworks/create/", create_artwork, name="create-artwork"), path("artworks/edit/", edit_artwork, name="edit-artwork"), diff --git a/src/core/views/static_views.py b/src/core/views/static_views.py index 26f45786b..9a8f02fb9 100644 --- a/src/core/views/static_views.py +++ b/src/core/views/static_views.py @@ -23,11 +23,7 @@ def me_hotsite(request): return render(request, "core/ME/hotsite.html", {}) -def home_new(request): - return render(request, "core/home_v2.jinja2", {}) - - -def home_old(request): +def home(request): return render(request, "core/home.jinja2", {}) diff --git a/src/core/views/views.py b/src/core/views/views.py index 3366697d6..34561c878 100644 --- a/src/core/views/views.py +++ b/src/core/views/views.py @@ -513,7 +513,12 @@ def get_element(request): case _: raise Http404("Invalid element type") - paginator = Paginator(qs, settings.MODAL_PAGE_SIZE if element_type != "object" and element_type != "sound" else settings.OBJECT_MODAL_PAGE_SIZE) + paginator = Paginator( + qs, + settings.MODAL_PAGE_SIZE + if element_type != "object" and element_type != "sound" + else settings.OBJECT_MODAL_PAGE_SIZE, + ) if page > paginator.num_pages: page = paginator.num_pages From 8653a55d6849304de53aa533287901feb9567a5c Mon Sep 17 00:00:00 2001 From: Pablo Silva Date: Fri, 3 Jul 2026 15:39:04 +0100 Subject: [PATCH 11/11] Rename focused layout page template name --- src/core/jinja2/core/exhibit_select.jinja2 | 2 +- .../jinja2/core/{arviewer.jinja2 => focused_layout.jinja2} | 7 ------- src/users/jinja2/users/login.jinja2 | 2 +- src/users/jinja2/users/password-change.jinja2 | 2 +- .../jinja2/users/reset-password/password_reset.jinja2 | 2 +- .../users/reset-password/password_reset_complete.jinja2 | 2 +- .../users/reset-password/password_reset_confirm.jinja2 | 2 +- src/users/jinja2/users/signup.jinja2 | 2 +- 8 files changed, 7 insertions(+), 14 deletions(-) rename src/core/jinja2/core/{arviewer.jinja2 => focused_layout.jinja2} (77%) diff --git a/src/core/jinja2/core/exhibit_select.jinja2 b/src/core/jinja2/core/exhibit_select.jinja2 index db5a722c2..08e3ae148 100644 --- a/src/core/jinja2/core/exhibit_select.jinja2 +++ b/src/core/jinja2/core/exhibit_select.jinja2 @@ -1,4 +1,4 @@ -{% extends '/core/arviewer.jinja2' %} +{% extends '/core/focused_layout.jinja2' %} {% block content %} {# FIXME: maybe this can be improved #} diff --git a/src/core/jinja2/core/arviewer.jinja2 b/src/core/jinja2/core/focused_layout.jinja2 similarity index 77% rename from src/core/jinja2/core/arviewer.jinja2 rename to src/core/jinja2/core/focused_layout.jinja2 index 482241eeb..d791da639 100644 --- a/src/core/jinja2/core/arviewer.jinja2 +++ b/src/core/jinja2/core/focused_layout.jinja2 @@ -23,13 +23,6 @@ {% block extra_css %}{% endblock %} {% block extra_js %}{% endblock %} - - - - - {# #} {% if content is defined %}
diff --git a/src/users/jinja2/users/login.jinja2 b/src/users/jinja2/users/login.jinja2 index 1559b45a7..29344b52c 100644 --- a/src/users/jinja2/users/login.jinja2 +++ b/src/users/jinja2/users/login.jinja2 @@ -1,4 +1,4 @@ -{% extends '/core/arviewer.jinja2' %} +{% extends '/core/focused_layout.jinja2' %} {% block content %} {# FIXME: maybe this can be improved #}