feat(admin): add "See on site" link in change forms for better navigation
Integrates a "See on site" button in the admin change forms for `Category`, `Brand`, and `Product` models. This allows users to navigate directly to the corresponding storefront page of the object being edited, improving efficiency and user experience. - Introduced `StorefrontLinkMixin` for reusable storefront URL logic. - Added custom template `change_form_with_storefront_link.html` for rendering the button.
This commit is contained in:
parent
f0b92bb475
commit
af69abf8e3
5 changed files with 84 additions and 10 deletions
|
|
@ -69,6 +69,24 @@ from engine.core.models import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class StorefrontLinkMixin:
|
||||||
|
"""Adds a 'See on site' link button to the change form submit row."""
|
||||||
|
|
||||||
|
change_form_template = "admin/core/change_form_with_storefront_link.html"
|
||||||
|
storefront_path_prefix: str = ""
|
||||||
|
|
||||||
|
def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
|
||||||
|
extra_context = extra_context or {}
|
||||||
|
if object_id:
|
||||||
|
obj = self.get_object(request, object_id)
|
||||||
|
if obj and hasattr(obj, "slug"):
|
||||||
|
extra_context["storefront_url"] = (
|
||||||
|
f"https://{settings.STOREFRONT_DOMAIN}"
|
||||||
|
f"/{self.storefront_path_prefix}/{obj.slug}"
|
||||||
|
)
|
||||||
|
return super().changeform_view(request, object_id, form_url, extra_context)
|
||||||
|
|
||||||
|
|
||||||
class FieldsetsMixin:
|
class FieldsetsMixin:
|
||||||
general_fields: list[str] | None = []
|
general_fields: list[str] | None = []
|
||||||
relation_fields: list[str] | None = []
|
relation_fields: list[str] | None = []
|
||||||
|
|
@ -361,12 +379,14 @@ class AttributeValueAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
|
||||||
|
|
||||||
@register(Category)
|
@register(Category)
|
||||||
class CategoryAdmin(
|
class CategoryAdmin(
|
||||||
|
StorefrontLinkMixin,
|
||||||
DjangoQLSearchMixin,
|
DjangoQLSearchMixin,
|
||||||
FieldsetsMixin,
|
FieldsetsMixin,
|
||||||
ActivationActionsMixin,
|
ActivationActionsMixin,
|
||||||
DraggableMPTTAdmin,
|
DraggableMPTTAdmin,
|
||||||
ModelAdmin,
|
ModelAdmin,
|
||||||
):
|
):
|
||||||
|
storefront_path_prefix = "catalog"
|
||||||
# noinspection PyClassVar
|
# noinspection PyClassVar
|
||||||
model = Category
|
model = Category
|
||||||
formfield_overrides = {TextField: {"widget": MarkdownWidget}}
|
formfield_overrides = {TextField: {"widget": MarkdownWidget}}
|
||||||
|
|
@ -417,8 +437,13 @@ class CategoryAdmin(
|
||||||
|
|
||||||
@register(Brand)
|
@register(Brand)
|
||||||
class BrandAdmin(
|
class BrandAdmin(
|
||||||
DjangoQLSearchMixin, FieldsetsMixin, ActivationActionsMixin, ModelAdmin
|
StorefrontLinkMixin,
|
||||||
|
DjangoQLSearchMixin,
|
||||||
|
FieldsetsMixin,
|
||||||
|
ActivationActionsMixin,
|
||||||
|
ModelAdmin,
|
||||||
):
|
):
|
||||||
|
storefront_path_prefix = "brand"
|
||||||
# noinspection PyClassVar
|
# noinspection PyClassVar
|
||||||
model = Brand
|
model = Brand
|
||||||
formfield_overrides = {TextField: {"widget": MarkdownWidget}}
|
formfield_overrides = {TextField: {"widget": MarkdownWidget}}
|
||||||
|
|
@ -449,12 +474,14 @@ class BrandAdmin(
|
||||||
|
|
||||||
@register(Product)
|
@register(Product)
|
||||||
class ProductAdmin(
|
class ProductAdmin(
|
||||||
|
StorefrontLinkMixin,
|
||||||
DjangoQLSearchMixin,
|
DjangoQLSearchMixin,
|
||||||
FieldsetsMixin,
|
FieldsetsMixin,
|
||||||
ActivationActionsMixin,
|
ActivationActionsMixin,
|
||||||
ModelAdmin,
|
ModelAdmin,
|
||||||
ImportExportModelAdmin,
|
ImportExportModelAdmin,
|
||||||
):
|
):
|
||||||
|
storefront_path_prefix = "product"
|
||||||
# noinspection PyClassVar
|
# noinspection PyClassVar
|
||||||
model = Product
|
model = Product
|
||||||
formfield_overrides = {TextField: {"widget": MarkdownWidget}}
|
formfield_overrides = {TextField: {"widget": MarkdownWidget}}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends "admin/change_form.html" %}
|
||||||
|
{% load i18n admin_modify %}
|
||||||
|
|
||||||
|
{% block submit_buttons_bottom %}
|
||||||
|
{% submit_row %}
|
||||||
|
|
||||||
|
{% if original and storefront_url %}
|
||||||
|
<div class="flex justify-end px-4 -mt-2 pb-2">
|
||||||
|
<a href="{{ storefront_url }}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
class="font-medium inline-flex group items-center gap-2 px-3 py-2 rounded-default justify-center whitespace-nowrap cursor-pointer border border-base-200 bg-white shadow-xs text-important dark:border-base-700 dark:bg-transparent hover:bg-base-100/80 dark:hover:bg-base-800/80 w-full lg:w-auto">
|
||||||
|
<span class="material-symbols-outlined text-base">open_in_new</span>
|
||||||
|
{% trans "See on site" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
21
engine/core/templates/unfold/helpers/language_switch.html
Normal file
21
engine/core/templates/unfold/helpers/language_switch.html
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
{% get_available_languages as LANGUAGES %}
|
||||||
|
{% get_language_info_list for LANGUAGES as languages %}
|
||||||
|
|
||||||
|
{% if show_languages %}
|
||||||
|
<div class="max-h-[280px] overflow-y-auto">
|
||||||
|
{% if languages_list %}
|
||||||
|
{% for language in languages_list %}
|
||||||
|
{% include "unfold/helpers/language_form.html" with language=language %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% for language in languages %}
|
||||||
|
{% include "unfold/helpers/language_form.html" with language=language %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border-t border-base-200 mt-1 pt-1 dark:border-base-700"></div>
|
||||||
|
{% endif %}
|
||||||
|
|
@ -156,12 +156,12 @@ class UpdateUser(Mutation):
|
||||||
attribute_pairs = kwargs.pop("attributes", "")
|
attribute_pairs = kwargs.pop("attributes", "")
|
||||||
|
|
||||||
if attribute_pairs:
|
if attribute_pairs:
|
||||||
|
if not isinstance(user.attributes, dict):
|
||||||
|
user.attributes = {}
|
||||||
for attribute_pair in attribute_pairs.split(";"):
|
for attribute_pair in attribute_pairs.split(";"):
|
||||||
if "-" in attribute_pair:
|
if "-" in attribute_pair:
|
||||||
attr, value = attribute_pair.split("-", 1)
|
attr, value = attribute_pair.split("-", 1)
|
||||||
if not user.attributes:
|
user.attributes[attr] = value
|
||||||
user.attributes = {}
|
|
||||||
user.attributes.update({attr: value})
|
|
||||||
else:
|
else:
|
||||||
raise BadRequest(
|
raise BadRequest(
|
||||||
_(f"Invalid attribute format: {attribute_pair}")
|
_(f"Invalid attribute format: {attribute_pair}")
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from os import getenv
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
|
@ -31,7 +32,7 @@ UNFOLD: dict[str, Any] = {
|
||||||
"950": "#22282d",
|
"950": "#22282d",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"SITE_URL": STOREFRONT_DOMAIN,
|
"SITE_URL": f"https://{STOREFRONT_DOMAIN}",
|
||||||
"SITE_TITLE": f"{PROJECT_NAME} Dashboard",
|
"SITE_TITLE": f"{PROJECT_NAME} Dashboard",
|
||||||
"SITE_HEADER": PROJECT_NAME,
|
"SITE_HEADER": PROJECT_NAME,
|
||||||
"SITE_LOGO": lambda request: static("favicon.png"),
|
"SITE_LOGO": lambda request: static("favicon.png"),
|
||||||
|
|
@ -120,11 +121,18 @@ UNFOLD: dict[str, Any] = {
|
||||||
"icon": "api",
|
"icon": "api",
|
||||||
"link": reverse_lazy("rapidoc-platform"),
|
"link": reverse_lazy("rapidoc-platform"),
|
||||||
},
|
},
|
||||||
|
*(
|
||||||
|
[
|
||||||
{
|
{
|
||||||
"title": "GraphQL",
|
"title": "GraphQL",
|
||||||
"icon": "graph_5",
|
"icon": "graph_5",
|
||||||
"link": reverse_lazy("graphql-platform"),
|
"link": reverse_lazy("graphql-platform"),
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
if getenv("GRAPHQL_INTROSPECTION", "").lower()
|
||||||
|
in ("1", "true", "yes")
|
||||||
|
else []
|
||||||
|
),
|
||||||
{
|
{
|
||||||
"title": _("Taskboard"),
|
"title": _("Taskboard"),
|
||||||
"icon": "view_kanban",
|
"icon": "view_kanban",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue