From 472eeee2f352554ab1ff446552440ad90d3fc126 Mon Sep 17 00:00:00 2001 From: mohamad Date: Sat, 1 Aug 2026 16:57:49 +0330 Subject: [PATCH] fix: video pagination --- apps/core/admin.py | 23 +++++- ...ogo_sitebranding_hero_logo_alt_and_more.py | 34 +++++++++ ..._sitebranding_footer_icon_size_and_more.py | 23 ++++++ apps/core/models.py | 41 ++++++++-- apps/core/tests/test_site_branding.py | 28 ++++++- apps/pages/tests/test_video.py | 67 +++++++++++++++++ apps/pages/urls.py | 1 + apps/pages/views.py | 74 +++++++++++++++++-- apps/products/urls.py | 10 +++ apps/products/views.py | 72 +++++++++++++++++- static/css/main.css | 36 +++++++++ templates/base.html | 4 + templates/pages/home.html | 4 + templates/partials/_brand_icon.html | 5 +- templates/partials/_footer.html | 2 +- templates/partials/_page_videos.html | 13 +++- templates/videos/archive.html | 56 ++++++++++++++ 17 files changed, 473 insertions(+), 20 deletions(-) create mode 100644 apps/core/migrations/0004_sitebranding_hero_logo_sitebranding_hero_logo_alt_and_more.py create mode 100644 apps/core/migrations/0005_alter_sitebranding_footer_icon_size_and_more.py create mode 100644 templates/videos/archive.html diff --git a/apps/core/admin.py b/apps/core/admin.py index 8c25e6d..8c2dc30 100644 --- a/apps/core/admin.py +++ b/apps/core/admin.py @@ -8,19 +8,36 @@ from .models import SiteBranding, SiteContact @admin.register(SiteBranding) class SiteBrandingAdmin(admin.ModelAdmin): fieldsets = ( - ("Icon", {"fields": ("icon", "icon_alt")}), + ( + "Brand assets", + { + "fields": ( + "icon", + "icon_alt", + "website_icon", + "hero_logo", + "hero_logo_alt", + ), + "description": ( + "Manage each placement independently. Removing an upload restores " + "the built-in Tecvico asset for that placement." + ), + }, + ), ( "Sizes", { "fields": ("navbar_icon_size", "footer_icon_size"), - "description": "Square dimensions in pixels for each placement.", + "description": ( + "Set the logo height for each placement. Its width is calculated " + "automatically so the uploaded image is never cropped or stretched." + ), }, ), ( "Appearance", { "fields": ( - "object_fit", "show_border", "border_width", "border_color", diff --git a/apps/core/migrations/0004_sitebranding_hero_logo_sitebranding_hero_logo_alt_and_more.py b/apps/core/migrations/0004_sitebranding_hero_logo_sitebranding_hero_logo_alt_and_more.py new file mode 100644 index 0000000..55329aa --- /dev/null +++ b/apps/core/migrations/0004_sitebranding_hero_logo_sitebranding_hero_logo_alt_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.15 on 2026-08-01 13:13 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0003_alter_sitebranding_border_color'), + ] + + operations = [ + migrations.AddField( + model_name='sitebranding', + name='hero_logo', + field=models.ImageField(blank=True, help_text='Large brand artwork shown in the homepage hero. Leave empty to use the default hero artwork.', null=True, upload_to='branding/hero/'), + ), + migrations.AddField( + model_name='sitebranding', + name='hero_logo_alt', + field=models.CharField(blank=True, default='Tecvico showcase', help_text='Accessible description for the homepage hero logo.', max_length=200), + ), + migrations.AddField( + model_name='sitebranding', + name='website_icon', + field=models.FileField(blank=True, help_text='Icon shown in browser tabs and bookmarks. Use a square ICO, PNG, SVG, JPG, or WebP file. Leave empty to use the default Tecvico icon.', null=True, upload_to='branding/icons/', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=('ico', 'png', 'svg', 'jpg', 'jpeg', 'webp'))]), + ), + migrations.AlterField( + model_name='sitebranding', + name='icon', + field=models.ImageField(blank=True, help_text='Brand logo shown in the site navigation and footer. Leave empty to use the default logo.', null=True, upload_to='branding/', verbose_name='Header and footer logo'), + ), + ] diff --git a/apps/core/migrations/0005_alter_sitebranding_footer_icon_size_and_more.py b/apps/core/migrations/0005_alter_sitebranding_footer_icon_size_and_more.py new file mode 100644 index 0000000..beda3a8 --- /dev/null +++ b/apps/core/migrations/0005_alter_sitebranding_footer_icon_size_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.15 on 2026-08-01 13:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0004_sitebranding_hero_logo_sitebranding_hero_logo_alt_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='sitebranding', + name='footer_icon_size', + field=models.PositiveSmallIntegerField(default=34, help_text='Maximum height in pixels for the footer logo. Width scales automatically.'), + ), + migrations.AlterField( + model_name='sitebranding', + name='navbar_icon_size', + field=models.PositiveSmallIntegerField(default=26, help_text='Maximum height in pixels for the header logo. Width scales automatically.'), + ), + ] diff --git a/apps/core/models.py b/apps/core/models.py index f278b86..01ff0ab 100644 --- a/apps/core/models.py +++ b/apps/core/models.py @@ -1,3 +1,4 @@ +from django.core.validators import FileExtensionValidator from django.db import models @@ -13,20 +14,50 @@ class SiteBranding(models.Model): upload_to="branding/", blank=True, null=True, - help_text="Logo shown in the site header and footer. Leave empty to use the default static icon.", + verbose_name="Header and footer logo", + help_text="Brand logo shown in the site navigation and footer. Leave empty to use the default logo.", ) icon_alt = models.CharField( max_length=200, blank=True, help_text="Alt text for the brand icon (decorative icons can stay empty).", ) + website_icon = models.FileField( + upload_to="branding/icons/", + blank=True, + null=True, + validators=[ + FileExtensionValidator( + allowed_extensions=("ico", "png", "svg", "jpg", "jpeg", "webp") + ) + ], + help_text=( + "Icon shown in browser tabs and bookmarks. Use a square ICO, PNG, SVG, " + "JPG, or WebP file. Leave empty to use the default Tecvico icon." + ), + ) + hero_logo = models.ImageField( + upload_to="branding/hero/", + blank=True, + null=True, + help_text=( + "Large brand artwork shown in the homepage hero. Leave empty to use " + "the default hero artwork." + ), + ) + hero_logo_alt = models.CharField( + max_length=200, + blank=True, + default="Tecvico showcase", + help_text="Accessible description for the homepage hero logo.", + ) navbar_icon_size = models.PositiveSmallIntegerField( default=26, - help_text="Width and height in pixels for the header icon.", + help_text="Maximum height in pixels for the header logo. Width scales automatically.", ) footer_icon_size = models.PositiveSmallIntegerField( default=34, - help_text="Width and height in pixels for the footer icon.", + help_text="Maximum height in pixels for the footer logo. Width scales automatically.", ) show_border = models.BooleanField( default=False, @@ -61,9 +92,9 @@ class SiteBranding(models.Model): def icon_style(self, size_px): parts = [ - f"width:{size_px}px", + "width:auto", f"height:{size_px}px", - f"object-fit:{self.object_fit}", + "object-fit:contain", ] if self.show_border: parts.append(f"border:{self.border_width}px solid {self.border_color}") diff --git a/apps/core/tests/test_site_branding.py b/apps/core/tests/test_site_branding.py index b2eed72..c516a2e 100644 --- a/apps/core/tests/test_site_branding.py +++ b/apps/core/tests/test_site_branding.py @@ -1,4 +1,5 @@ from django.test import RequestFactory, TestCase +from django.urls import reverse from apps.core.context_processors import site_branding from apps.core.models import SiteBranding @@ -17,7 +18,9 @@ class SiteBrandingModelTests(TestCase): branding.border_width = 2 branding.navbar_icon_size = 30 style = branding.navbar_icon_style - self.assertIn("width:30px", style) + self.assertIn("width:auto", style) + self.assertIn("height:30px", style) + self.assertIn("object-fit:contain", style) self.assertIn("border:2px solid #ffffff", style) def test_icon_style_omits_border_when_disabled(self): @@ -33,3 +36,26 @@ class SiteBrandingContextProcessorTests(TestCase): ctx = site_branding(request) self.assertIn("site_branding", ctx) self.assertIsInstance(ctx["site_branding"], SiteBranding) + + +class SiteBrandingTemplateTests(TestCase): + def test_custom_brand_assets_are_rendered_independently(self): + branding = SiteBranding.load() + branding.icon = "branding/navigation-logo.png" + branding.website_icon = "branding/icons/site-icon.png" + branding.hero_logo = "branding/hero/hero-logo.png" + branding.hero_logo_alt = "Tecvico research platform" + branding.save() + + response = self.client.get(reverse("pages:home")) + + self.assertContains(response, 'src="/media/branding/navigation-logo.png"') + self.assertContains(response, 'href="/media/branding/icons/site-icon.png"') + self.assertContains(response, 'src="/media/branding/hero/hero-logo.png"') + self.assertContains(response, 'alt="Tecvico research platform"') + + def test_default_assets_remain_when_custom_assets_are_empty(self): + response = self.client.get(reverse("pages:home")) + + self.assertContains(response, "images/tecvico/brand-logo.svg") + self.assertContains(response, "images/tecvico/hero-image.svg") diff --git a/apps/pages/tests/test_video.py b/apps/pages/tests/test_video.py index 856947f..0f27376 100644 --- a/apps/pages/tests/test_video.py +++ b/apps/pages/tests/test_video.py @@ -80,6 +80,17 @@ class CustomPageVideoSectionTest(TestCase): class PageVideoTest(TestCase): + def _create_page_videos(self, page, count): + for index in range(count): + PageVideo.objects.create( + page=page, + title=f"Video {index + 1}", + video_source="youtube", + video_url="https://youtu.be/dQw4w9WgXcQ", + order=index, + is_active=True, + ) + def test_contact_page_video(self): PageVideo.objects.create( page=PageVideo.PAGE_CONTACT, @@ -103,6 +114,38 @@ class PageVideoTest(TestCase): response = self.client.get(reverse("pages:faq")) self.assertContains(response, "Tutorial") + def test_page_shows_three_video_preview_and_archive_link(self): + self._create_page_videos(PageVideo.PAGE_FAQ, 8) + + response = self.client.get(reverse("pages:faq")) + + self.assertEqual(response.content.count(b"youtube-nocookie.com/embed"), 3) + self.assertContains( + response, + reverse("pages:video_archive", kwargs={"library": PageVideo.PAGE_FAQ}), + ) + self.assertContains(response, "More videos") + + def test_page_video_archive_is_paginated(self): + self._create_page_videos(PageVideo.PAGE_FAQ, 8) + archive_url = reverse("pages:video_archive", kwargs={"library": PageVideo.PAGE_FAQ}) + + first_page = self.client.get(archive_url) + second_page = self.client.get(archive_url, {"page": 2}) + + self.assertEqual(first_page.status_code, 200) + self.assertEqual(first_page.content.count(b"youtube-nocookie.com/embed"), 6) + self.assertContains(first_page, "Page 1 of 2") + self.assertContains(first_page, "?page=2") + self.assertEqual(second_page.content.count(b"youtube-nocookie.com/embed"), 2) + self.assertContains(second_page, "Page 2 of 2") + + def test_unknown_page_video_archive_returns_404(self): + response = self.client.get( + reverse("pages:video_archive", kwargs={"library": "unknown"}) + ) + self.assertEqual(response.status_code, 404) + class ProductVideoTest(TestCase): def setUp(self): @@ -129,6 +172,30 @@ class ProductVideoTest(TestCase): self.assertContains(response, "Product Demo") self.assertContains(response, "video-block--full") + def test_product_video_preview_links_to_paginated_archive(self): + for index in range(7): + ProductVideo.objects.create( + main_product=self.main_product, + title=f"Product video {index + 1}", + video_source="youtube", + video_url="https://youtu.be/dQw4w9WgXcQ", + order=index, + is_active=True, + ) + archive_url = reverse( + "products:main_product_videos", + kwargs={"main_slug": self.main_product.slug}, + ) + + detail_response = self.client.get(self.main_product.get_absolute_url()) + archive_response = self.client.get(archive_url) + second_page = self.client.get(archive_url, {"page": 2}) + + self.assertEqual(detail_response.content.count(b"youtube-nocookie.com/embed"), 3) + self.assertContains(detail_response, archive_url) + self.assertEqual(archive_response.content.count(b"youtube-nocookie.com/embed"), 6) + self.assertEqual(second_page.content.count(b"youtube-nocookie.com/embed"), 1) + class VideoStyledBackgroundTest(TestCase): def test_styled_background_renders_panel(self): diff --git a/apps/pages/urls.py b/apps/pages/urls.py index cf13072..fd675bf 100644 --- a/apps/pages/urls.py +++ b/apps/pages/urls.py @@ -9,5 +9,6 @@ urlpatterns = [ path("about/", views.AboutView.as_view(), name="about"), path("faq/", views.FAQView.as_view(), name="faq"), path("contact/", views.ContactView.as_view(), name="contact"), + path("videos//", views.PageVideoArchiveView.as_view(), name="video_archive"), path("/", views.CustomPageView.as_view(), name="custom_page"), ] diff --git a/apps/pages/views.py b/apps/pages/views.py index fde4a94..3b30d73 100644 --- a/apps/pages/views.py +++ b/apps/pages/views.py @@ -1,8 +1,10 @@ import random from django.db.models import Prefetch +from django.http import Http404 from django.http import JsonResponse from django.shortcuts import render +from django.urls import reverse from django.views import View from django.views.generic import DetailView, ListView, TemplateView @@ -21,6 +23,18 @@ from .models import ( PageVideo, ) +VIDEO_PREVIEW_LIMIT = 3 +VIDEO_ARCHIVE_PAGE_SIZE = 6 + + +def _video_preview_context(queryset, archive_url): + total = queryset.count() + return { + "videos": queryset[:VIDEO_PREVIEW_LIMIT], + "videos_total_count": total, + "videos_archive_url": archive_url if total > VIDEO_PREVIEW_LIMIT else "", + } + def _homepage_products_catalog_queryset(): return ( @@ -79,10 +93,14 @@ class FAQView(ListView): def get_context_data(self, **kwargs): ctx = super().get_context_data(**kwargs) - ctx["faq_videos"] = PageVideo.objects.filter( + videos = PageVideo.objects.filter( page=PageVideo.PAGE_FAQ, is_active=True, ).order_by("order") + preview = _video_preview_context(videos, reverse("pages:video_archive", kwargs={"library": PageVideo.PAGE_FAQ})) + ctx["faq_videos"] = preview["videos"] + ctx["videos_total_count"] = preview["videos_total_count"] + ctx["videos_archive_url"] = preview["videos_archive_url"] return ctx @@ -95,16 +113,23 @@ class ContactView(View): return f"{a} + {b}" def get(self, request, *args, **kwargs): + videos = PageVideo.objects.filter( + page=PageVideo.PAGE_CONTACT, + is_active=True, + ).order_by("order") + preview = _video_preview_context( + videos, + reverse("pages:video_archive", kwargs={"library": PageVideo.PAGE_CONTACT}), + ) return render( request, self.template_name, { "form": ContactForm(), "captcha_question": self._new_captcha(request), - "contact_videos": PageVideo.objects.filter( - page=PageVideo.PAGE_CONTACT, - is_active=True, - ).order_by("order"), + "contact_videos": preview["videos"], + "videos_total_count": preview["videos_total_count"], + "videos_archive_url": preview["videos_archive_url"], }, ) @@ -168,3 +193,42 @@ class CustomPageView(DetailView): ) ctx["homepage_products_catalog"] = _homepage_products_catalog_queryset() return ctx + + +class PageVideoArchiveView(ListView): + model = PageVideo + template_name = "videos/archive.html" + context_object_name = "videos" + paginate_by = VIDEO_ARCHIVE_PAGE_SIZE + + PAGE_CONFIG = { + PageVideo.PAGE_CONTACT: ("Contact videos", "Guides and updates from the Tecvico team.", "pages:contact"), + PageVideo.PAGE_FAQ: ("FAQ videos", "Video answers to common questions.", "pages:faq"), + } + + def get_page_config(self): + try: + return self.PAGE_CONFIG[self.kwargs["library"]] + except KeyError as exc: + raise Http404("Video library not found.") from exc + + def get_queryset(self): + self.get_page_config() + return PageVideo.objects.filter( + page=self.kwargs["library"], + is_active=True, + ).order_by("order", "pk") + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + title, description, source_url_name = self.get_page_config() + context.update( + { + "library_title": title, + "library_description": description, + "library_back_url": reverse(source_url_name), + "library_back_label": "Back to page", + "pagination_range": context["paginator"].get_elided_page_range(context["page_obj"].number), + } + ) + return context diff --git a/apps/products/urls.py b/apps/products/urls.py index e94b4c2..74ac644 100644 --- a/apps/products/urls.py +++ b/apps/products/urls.py @@ -11,6 +11,16 @@ urlpatterns = [ views.ReleaseAssetDownloadView.as_view(), name="release_asset_download", ), + path( + "/videos/", + views.ProductVideoArchiveView.as_view(), + name="main_product_videos", + ), + path( + "//videos/", + views.ProductVideoArchiveView.as_view(), + name="sub_product_videos", + ), path( "/", views.MainProductDetailView.as_view(), diff --git a/apps/products/views.py b/apps/products/views.py index ec7e52f..b90be2d 100644 --- a/apps/products/views.py +++ b/apps/products/views.py @@ -2,6 +2,7 @@ import mimetypes from django.http import FileResponse, Http404 from django.shortcuts import get_object_or_404 +from django.urls import reverse from django.views import View from django.views.generic import DetailView, ListView, TemplateView @@ -9,6 +10,18 @@ from .models import Article, ArticleCitation, ArticleSection, MainProduct, Produ from .release_assets import FILE_FIELD_BY_ASSET_KEY, RELEASE_ASSET_KEYS, URL_FIELD_BY_ASSET_KEY from .release_context import build_archive_context, build_release_context +VIDEO_PREVIEW_LIMIT = 3 +VIDEO_ARCHIVE_PAGE_SIZE = 6 + + +def _product_video_preview(queryset, archive_url): + total = queryset.count() + return { + "product_videos": queryset[:VIDEO_PREVIEW_LIMIT], + "videos_total_count": total, + "videos_archive_url": archive_url if total > VIDEO_PREVIEW_LIMIT else "", + } + class ReleaseAssetDownloadView(View): def get(self, request, version_id, asset): @@ -58,7 +71,13 @@ class MainProductDetailView(DetailView): context["articles"] = self.object.articles.prefetch_related( "sections", "citations" ).all() - context["product_videos"] = self.object.videos.filter(is_active=True).order_by("order") + videos = self.object.videos.filter(is_active=True).order_by("order", "pk") + context.update( + _product_video_preview( + videos, + reverse("products:main_product_videos", kwargs={"main_slug": self.object.slug}), + ) + ) release_context = build_release_context( self.object.distribution, self.object.versions.filter(is_active=True), @@ -112,7 +131,16 @@ class SubProductDetailView(TemplateView): context["articles"] = sub_product.articles.prefetch_related( "sections", "citations" ).all() - context["product_videos"] = sub_product.videos.filter(is_active=True).order_by("order") + videos = sub_product.videos.filter(is_active=True).order_by("order", "pk") + context.update( + _product_video_preview( + videos, + reverse( + "products:sub_product_videos", + kwargs={"main_slug": main_product.slug, "sub_slug": sub_product.slug}, + ), + ) + ) context["siblings"] = ( SubProduct.objects.filter(main_product=main_product, is_active=True) .exclude(pk=sub_product.pk) @@ -127,6 +155,46 @@ class SubProductDetailView(TemplateView): return context +class ProductVideoArchiveView(ListView): + model = ProductVideo + template_name = "videos/archive.html" + context_object_name = "videos" + paginate_by = VIDEO_ARCHIVE_PAGE_SIZE + + def get_parent(self): + main_product = get_object_or_404( + MainProduct, + slug=self.kwargs["main_slug"], + is_active=True, + ) + sub_slug = self.kwargs.get("sub_slug") + if sub_slug: + return get_object_or_404( + SubProduct, + slug=sub_slug, + main_product=main_product, + is_active=True, + ) + return main_product + + def get_queryset(self): + self.parent_object = self.get_parent() + return self.parent_object.videos.filter(is_active=True).order_by("order", "pk") + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context.update( + { + "library_title": f"{self.parent_object.name} videos", + "library_description": "Tutorials, demonstrations, and technical guides.", + "library_back_url": self.parent_object.get_absolute_url(), + "library_back_label": f"Back to {self.parent_object.name}", + "pagination_range": context["paginator"].get_elided_page_range(context["page_obj"].number), + } + ) + return context + + class SubProductOlderVersionsView(TemplateView): template_name = "products/versions_archive.html" diff --git a/static/css/main.css b/static/css/main.css index d257c5b..e17d153 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -4028,6 +4028,42 @@ a.citation-count-badge:hover { margin-top: 0; } +.video-preview-heading { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; margin-bottom: 1.5rem; } +.video-preview-heading .section-badge { margin-bottom: .55rem; } +.video-preview-heading h2 { font-size: clamp(1.4rem, 3vw, 2rem); } +.video-preview-heading > span { color: var(--text-muted); font-size: .76rem; font-weight: 700; } +.video-preview-more { display: flex; justify-content: center; margin-top: 1.75rem; } + +.video-library-hero .page-hero-content { max-width: none; } +.video-library-section { background: var(--bg-secondary); } +.video-library-summary { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 1.25rem; color: var(--text-secondary); font-size: .8rem; } +.video-library-summary strong { color: var(--text-primary); font-size: 1rem; } +.video-library-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.5rem; } +.video-library-card { min-width: 0; padding: 1.2rem; border: 1px solid var(--border-default); border-radius: var(--radius-md); background: #fff; box-shadow: var(--shadow-sm); } +.video-library-card .video-panel--styled { height: 100%; padding: 1.25rem; } +.video-library-card .video-panel-title, .video-library-card .section-title { font-size: 1.05rem; } +.video-library-card .section-header { margin-bottom: 1rem; text-align: left; } +.video-library-card .section-desc { margin-bottom: 1rem; font-size: .78rem; } +.video-library-empty { grid-column: 1 / -1; } + +.video-pagination { display: grid; grid-template-columns: minmax(110px,1fr) auto minmax(110px,1fr); align-items: center; gap: 1rem; margin-top: 2.25rem; padding-top: 1.5rem; border-top: 1px solid var(--border-default); } +.video-pagination__pages { display: flex; align-items: center; gap: .35rem; } +.video-pagination__page, .video-pagination__ellipsis { display: grid; place-items: center; min-width: 38px; height: 38px; padding: 0 .4rem; border: 1px solid var(--border-default); border-radius: var(--radius-sm); background: #fff; color: #4c586c; font-size: .76rem; font-weight: 700; } +.video-pagination__page:hover { border-color: var(--accent-blue); color: var(--accent-blue); } +.video-pagination__page.is-current { border-color: var(--accent-blue); background: var(--accent-blue); color: #fff; } +.video-pagination__ellipsis { border-color: transparent; background: transparent; } +.video-pagination__direction { color: var(--accent-blue); font-size: .78rem; font-weight: 700; } +.video-pagination__direction:last-child { justify-self: end; } +.video-pagination__direction.is-disabled { color: var(--text-muted); pointer-events: none; } + +@media (max-width: 768px) { + .video-library-grid { grid-template-columns: 1fr; } + .video-pagination { grid-template-columns: 1fr 1fr; } + .video-pagination__pages { grid-column: 1 / -1; grid-row: 1; justify-content: center; flex-wrap: wrap; } + .video-pagination__direction { grid-row: 2; } + .video-preview-heading { align-items: flex-start; } +} + @media (max-width: 640px) { .video-panel--styled { padding: 1.25rem 1rem 1rem; diff --git a/templates/base.html b/templates/base.html index c3c9414..af3d058 100644 --- a/templates/base.html +++ b/templates/base.html @@ -6,7 +6,11 @@ {% block title %}Tecvico{% endblock %} | Tecvico + {% if site_branding.website_icon %} + + {% else %} + {% endif %} diff --git a/templates/pages/home.html b/templates/pages/home.html index a0fa0e9..5adf388 100644 --- a/templates/pages/home.html +++ b/templates/pages/home.html @@ -32,7 +32,11 @@ {% endif %}
+ {% if site_branding.hero_logo %} + {{ site_branding.hero_logo_alt|default:'Tecvico showcase' }} + {% else %} Tecvico showcase + {% endif %}
diff --git a/templates/partials/_brand_icon.html b/templates/partials/_brand_icon.html index 87c209b..7dfb8cc 100644 --- a/templates/partials/_brand_icon.html +++ b/templates/partials/_brand_icon.html @@ -1,6 +1,7 @@ {% load static %} Tecvico diff --git a/templates/partials/_footer.html b/templates/partials/_footer.html index b4c4863..69a155a 100644 --- a/templates/partials/_footer.html +++ b/templates/partials/_footer.html @@ -4,7 +4,7 @@