From 9dddf71837c96b21fcd62c11e5d3c46cf97a6071 Mon Sep 17 00:00:00 2001 From: Tudor Amariei Date: Mon, 7 Oct 2024 11:00:47 +0300 Subject: [PATCH] Initial admin interface set-up - add environment callback - add some Unfold settings --- backend/donations/admin.py | 36 ++++- backend/donations/models/main.py | 2 +- backend/redirectioneaza/callbacks.py | 8 ++ backend/redirectioneaza/settings.py | 134 +++++++++++++++--- .../v3/admin/announcements/base.html | 24 ++++ .../admin/announcements/work_in_progress.html | 15 ++ backend/templates/v3/admin/base_admin.html | 25 ++++ backend/templates/v3/admin/index.html | 38 +++++ .../v3/redirect/components/card.html | 27 ++++ .../components/dropdown_navigation.html | 19 +++ 10 files changed, 303 insertions(+), 25 deletions(-) create mode 100644 backend/redirectioneaza/callbacks.py create mode 100644 backend/templates/v3/admin/announcements/base.html create mode 100644 backend/templates/v3/admin/announcements/work_in_progress.html create mode 100644 backend/templates/v3/admin/base_admin.html create mode 100644 backend/templates/v3/admin/index.html create mode 100644 backend/templates/v3/redirect/components/card.html create mode 100644 backend/templates/v3/redirect/components/dropdown_navigation.html diff --git a/backend/donations/admin.py b/backend/donations/admin.py index 46c54ed1..f6c896b0 100644 --- a/backend/donations/admin.py +++ b/backend/donations/admin.py @@ -73,8 +73,8 @@ def queryset(self, request, queryset): @admin.register(Ngo) class NgoAdmin(ModelAdmin): - list_display = ("id", "slug", "registration_number", "name") - list_display_links = ("id", "slug", "registration_number", "name") + list_display = ("id", "ngohub_org_id", "slug", "registration_number", "name") + list_display_links = ("id", "ngohub_org_id", "slug", "registration_number", "name") list_filter = ( "date_created", "is_verified", @@ -112,7 +112,14 @@ class NgoAdmin(ModelAdmin): ), ( _("Activity"), - {"fields": ("is_verified", "is_active", "is_accepting_forms", "is_social_service_viable")}, + { + "fields": ( + "is_verified", + "is_active", + "is_accepting_forms", + "is_social_service_viable", + ) + }, ), ( _("Logo"), @@ -120,7 +127,16 @@ class NgoAdmin(ModelAdmin): ), ( _("Contact"), - {"fields": ("address", "county", "active_region", "phone", "website", "email")}, + { + "fields": ( + "address", + "county", + "active_region", + "phone", + "website", + "email", + ) + }, ), ( _("Details"), @@ -217,7 +233,17 @@ class DonorAdmin(ModelAdmin): ), ( _("Identity"), - {"fields": ("first_name", "last_name", "initial", "county", "city", "email", "phone")}, + { + "fields": ( + "first_name", + "last_name", + "initial", + "county", + "city", + "email", + "phone", + ) + }, ), ( _("Info"), diff --git a/backend/donations/models/main.py b/backend/donations/models/main.py index 45af20e4..387f36ef 100644 --- a/backend/donations/models/main.py +++ b/backend/donations/models/main.py @@ -99,7 +99,7 @@ def ngo_id_number_validator(value): if 2 > len(reg_num) or len(reg_num) > 10: raise ValidationError(_("The ID number must be between 2 and 10 digits long")) - if not settings.RUN_FULL_CUI_VALIDATION: + if not settings.ENABLE_FULL_CUI_VALIDATION: return control_key: str = "753217532" diff --git a/backend/redirectioneaza/callbacks.py b/backend/redirectioneaza/callbacks.py new file mode 100644 index 00000000..4129e384 --- /dev/null +++ b/backend/redirectioneaza/callbacks.py @@ -0,0 +1,8 @@ +from django.conf import settings + + +def environment_callback(_): + environment = f"{settings.ENVIRONMENT} | {settings.VERSION_SUFFIX}" + log_level = settings.DJANGO_LOG_LEVEL + + return environment, log_level diff --git a/backend/redirectioneaza/settings.py b/backend/redirectioneaza/settings.py index 708d0008..271efb4c 100644 --- a/backend/redirectioneaza/settings.py +++ b/backend/redirectioneaza/settings.py @@ -23,6 +23,7 @@ from django.templatetags.static import static from django.urls import reverse_lazy from django.utils import timezone +from django.utils.translation import gettext_lazy as _ from localflavor.ro.ro_counties import COUNTIES_CHOICES # Constants for memory sizes @@ -147,7 +148,7 @@ # Google Analytics: GOOGLE_ANALYTICS_ID=(str, ""), # App settings - RUN_FULL_CUI_VALIDATION=(bool, True), + ENABLE_FULL_CUI_VALIDATION=(bool, True), ) environ.Env.read_env(ENV_FILE_PATH) @@ -248,6 +249,7 @@ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", + "django.contrib.humanize", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", @@ -295,7 +297,10 @@ { # New templates for v2 "BACKEND": "django.template.backends.django.DjangoTemplates", - "DIRS": [os.path.abspath(os.path.join(BASE_DIR, "templates", "v2"))], + "DIRS": [ + os.path.abspath(os.path.join(BASE_DIR, "templates", "v2")), + os.path.abspath(os.path.join(BASE_DIR, "templates", "v3")), + ], "APP_DIRS": True, "OPTIONS": { "context_processors": [ @@ -615,10 +620,94 @@ CONTACT_EMAIL_ADDRESS = env.str("CONTACT_EMAIL_ADDRESS") + # Unfold Admin settings -# https://unfoldadmin.com/docs/configuration/settings/ -# Supported icon set: https://fonts.google.com/icons + +SIDEBAR_NAVIGATION = [ + # Supported icon set: https://fonts.google.com/icons + { + "title": _("Donations"), + "items": [ + { + "title": _("NGOs"), + "icon": "foundation", + "link": reverse_lazy("admin:donations_ngo_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + { + "title": _("Donations"), + "icon": "edit_document", + "link": reverse_lazy("admin:donations_donor_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + { + "title": _("Partners"), + "icon": "handshake", + "link": reverse_lazy("admin:partners_partner_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + { + "title": _("Donation exports"), + "icon": "file_copy", + "link": reverse_lazy("admin:donations_job_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + ], + }, + { + "title": _("Users"), + "items": [ + { + "title": _("Users"), + "icon": "person", + "link": reverse_lazy("admin:users_user_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + { + "title": _("Groups"), + "icon": "group", + "link": reverse_lazy("admin:users_groupproxy_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + ], + }, + { + "title": _("Django Q"), + "items": [ + { + "title": _("Failed tasks"), + "icon": "assignment_late", + "link": reverse_lazy("admin:django_q_failure_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + { + "title": _("Queued tasks"), + "icon": "assignment_add", + "link": reverse_lazy("admin:django_q_ormq_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + { + "title": _("Scheduled tasks"), + "icon": "assignment", + "link": reverse_lazy("admin:django_q_schedule_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + { + "title": _("Successful tasks"), + "icon": "assignment_turned_in", + "link": reverse_lazy("admin:django_q_success_changelist"), + "permission": lambda request: request.user.is_superuser, + }, + ], + }, +] + UNFOLD = { + # https://unfoldadmin.com/docs/configuration/settings/ + # Site configuration + "ENVIRONMENT": "redirectioneaza.callbacks.environment_callback", + # Site customization + "SITE_HEADER": f"Admin | {VERSION_LABEL}", "SITE_TITLE": TITLE, "SITE_ICON": lambda request: static("images/logo-smaller.png"), "SITE_LOGO": lambda request: static("images/logo-smaller.png"), @@ -659,10 +748,16 @@ 950: "#745E06", }, }, + # Sidebar settings + "SIDEBAR": { + "show_search": True, + "show_all_applications": False, + "navigation": SIDEBAR_NAVIGATION, + }, } -# Django Q2 -# https://django-q2.readthedocs.io/en/stable/configure.html#configuration + +# Django Q2 — https://django-q2.readthedocs.io/en/stable/configure.html#configuration Q_CLUSTER_WORKERS: int = env.int("DJANGO_Q_WORKERS_COUNT") Q_CLUSTER_RECYCLE: int = env.int("DJANGO_Q_RECYCLE_RATE") @@ -692,7 +787,8 @@ IMPORT_USE_BATCHES = env.bool("IMPORT_USE_BATCHES") IMPORT_BATCH_SIZE = env.int("IMPORT_BATCH_SIZE") -# reCAPTCHA +# reCAPTCHA & Analytics settings +GOOGLE_ANALYTICS_ID = env.str("GOOGLE_ANALYTICS_ID") RECAPTCHA_PUBLIC_KEY = env.str("CAPTCHA_PUBLIC_KEY") RECAPTCHA_PRIVATE_KEY = env.str("CAPTCHA_PRIVATE_KEY") @@ -706,23 +802,29 @@ if DEBUG or not RECAPTCHA_ENABLED: SILENCED_SYSTEM_CHECKS = ["django_recaptcha.recaptcha_test_key_error"] -ENABLE_FORMS_DOWNLOAD = env.bool("ENABLE_FORMS_DOWNLOAD", True) -TIMEDELTA_FORMS_DOWNLOAD_MINUTES = env.int("TIMEDELTA_FORMS_DOWNLOAD_MINUTES") - -# encryption +# Encryption settings ENCRYPT_KEY = env.str("ENCRYPT_KEY", "%INVALID%") if len(ENCRYPT_KEY) != 32 or ENCRYPT_KEY == "%INVALID%": raise Exception("ENCRYPT_KEY must be exactly 32 characters long") FERNET_OBJECT = Fernet(urlsafe_b64encode(ENCRYPT_KEY.encode("utf-8"))) -GOOGLE_ANALYTICS_ID = env.str("GOOGLE_ANALYTICS_ID") - # Feature flags ENABLE_FLAG_CONTACT = env.bool("ENABLE_FLAG_CONTACT", False) +# Configurations for the NGO Hub integration +UPDATE_ORGANIZATION_METHOD = env("UPDATE_ORGANIZATION_METHOD") + +# Other settings +ENABLE_FULL_CUI_VALIDATION = env.bool("ENABLE_FULL_CUI_VALIDATION") + +# Form download settings +ENABLE_FORMS_DOWNLOAD = env.bool("ENABLE_FORMS_DOWNLOAD", True) +TIMEDELTA_FORMS_DOWNLOAD_MINUTES = env.int("TIMEDELTA_FORMS_DOWNLOAD_MINUTES") + +# Login & Auth settings LOGIN_URL = reverse_lazy("login") LOGIN_REDIRECT_URL = reverse_lazy("home") LOGOUT_REDIRECT_URL = reverse_lazy("home") @@ -769,9 +871,3 @@ NGOHUB_ROLE_SUPER_ADMIN = "super-admin" NGOHUB_ROLE_NGO_ADMIN = "admin" NGOHUB_ROLE_NGO_EMPLOYEE = "employee" - -# Configurations for the NGO Hub integration -UPDATE_ORGANIZATION_METHOD = env("UPDATE_ORGANIZATION_METHOD") - -# Other settings -RUN_FULL_CUI_VALIDATION = env.bool("RUN_FULL_CUI_VALIDATION") diff --git a/backend/templates/v3/admin/announcements/base.html b/backend/templates/v3/admin/announcements/base.html new file mode 100644 index 00000000..1d6610df --- /dev/null +++ b/backend/templates/v3/admin/announcements/base.html @@ -0,0 +1,24 @@ +{% load i18n %} + +
+
+ + {% block announcement_title %}{% endblock %} + + +

+ {% block announcement_description %}{% endblock %} +

+
+ + {% block announcement_cta %} + + {% endblock %} +
diff --git a/backend/templates/v3/admin/announcements/work_in_progress.html b/backend/templates/v3/admin/announcements/work_in_progress.html new file mode 100644 index 00000000..50a4085a --- /dev/null +++ b/backend/templates/v3/admin/announcements/work_in_progress.html @@ -0,0 +1,15 @@ +{% extends "admin/announcements/base.html" %} + +{% load i18n %} + +{% block announcement_title %} + {% trans "Work in progress" %} +{% endblock %} + +{% block announcement_description %} + {% trans "The site is still a work in progress. If you have any feedback, please let us know" %} +{% endblock %} + +{% block announcement_cta_url %}mailto:{{ contact_email }}{% endblock %} + +{% block announcement_cta_text %}{% trans "Contact us" %}{% endblock %} diff --git a/backend/templates/v3/admin/base_admin.html b/backend/templates/v3/admin/base_admin.html new file mode 100644 index 00000000..598242cc --- /dev/null +++ b/backend/templates/v3/admin/base_admin.html @@ -0,0 +1,25 @@ +{% extends 'admin/base.html' %} + +{% load cache humanize i18n %} + +{% block breadcrumbs %}{{ block.super }}{% endblock %} + +{% block title %} + {% if subtitle %} + {{ subtitle }} | + {% endif %} + + {{ title }} | {{ site_title|default:_('Django site admin') }} +{% endblock %} + +{% block branding %} +

+ + {{ site_header|default:_('Django administration') }} + +

+{% endblock %} + +{% block content %} + {% include "unfold/helpers/messages.html" %} +{% endblock %} diff --git a/backend/templates/v3/admin/index.html b/backend/templates/v3/admin/index.html new file mode 100644 index 00000000..9c6c5c4f --- /dev/null +++ b/backend/templates/v3/admin/index.html @@ -0,0 +1,38 @@ +{% extends 'admin/base_admin.html' %} + +{% load unfold i18n %} + +{% block content %} + {% component "unfold/components/container.html" %} + + {% component "unfold/components/flex.html" with class="gap-4" %} + {% component "unfold/components/navigation.html" with items=navigation %}{% endcomponent %} + + {% component "redirect/components/dropdown_navigation.html" with class="ml-auto" items=filters title="AAAAA" %}{% endcomponent %} + {% endcomponent %} + + {% component "unfold/components/flex.html" with class="gap-8 mb-8 flex-col lg:flex-row" %} + {% for stat in stats %} + {% component "redirect/components/card.html" with class="lg:w-1/3" label=stat.label footer=stat.footer footer_class=stat.footer_class %} + {% component "unfold/components/title.html" %} + {{ stat.metric }} + {% endcomponent %} + + {% component "unfold/components/text.html" %} + {{ stat.title }} + {% endcomponent %} + {% endcomponent %} + {% endfor %} + {% endcomponent %} + + {% endcomponent %} + + +

+ {{ page_title }} +

+

+ {{ page_description }} +

+ +{% endblock %} diff --git a/backend/templates/v3/redirect/components/card.html b/backend/templates/v3/redirect/components/card.html new file mode 100644 index 00000000..3af63678 --- /dev/null +++ b/backend/templates/v3/redirect/components/card.html @@ -0,0 +1,27 @@ +
+ {% if title %} +

+ {{ title }} +

+ {% endif %} + +
+ {{ children }} + + {% if label %} +
+ {% include "unfold/helpers/label.html" with text=label type="primary" %} +
+ {% endif %} + + {% if icon %} + {{ icon }} + {% endif %} +
+ + {% if footer %} + + {% endif %} +
diff --git a/backend/templates/v3/redirect/components/dropdown_navigation.html b/backend/templates/v3/redirect/components/dropdown_navigation.html new file mode 100644 index 00000000..2555a09d --- /dev/null +++ b/backend/templates/v3/redirect/components/dropdown_navigation.html @@ -0,0 +1,19 @@ +{% if items %} + + +{% endif %}