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:
|
||||
general_fields: list[str] | None = []
|
||||
relation_fields: list[str] | None = []
|
||||
|
|
@ -361,12 +379,14 @@ class AttributeValueAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
|
|||
|
||||
@register(Category)
|
||||
class CategoryAdmin(
|
||||
StorefrontLinkMixin,
|
||||
DjangoQLSearchMixin,
|
||||
FieldsetsMixin,
|
||||
ActivationActionsMixin,
|
||||
DraggableMPTTAdmin,
|
||||
ModelAdmin,
|
||||
):
|
||||
storefront_path_prefix = "catalog"
|
||||
# noinspection PyClassVar
|
||||
model = Category
|
||||
formfield_overrides = {TextField: {"widget": MarkdownWidget}}
|
||||
|
|
@ -417,8 +437,13 @@ class CategoryAdmin(
|
|||
|
||||
@register(Brand)
|
||||
class BrandAdmin(
|
||||
DjangoQLSearchMixin, FieldsetsMixin, ActivationActionsMixin, ModelAdmin
|
||||
StorefrontLinkMixin,
|
||||
DjangoQLSearchMixin,
|
||||
FieldsetsMixin,
|
||||
ActivationActionsMixin,
|
||||
ModelAdmin,
|
||||
):
|
||||
storefront_path_prefix = "brand"
|
||||
# noinspection PyClassVar
|
||||
model = Brand
|
||||
formfield_overrides = {TextField: {"widget": MarkdownWidget}}
|
||||
|
|
@ -449,12 +474,14 @@ class BrandAdmin(
|
|||
|
||||
@register(Product)
|
||||
class ProductAdmin(
|
||||
StorefrontLinkMixin,
|
||||
DjangoQLSearchMixin,
|
||||
FieldsetsMixin,
|
||||
ActivationActionsMixin,
|
||||
ModelAdmin,
|
||||
ImportExportModelAdmin,
|
||||
):
|
||||
storefront_path_prefix = "product"
|
||||
# noinspection PyClassVar
|
||||
model = Product
|
||||
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", "")
|
||||
|
||||
if attribute_pairs:
|
||||
if not isinstance(user.attributes, dict):
|
||||
user.attributes = {}
|
||||
for attribute_pair in attribute_pairs.split(";"):
|
||||
if "-" in attribute_pair:
|
||||
attr, value = attribute_pair.split("-", 1)
|
||||
if not user.attributes:
|
||||
user.attributes = {}
|
||||
user.attributes.update({attr: value})
|
||||
user.attributes[attr] = value
|
||||
else:
|
||||
raise BadRequest(
|
||||
_(f"Invalid attribute format: {attribute_pair}")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from os import getenv
|
||||
from typing import Any
|
||||
|
||||
from django.templatetags.static import static
|
||||
|
|
@ -31,7 +32,7 @@ UNFOLD: dict[str, Any] = {
|
|||
"950": "#22282d",
|
||||
},
|
||||
},
|
||||
"SITE_URL": STOREFRONT_DOMAIN,
|
||||
"SITE_URL": f"https://{STOREFRONT_DOMAIN}",
|
||||
"SITE_TITLE": f"{PROJECT_NAME} Dashboard",
|
||||
"SITE_HEADER": PROJECT_NAME,
|
||||
"SITE_LOGO": lambda request: static("favicon.png"),
|
||||
|
|
@ -120,11 +121,18 @@ UNFOLD: dict[str, Any] = {
|
|||
"icon": "api",
|
||||
"link": reverse_lazy("rapidoc-platform"),
|
||||
},
|
||||
{
|
||||
"title": "GraphQL",
|
||||
"icon": "graph_5",
|
||||
"link": reverse_lazy("graphql-platform"),
|
||||
},
|
||||
*(
|
||||
[
|
||||
{
|
||||
"title": "GraphQL",
|
||||
"icon": "graph_5",
|
||||
"link": reverse_lazy("graphql-platform"),
|
||||
},
|
||||
]
|
||||
if getenv("GRAPHQL_INTROSPECTION", "").lower()
|
||||
in ("1", "true", "yes")
|
||||
else []
|
||||
),
|
||||
{
|
||||
"title": _("Taskboard"),
|
||||
"icon": "view_kanban",
|
||||
|
|
|
|||
Loading…
Reference in a new issue