feat: Make the admin interface less ugh
This commit is contained in:
parent
9c754f55c6
commit
925a4158ac
10 changed files with 2048 additions and 747 deletions
|
@ -1,2 +1,3 @@
|
|||
migrations/
|
||||
.ruff_cache/
|
||||
admin/
|
|
@ -1,5 +1,4 @@
|
|||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.contrib.admin import AdminSite
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
@ -7,6 +6,7 @@ from django.urls import reverse
|
|||
from django.utils.html import format_html
|
||||
from django.contrib.contenttypes.admin import GenericTabularInline
|
||||
from django.utils import timezone
|
||||
from django.contrib.admin.models import LogEntry
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
|
@ -47,6 +47,10 @@ class DuckpondAdminSite(AdminSite):
|
|||
site_title = "Duckpond Admin"
|
||||
index_title = "Dashboard"
|
||||
|
||||
index_template = "admin/index.html"
|
||||
login_template = "admin/login.html"
|
||||
app_index_template = "admin/app_index.html"
|
||||
|
||||
def index(self, request, extra_context=None):
|
||||
# Get counts for dashboard stats
|
||||
from duckpond.core.models import User, Membership
|
||||
|
@ -55,6 +59,9 @@ class DuckpondAdminSite(AdminSite):
|
|||
# Calculate date for "recent" items
|
||||
recent_date = timezone.now() - timedelta(days=30)
|
||||
|
||||
# Get recent admin actions
|
||||
recent_actions = LogEntry.objects.select_related("content_type", "user")[:10]
|
||||
|
||||
# Prepare stats
|
||||
stats = {
|
||||
"user_count": User.objects.count(),
|
||||
|
@ -63,6 +70,7 @@ class DuckpondAdminSite(AdminSite):
|
|||
"recent_payments": Payment.objects.filter(
|
||||
created_at__gte=recent_date
|
||||
).count(),
|
||||
"recent_actions": recent_actions,
|
||||
}
|
||||
|
||||
context = extra_context or {}
|
||||
|
@ -114,7 +122,16 @@ class UserAdmin(BaseUserAdmin):
|
|||
(None, {"fields": ("username", "password")}),
|
||||
(
|
||||
_("Personal info"),
|
||||
{"fields": ("preferred_name", "first_name", "last_name", "company_name", "email", "address")},
|
||||
{
|
||||
"fields": (
|
||||
"preferred_name",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"company_name",
|
||||
"email",
|
||||
"address",
|
||||
)
|
||||
},
|
||||
),
|
||||
(
|
||||
_("Permissions"),
|
||||
|
@ -131,7 +148,14 @@ class UserAdmin(BaseUserAdmin):
|
|||
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
|
||||
(
|
||||
_("Membership"),
|
||||
{"fields": ("is_approved", "email_verified", "gnucash_customer_id", "payment_reference")},
|
||||
{
|
||||
"fields": (
|
||||
"is_approved",
|
||||
"email_verified",
|
||||
"gnucash_customer_id",
|
||||
"payment_reference",
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
list_display = (
|
||||
|
|
|
@ -1,589 +0,0 @@
|
|||
/* static/core/css/admin_override.css */
|
||||
|
||||
:root {
|
||||
/* Admin Color Palette (Derived from your main style.css) */
|
||||
--admin-primary: #4361ee; /* Main accent color */
|
||||
--admin-primary-dark: #3f37c9; /* Darker accent for hover */
|
||||
--admin-primary-light: #a1aff7; /* Lighter accent */
|
||||
--admin-primary-fg: #ffffff; /* Text color on primary */
|
||||
|
||||
--admin-secondary: #4cc9f0; /* Secondary accent (less used) */
|
||||
|
||||
--admin-body-bg: #f8f9fa; /* Main background */
|
||||
--admin-content-bg: #ffffff; /* Background for content areas */
|
||||
--admin-body-fg: #212529; /* Main text color */
|
||||
--admin-body-quiet-color: #6c757d; /* Lighter text */
|
||||
--admin-body-loud-color: #000000; /* Used rarely */
|
||||
|
||||
--admin-header-bg: var(--admin-primary);
|
||||
--admin-header-fg: var(--admin-primary-fg);
|
||||
|
||||
--admin-module-header-bg: #e9ecef; /* Lighter header for modules */
|
||||
--admin-module-header-fg: var(--admin-body-fg);
|
||||
--admin-module-border-color: #dee2e6;
|
||||
|
||||
--admin-link-fg: var(--admin-primary);
|
||||
--admin-link-hover-color: var(--admin-primary-dark);
|
||||
|
||||
--admin-button-bg: var(--admin-primary);
|
||||
--admin-button-fg: var(--admin-primary-fg);
|
||||
--admin-button-hover-bg: var(--admin-primary-dark);
|
||||
|
||||
--admin-delete-button-bg: #dc3545; /* Danger color */
|
||||
--admin-delete-button-hover-bg: #c82333;
|
||||
|
||||
--admin-message-success-bg: #d1e7dd;
|
||||
--admin-message-success-fg: #0f5132;
|
||||
--admin-message-warning-bg: #fff3cd;
|
||||
--admin-message-warning-fg: #664d03;
|
||||
--admin-message-error-bg: #f8d7da;
|
||||
--admin-message-error-fg: #58151c;
|
||||
|
||||
--admin-border-radius: 0.375rem; /* Consistent border radius */
|
||||
--admin-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
--admin-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--admin-body-bg);
|
||||
color: var(--admin-body-fg);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
font-size: 0.95rem; /* Slightly smaller base font for admin */
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--admin-link-fg);
|
||||
}
|
||||
a:hover {
|
||||
color: var(--admin-link-hover-color);
|
||||
}
|
||||
|
||||
/* --- Header --- */
|
||||
#header {
|
||||
background-color: var(--admin-header-bg);
|
||||
color: var(--admin-header-fg);
|
||||
box-shadow: var(--admin-box-shadow-sm);
|
||||
height: auto;
|
||||
padding: 0.75rem 1.5rem;
|
||||
}
|
||||
#branding h1, #branding h1 a {
|
||||
color: var(--admin-header-fg);
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
#branding img {
|
||||
vertical-align: -5px; /* Better alignment for the logo */
|
||||
}
|
||||
#user-tools {
|
||||
color: var(--admin-header-fg);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
#user-tools a {
|
||||
color: var(--admin-header-fg);
|
||||
border-bottom: none;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: var(--admin-border-radius);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
#user-tools a:hover {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
color: var(--admin-header-fg);
|
||||
}
|
||||
|
||||
/* --- Breadcrumbs --- */
|
||||
div.breadcrumbs {
|
||||
background: var(--admin-content-bg);
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-bottom: 1px solid var(--admin-module-border-color);
|
||||
box-shadow: var(--admin-box-shadow-sm);
|
||||
color: var(--admin-body-quiet-color);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
div.breadcrumbs a {
|
||||
color: var(--admin-link-fg);
|
||||
}
|
||||
div.breadcrumbs a:hover {
|
||||
color: var(--admin-link-hover-color);
|
||||
}
|
||||
|
||||
/* --- Main Content Area --- */
|
||||
#content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* --- Modules --- */
|
||||
.module {
|
||||
border: none;
|
||||
background: var(--admin-content-bg);
|
||||
border-radius: var(--admin-border-radius);
|
||||
box-shadow: var(--admin-box-shadow-sm);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.module caption, .module h2, .inline-group h2 {
|
||||
background: var(--admin-module-header-bg);
|
||||
color: var(--admin-module-header-fg);
|
||||
border-bottom: 1px solid var(--admin-module-border-color);
|
||||
padding: 0.75rem 1.25rem;
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.module table {
|
||||
border-collapse: collapse; /* Ensure borders look clean */
|
||||
}
|
||||
.module td, .module th {
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-bottom: 1px solid var(--admin-module-border-color);
|
||||
font-size: 0.9rem;
|
||||
vertical-align: middle; /* Ensure vertical alignment is consistent */
|
||||
}
|
||||
.module tr:last-child td, .module tr:last-child th {
|
||||
border-bottom: none; /* Remove border from last row */
|
||||
}
|
||||
.module th {
|
||||
font-weight: 600;
|
||||
color: var(--admin-body-fg);
|
||||
background-color: #f8f9fa; /* Slightly different bg for table headers */
|
||||
}
|
||||
.module table thead th {
|
||||
text-align: left;
|
||||
border-bottom-width: 2px; /* Thicker border below table main headers */
|
||||
border-bottom-color: var(--admin-module-border-color);
|
||||
}
|
||||
|
||||
/* Alternating Row Colors (Applied to both td and th in tbody) */
|
||||
.module table tbody tr:nth-child(even) td,
|
||||
.module table tbody tr:nth-child(even) th {
|
||||
background-color: var(--admin-body-bg);
|
||||
}
|
||||
|
||||
/* --- Forms --- */
|
||||
.form-row {
|
||||
padding: 1rem 1.25rem;
|
||||
border-bottom: 1px solid var(--admin-module-border-color);
|
||||
margin-bottom: 0; /* Remove default margin */
|
||||
}
|
||||
.form-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.aligned label {
|
||||
font-weight: 500;
|
||||
padding-top: 0.6em; /* Align label better with input */
|
||||
color: var(--admin-body-quiet-color);
|
||||
width: 12em; /* Adjust label width as needed */
|
||||
}
|
||||
form .aligned p.help, form .aligned div.help {
|
||||
font-size: 0.85rem;
|
||||
color: var(--admin-body-quiet-color);
|
||||
margin-left: 13em; /* Match label width + spacing */
|
||||
}
|
||||
input[type="text"], input[type="password"], input[type="email"], input[type="url"],
|
||||
input[type="number"], input[type="date"], input[type="datetime-local"],
|
||||
input[type="month"], input[type="week"], input[type="time"],
|
||||
input[type="search"], input[type="tel"], textarea, select {
|
||||
border: 1px solid var(--admin-module-border-color);
|
||||
border-radius: var(--admin-border-radius);
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.9rem;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
input:focus, textarea:focus, select:focus {
|
||||
border-color: var(--admin-primary-light);
|
||||
box-shadow: 0 0 0 0.2rem rgba(67, 97, 238, 0.25);
|
||||
outline: none;
|
||||
}
|
||||
.selector-available h2, .selector-chosen h2 {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
background: var(--admin-module-header-bg);
|
||||
border: 1px solid var(--admin-module-border-color);
|
||||
border-bottom: none;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
/* --- Buttons --- */
|
||||
.button, input[type=submit], input[type=button], .submit-row input, a.button {
|
||||
background: var(--admin-button-bg);
|
||||
color: var(--admin-button-fg);
|
||||
border: none;
|
||||
border-radius: var(--admin-border-radius);
|
||||
padding: 0.6rem 1.2rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
text-decoration: none;
|
||||
line-height: normal; /* Reset line-height */
|
||||
}
|
||||
.button:hover, input[type=submit]:hover, input[type=button]:hover, .submit-row input:hover, a.button:hover {
|
||||
background: var(--admin-button-hover-bg);
|
||||
color: var(--admin-button-fg);
|
||||
}
|
||||
.button.default, input[type=submit].default, .submit-row input.default {
|
||||
background: var(--admin-primary); /* Ensure default uses primary */
|
||||
}
|
||||
.button.default:hover, input[type=submit].default:hover, .submit-row input.default:hover {
|
||||
background: var(--admin-primary-dark);
|
||||
}
|
||||
.button.cancel-link, a.button.cancel-link {
|
||||
background: var(--admin-body-quiet-color);
|
||||
}
|
||||
.button.cancel-link:hover, a.button.cancel-link:hover {
|
||||
background: var(--admin-body-fg);
|
||||
}
|
||||
a.deletelink {
|
||||
background: var(--admin-delete-button-bg);
|
||||
color: var(--admin-button-fg);
|
||||
}
|
||||
a.deletelink:hover {
|
||||
background: var(--admin-delete-button-hover-bg);
|
||||
}
|
||||
.submit-row {
|
||||
background: var(--admin-content-bg);
|
||||
border-top: 1px solid var(--admin-module-border-color);
|
||||
padding: 1rem 1.25rem;
|
||||
overflow: visible;
|
||||
border-radius: 0 0 var(--admin-border-radius) var(--admin-border-radius);
|
||||
}
|
||||
.submit-row p.deletelink-box {
|
||||
float: left; /* Align delete button left */
|
||||
}
|
||||
.submit-row input {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* --- Object Tools --- */
|
||||
.object-tools {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.object-tools a {
|
||||
background: var(--admin-button-bg);
|
||||
color: var(--admin-button-fg);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: var(--admin-border-radius);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-left: 0.5rem;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.object-tools a:hover {
|
||||
background: var(--admin-button-hover-bg);
|
||||
color: var(--admin-button-fg);
|
||||
}
|
||||
.object-tools a.addlink {
|
||||
background-color: #28a745; /* Green for add */
|
||||
}
|
||||
.object-tools a.addlink:hover {
|
||||
background-color: #218838;
|
||||
}
|
||||
.object-tools a.historylink {
|
||||
background-color: #ffc107; /* Yellow for history */
|
||||
color: var(--admin-body-fg);
|
||||
}
|
||||
.object-tools a.historylink:hover {
|
||||
background-color: #e0a800;
|
||||
}
|
||||
|
||||
/* --- Changelist --- */
|
||||
#changelist-filter {
|
||||
background: var(--admin-content-bg);
|
||||
border-radius: var(--admin-border-radius);
|
||||
box-shadow: var(--admin-box-shadow-sm);
|
||||
padding: 1rem 1.25rem;
|
||||
border: none;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
#changelist-filter h2 {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: var(--admin-body-fg);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
#changelist-filter h3 {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
color: var(--admin-body-quiet-color);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
#changelist-filter ul {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
#changelist-filter li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
#changelist-filter li.selected a {
|
||||
font-weight: bold;
|
||||
color: var(--admin-link-hover-color);
|
||||
}
|
||||
#changelist table thead th {
|
||||
background-color: var(--admin-module-header-bg); /* Header background for changelist */
|
||||
}
|
||||
#changelist .actions {
|
||||
background: var(--admin-module-header-bg);
|
||||
border-top: 1px solid var(--admin-module-border-color);
|
||||
border-bottom: 1px solid var(--admin-module-border-color);
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: 0; /* Remove radius from actions bar */
|
||||
}
|
||||
#changelist .paginator {
|
||||
border-top: 1px solid var(--admin-module-border-color);
|
||||
padding: 0.75rem 1.25rem;
|
||||
background: var(--admin-content-bg);
|
||||
border-radius: 0 0 var(--admin-border-radius) var(--admin-border-radius);
|
||||
}
|
||||
#changelist table tbody tr:hover {
|
||||
background-color: #f0f3ff; /* Subtle hover effect */
|
||||
}
|
||||
#changelist table tbody th a {
|
||||
font-weight: 500; /* Make main link slightly bolder */
|
||||
}
|
||||
|
||||
/* --- Messages --- */
|
||||
ul.messagelist li {
|
||||
background-color: var(--admin-message-success-bg);
|
||||
color: var(--admin-message-success-fg);
|
||||
border: 1px solid rgba(0,0,0,0.1);
|
||||
padding: 0.75rem 1.25rem;
|
||||
border-radius: var(--admin-border-radius);
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
ul.messagelist li.warning {
|
||||
background-color: var(--admin-message-warning-bg);
|
||||
color: var(--admin-message-warning-fg);
|
||||
}
|
||||
ul.messagelist li.error {
|
||||
background-color: var(--admin-message-error-bg);
|
||||
color: var(--admin-message-error-fg);
|
||||
}
|
||||
|
||||
/* --- Related Widget Wrapper --- */
|
||||
.related-widget-wrapper {
|
||||
border: 1px solid var(--admin-module-border-color);
|
||||
border-radius: var(--admin-border-radius);
|
||||
}
|
||||
.related-widget-wrapper > select {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* --- Dashboard / Index --- */
|
||||
.dashboard #content {
|
||||
width: auto; /* Let content flow */
|
||||
}
|
||||
.dashboard .module table caption a {
|
||||
color: var(--admin-module-header-fg); /* Match caption text color */
|
||||
}
|
||||
.dashboard .module table td a {
|
||||
background: none;
|
||||
color: var(--admin-link-fg);
|
||||
display: inline;
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.dashboard .module table td a:hover {
|
||||
background: none;
|
||||
color: var(--admin-link-hover-color);
|
||||
}
|
||||
.dashboard .addlink, .dashboard .changelink, .dashboard .viewlink {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.dashboard .module table th a {
|
||||
font-weight: bold;
|
||||
}
|
||||
.dashboard .module table td, .dashboard .module table th {
|
||||
white-space: normal; /* Allow wrapping in dashboard tables */
|
||||
}
|
||||
|
||||
/* Custom Dashboard Widgets from index.html */
|
||||
.dashboard-widget {
|
||||
background: var(--admin-content-bg);
|
||||
border-radius: var(--admin-border-radius);
|
||||
box-shadow: var(--admin-box-shadow-sm);
|
||||
margin-bottom: 1.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.dashboard-widget-title {
|
||||
background: var(--admin-module-header-bg);
|
||||
color: var(--admin-module-header-fg);
|
||||
border-bottom: 1px solid var(--admin-module-border-color);
|
||||
padding: 0.75rem 1.25rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.dashboard-widget-content {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
.dashboard-widget .module { /* Reset styling for modules inside widgets */
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
background: none;
|
||||
}
|
||||
.dashboard-widget .module table {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.dashboard-widget .module table caption {
|
||||
background: none;
|
||||
color: var(--admin-body-fg);
|
||||
padding: 0 0 0.5rem 0;
|
||||
border-bottom: none;
|
||||
text-align: left;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.dashboard-widget ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.dashboard-widget ul li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* Dashboard Stats */
|
||||
.dashboard-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.stat-box {
|
||||
background: var(--admin-content-bg);
|
||||
border-radius: var(--admin-border-radius);
|
||||
box-shadow: var(--admin-box-shadow-sm);
|
||||
padding: 1.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
.stat-number {
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
color: var(--admin-primary);
|
||||
margin-bottom: 0.25rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.stat-label {
|
||||
color: var(--admin-body-quiet-color);
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* --- Sidebar --- */
|
||||
#content-related {
|
||||
background: var(--admin-body-bg); /* Match main bg */
|
||||
border: none;
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
#content-related .module {
|
||||
background: var(--admin-content-bg);
|
||||
border: none; /* Remove border */
|
||||
}
|
||||
#content-related h2 {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
padding: 0.75rem 1.25rem;
|
||||
background: var(--admin-module-header-bg);
|
||||
color: var(--admin-module-header-fg);
|
||||
border-bottom: 1px solid var(--admin-module-border-color);
|
||||
}
|
||||
#content-related .actionlist {
|
||||
padding: 0.75rem 1.25rem;
|
||||
}
|
||||
#content-related .actionlist li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* --- Responsive Adjustments --- */
|
||||
@media (max-width: 1024px) {
|
||||
#header {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
#content {
|
||||
padding: 1rem;
|
||||
}
|
||||
.aligned label {
|
||||
width: 10em;
|
||||
}
|
||||
form .aligned p.help, form .aligned div.help {
|
||||
margin-left: 11em;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
#header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
height: auto;
|
||||
}
|
||||
#branding {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
#user-tools {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
padding-top: 0.5rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.object-tools {
|
||||
position: static;
|
||||
margin: 1rem calc(-1 * var(--content-padding)) 0; /* Adjust based on #content padding */
|
||||
padding: 0.75rem var(--content-padding);
|
||||
background: var(--admin-module-header-bg);
|
||||
border-top: 1px solid var(--admin-module-border-color);
|
||||
border-bottom: 1px solid var(--admin-module-border-color);
|
||||
}
|
||||
.object-tools a {
|
||||
margin-left: 0;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
#content-related {
|
||||
margin-left: 0;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.colM { /* Make main column full width on mobile */
|
||||
width: 100%;
|
||||
}
|
||||
.colSM { /* Make sidebar full width on mobile */
|
||||
width: 100%;
|
||||
}
|
||||
.aligned label {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
float: none;
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
.aligned .form-row div {
|
||||
float: none;
|
||||
width: 100%;
|
||||
padding-left: 0;
|
||||
}
|
||||
form .aligned p.help, form .aligned div.help {
|
||||
margin-left: 0;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.submit-row {
|
||||
padding: 1rem;
|
||||
}
|
||||
.submit-row input, .submit-row p.deletelink-box a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
.submit-row p.deletelink-box {
|
||||
float: none;
|
||||
text-align: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
1441
duckpond/core/static/admin/css/duckpond-admin.css
Normal file
1441
duckpond/core/static/admin/css/duckpond-admin.css
Normal file
File diff suppressed because it is too large
Load diff
108
duckpond/core/static/admin/js/duckpond-admin.js
Normal file
108
duckpond/core/static/admin/js/duckpond-admin.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// User menu toggle
|
||||
const userInfo = document.querySelector('.user-info');
|
||||
const userMenu = document.querySelector('.user-menu');
|
||||
|
||||
if (userInfo && userMenu) {
|
||||
userInfo.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
userMenu.classList.toggle('show');
|
||||
});
|
||||
|
||||
// Close menu when clicking outside
|
||||
document.addEventListener('click', function (e) {
|
||||
if (!userInfo.contains(e.target) && !userMenu.contains(e.target)) {
|
||||
userMenu.classList.remove('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add icons to sidebar menu items based on model
|
||||
const modelIcons = {
|
||||
'user': 'fas fa-user',
|
||||
'membership': 'fas fa-id-card',
|
||||
'membershiptier': 'fas fa-layer-group',
|
||||
'paymentcycle': 'fas fa-calendar-alt',
|
||||
'customfield': 'fas fa-list-alt',
|
||||
'payment': 'fas fa-money-bill-wave',
|
||||
'paymentmethod': 'fas fa-credit-card',
|
||||
'content': 'fas fa-file-alt',
|
||||
'category': 'fas fa-folder',
|
||||
'contenttype': 'fas fa-file-code',
|
||||
'eventregistration': 'fas fa-calendar-check',
|
||||
'certificate': 'fas fa-certificate',
|
||||
'download': 'fas fa-download',
|
||||
'page': 'fas fa-file',
|
||||
'section': 'fas fa-puzzle-piece',
|
||||
'form': 'fas fa-wpforms',
|
||||
'formsubmission': 'fas fa-paper-plane',
|
||||
'announcement': 'fas fa-bullhorn'
|
||||
};
|
||||
|
||||
// Add default icon for models without specific icon
|
||||
const defaultIcon = 'fas fa-cube';
|
||||
|
||||
// Find all model links in the sidebar
|
||||
const modelLinks = document.querySelectorAll('.model-link');
|
||||
modelLinks.forEach(link => {
|
||||
const modelName = link.parentElement.className.split('model-')[1];
|
||||
if (modelName) {
|
||||
const icon = document.createElement('i');
|
||||
icon.className = modelIcons[modelName] || defaultIcon;
|
||||
icon.style.marginRight = '8px';
|
||||
link.insertBefore(icon, link.firstChild);
|
||||
}
|
||||
});
|
||||
|
||||
// Enhance form widgets
|
||||
enhanceFormWidgets();
|
||||
});
|
||||
|
||||
function enhanceFormWidgets() {
|
||||
// Add datepicker to date inputs
|
||||
const dateInputs = document.querySelectorAll('input[type="date"]');
|
||||
dateInputs.forEach(input => {
|
||||
// Add calendar icon
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'date-input-wrapper';
|
||||
wrapper.style.position = 'relative';
|
||||
|
||||
const icon = document.createElement('i');
|
||||
icon.className = 'fas fa-calendar-alt';
|
||||
icon.style.position = 'absolute';
|
||||
icon.style.right = '10px';
|
||||
icon.style.top = '50%';
|
||||
icon.style.transform = 'translateY(-50%)';
|
||||
icon.style.color = '#6c757d';
|
||||
icon.style.pointerEvents = 'none';
|
||||
|
||||
input.parentNode.insertBefore(wrapper, input);
|
||||
wrapper.appendChild(input);
|
||||
wrapper.appendChild(icon);
|
||||
});
|
||||
|
||||
// Add select2-like styling to select elements
|
||||
const selectElements = document.querySelectorAll('select:not([multiple])');
|
||||
selectElements.forEach(select => {
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'select-wrapper';
|
||||
wrapper.style.position = 'relative';
|
||||
|
||||
const icon = document.createElement('i');
|
||||
icon.className = 'fas fa-chevron-down';
|
||||
icon.style.position = 'absolute';
|
||||
icon.style.right = '10px';
|
||||
icon.style.top = '50%';
|
||||
icon.style.transform = 'translateY(-50%)';
|
||||
icon.style.color = '#6c757d';
|
||||
icon.style.pointerEvents = 'none';
|
||||
|
||||
select.parentNode.insertBefore(wrapper, select);
|
||||
wrapper.appendChild(select);
|
||||
wrapper.appendChild(icon);
|
||||
|
||||
// Style the select element
|
||||
select.style.appearance = 'none';
|
||||
select.style.paddingRight = '30px';
|
||||
});
|
||||
}
|
47
duckpond/core/templates/admin/app_list.html
Normal file
47
duckpond/core/templates/admin/app_list.html
Normal file
|
@ -0,0 +1,47 @@
|
|||
{% load i18n %}
|
||||
{% if app_list %}
|
||||
{% for app in app_list %}
|
||||
<div class="app-{{ app.app_label }} module{% if app.app_url in request.path %} current-app{% endif %}">
|
||||
<table>
|
||||
<caption>
|
||||
<a href="{{ app.app_url }}"
|
||||
class="section"
|
||||
title="{% blocktranslate with name=app.name %}Models in the {{ name }} application{% endblocktranslate %}">
|
||||
<i class="fas fa-folder"></i> {{ app.name }}
|
||||
</a>
|
||||
</caption>
|
||||
{% for model in app.models %}
|
||||
<tr class="model-{{ model.object_name|lower }}{% if model.admin_url in request.path %} current-model{% endif %}">
|
||||
{% if model.admin_url %}
|
||||
<th scope="row">
|
||||
<a href="{{ model.admin_url }}"
|
||||
{% if model.admin_url in request.path %}aria-current="page"{% endif %}>
|
||||
<i class="fas fa-table"></i> {{ model.name }}</a>
|
||||
</th>
|
||||
{% else %}
|
||||
<th scope="row">{{ model.name }}</th>
|
||||
{% endif %}
|
||||
{% if model.add_url %}
|
||||
<td>
|
||||
<a href="{{ model.add_url }}" class="addlink">
|
||||
<i class="fas fa-plus"></i> {% translate 'Add' %}</a>
|
||||
</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
{% if model.admin_url and show_changelinks %}
|
||||
<td>
|
||||
<a href="{{ model.admin_url }}" class="changelink">
|
||||
<i class="fas fa-list"></i> {% translate 'Change' %}</a>
|
||||
</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>{% translate 'You don\'t have permission to view or edit anything.' %}</p>
|
||||
{% endif %}
|
200
duckpond/core/templates/admin/base.html
Normal file
200
duckpond/core/templates/admin/base.html
Normal file
|
@ -0,0 +1,200 @@
|
|||
{% load i18n static %}
|
||||
<!DOCTYPE html>
|
||||
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
|
||||
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}"
|
||||
dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr,auto' }}">
|
||||
<head>
|
||||
<title>
|
||||
{% block title %}{% endblock %}
|
||||
</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet"
|
||||
href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}">
|
||||
<link rel="stylesheet"
|
||||
href="https://nocdnbs.private.coffee/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="{% static 'admin/css/duckpond-admin.css' %}">
|
||||
{% if not is_popup and is_nav_sidebar_enabled %}
|
||||
<link rel="stylesheet" href="{% static "admin/css/nav_sidebar.css" %}">
|
||||
<script src="{% static 'admin/js/nav_sidebar.js' %}" defer></script>
|
||||
{% endif %}
|
||||
{% block extrastyle %}{% endblock %}
|
||||
{% if LANGUAGE_BIDI %}
|
||||
<link rel="stylesheet"
|
||||
href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}">
|
||||
{% endif %}
|
||||
{% block extrahead %}{% endblock %}
|
||||
{% block responsive %}
|
||||
<meta name="viewport"
|
||||
content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
||||
<link rel="stylesheet" href="{% static "admin/css/responsive.css" %}">
|
||||
{% if LANGUAGE_BIDI %}
|
||||
<link rel="stylesheet" href="{% static "admin/css/responsive_rtl.css" %}">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE">{% endblock %}
|
||||
<style type="text/css">
|
||||
/* Force full width layout */
|
||||
html, body {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#container {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#main {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#content {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#content-main {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Fix for app list */
|
||||
#app-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.app-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Fix for colMS layout */
|
||||
.colMS {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.colMS #content-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.colMS #content-related {
|
||||
width: 260px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.colMS {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.colMS #content-main {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.colMS #content-related {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}"
|
||||
data-admin-utc-offset="{% now "Z" %}">
|
||||
<!-- Container -->
|
||||
<div id="container">
|
||||
{% if not is_popup %}
|
||||
<!-- Header -->
|
||||
<div id="header">
|
||||
<div id="branding">
|
||||
{% block branding %}{% endblock %}
|
||||
</div>
|
||||
{% block usertools %}
|
||||
{% if has_permission %}
|
||||
<div id="user-tools">
|
||||
{% block welcome-msg %}
|
||||
{% translate 'Welcome,' %}
|
||||
<strong>{% firstof user.get_short_name user.get_username %}</strong>.
|
||||
{% endblock %}
|
||||
{% block userlinks %}
|
||||
{% if site_url %}
|
||||
<a href="{{ site_url }}">{% translate 'View site' %}</a> /
|
||||
{% endif %}
|
||||
{% if user.is_active and user.is_staff %}
|
||||
{% url 'django-admindocs-docroot' as docsroot %}
|
||||
{% if docsroot %}
|
||||
<a href="{{ docsroot }}">{% translate 'Documentation' %}</a> /
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if user.has_usable_password %}
|
||||
<a href="{% url 'admin:password_change' %}">{% translate 'Change password' %}</a> /
|
||||
{% endif %}
|
||||
<form id="logout-form" method="post" action="{% url 'admin:logout' %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="logout-button">{% translate 'Log out' %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block nav-global %}{% endblock %}
|
||||
</div>
|
||||
<!-- END Header -->
|
||||
{% block breadcrumbs %}
|
||||
<div class="breadcrumbs">
|
||||
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
|
||||
{% if title %}› {{ title }}{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
<div id="main" class="{% if not is_popup %}full-width{% endif %}">
|
||||
{% if not is_popup and is_nav_sidebar_enabled %}
|
||||
{% block nav-sidebar %}
|
||||
{% include "admin/nav_sidebar.html" %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
<div id="content-wrapper"
|
||||
class="{% if not is_popup and is_nav_sidebar_enabled %}shifted{% endif %}">
|
||||
{% block messages %}
|
||||
{% if messages %}
|
||||
<ul class="messagelist">
|
||||
{% for message in messages %}
|
||||
<li {% if message.tags %}class="{{ message.tags }}"{% endif %}>{{ message|capfirst }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock messages %}
|
||||
<!-- Content -->
|
||||
<div id="content" class="{% block coltype %}colM{% endblock %}">
|
||||
{% block pretitle %}{% endblock %}
|
||||
{% block content_title %}
|
||||
{% if title %}<h1>{{ title }}</h1>{% endif %}
|
||||
{% endblock %}
|
||||
{% block content_subtitle %}
|
||||
{% if subtitle %}<h2>{{ subtitle }}</h2>{% endif %}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% block object-tools %}{% endblock %}
|
||||
{{ content }}
|
||||
{% endblock %}
|
||||
{% block sidebar %}{% endblock %}
|
||||
<br class="clear">
|
||||
</div>
|
||||
<!-- END Content -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END Container -->
|
||||
<script src="{% static 'admin/js/duckpond-admin.js' %}"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,21 +1,46 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{{ title }} | {{ site_title|default:_('Duckpond Admin') }}{% endblock %}
|
||||
|
||||
{% block branding %}
|
||||
<h1 id="site-name">
|
||||
<a href="{% url 'admin:index' %}">
|
||||
<img src="{% static 'core/img/logo_admin.png' %}" alt="{{ site_header|default:_('Duckpond Administration') }}" height="30" style="margin-right: 10px; vertical-align: middle;">
|
||||
</a>
|
||||
</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block nav-global %}{% endblock %}
|
||||
|
||||
{% extends "admin/base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% block title %}{{ title }} | {{ site_title|default:_("Duckpond Admin") }}{% endblock %}
|
||||
{% block extrastyle %}
|
||||
{{ block.super }} {# Loads default admin CSS #}
|
||||
<link rel="preconnect" href="https://googledonts.private.coffee" crossorigin>
|
||||
<link href="https://googledonts.private.coffee/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'admin/css/admin_override.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'admin/css/duckpond-admin.css' %}">
|
||||
<link rel="stylesheet"
|
||||
href="https://nocdnbs.private.coffee/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
{% endblock %}
|
||||
{% block branding %}
|
||||
<h1 id="site-name">
|
||||
<a href="{% url 'admin:index' %}">
|
||||
<span class="brand-icon"><i class="fas fa-water"></i></span>
|
||||
<span class="brand-text">{{ site_header|default:_("Duckpond Administration") }}</span>
|
||||
</a>
|
||||
</h1>
|
||||
{% endblock %}
|
||||
{% block nav-global %}{% endblock %}
|
||||
{% block usertools %}
|
||||
<div id="user-tools" class="user-tools-container">
|
||||
{% if user.is_active and user.is_staff %}
|
||||
<div class="user-info">
|
||||
<span class="user-avatar">
|
||||
<i class="fas fa-user-circle"></i>
|
||||
</span>
|
||||
<span class="user-name">
|
||||
{% firstof user.get_short_name user.get_username %}
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="user-menu">
|
||||
{% if user.has_usable_password %}
|
||||
<a href="{% url 'admin:password_change' %}">
|
||||
<i class="fas fa-key"></i> {% translate 'Change password' %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{% url 'admin:logout' %}">
|
||||
<i class="fas fa-sign-out-alt"></i> {% translate 'Log out' %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extrajs %}
|
||||
<script src="{% static 'admin/js/duckpond-admin.js' %}"></script>
|
||||
{% endblock %}
|
|
@ -1,34 +0,0 @@
|
|||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n admin_urls static admin_modify %}
|
||||
{% block extrahead %}
|
||||
{{ block.super }}
|
||||
<script src="{% url 'admin:jsi18n' %}"></script>
|
||||
{{ form.media }}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
<form method="post" id="add_section_form">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
<fieldset class="module aligned">
|
||||
{% for field in form %}
|
||||
<div class="form-row">
|
||||
<div class="field-box">
|
||||
{{ field.errors }}
|
||||
{{ field.label_tag }}
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class="help">{{ field.help_text|safe }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<div class="submit-row">
|
||||
<input type="submit"
|
||||
value="{% trans 'Add Section' %}"
|
||||
class="default"
|
||||
name="_save">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,116 +1,194 @@
|
|||
{% extends "admin/index.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% extends "admin/base_site.html" %}
|
||||
{% load i18n static %}
|
||||
{% block extrastyle %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet"
|
||||
type="text/css"
|
||||
href="{% static "admin/css/dashboard.css" %}">
|
||||
{% endblock %}
|
||||
{% block coltype %}colMS{% endblock %}
|
||||
{% block bodyclass %}{{ block.super }} dashboard{% endblock %}
|
||||
{% block breadcrumbs %}{% endblock %}
|
||||
{% block nav-sidebar %}{% endblock %}
|
||||
{% block content %}
|
||||
<div id="content-main">
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-box">
|
||||
<div class="stat-number">{{ user_count }}</div>
|
||||
<div class="stat-label">Users</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-number">{{ active_memberships }}</div>
|
||||
<div class="stat-label">Active Memberships</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-number">{{ pending_memberships }}</div>
|
||||
<div class="stat-label">Pending Memberships</div>
|
||||
</div>
|
||||
<div class="stat-box">
|
||||
<div class="stat-number">{{ recent_payments }}</div>
|
||||
<div class="stat-label">Recent Payments</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-widget">
|
||||
<div class="dashboard-widget-title">{% trans 'Quick Actions' %}</div>
|
||||
<div class="dashboard-widget-content">
|
||||
<div class="module">
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="{% url 'import_member' %}">Import Existing Member</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{% url 'admin:core_membership_add' %}">Add New Membership</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{% url 'admin:core_page_add' %}">Create New Page</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="{% url 'admin:core_announcement_add' %}">Add Announcement</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="content-main">
|
||||
<!-- Stats Dashboard -->
|
||||
<div class="dashboard">
|
||||
<div class="stat-card stat-users">
|
||||
<div class="stat-icon">
|
||||
<i class="fas fa-users"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3 class="stat-value">{{ user_count }}</h3>
|
||||
<p class="stat-label">Total Users</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card stat-memberships">
|
||||
<div class="stat-icon">
|
||||
<i class="fas fa-id-card"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3 class="stat-value">{{ active_memberships }}</h3>
|
||||
<p class="stat-label">Active Memberships</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card stat-payments">
|
||||
<div class="stat-icon">
|
||||
<i class="fas fa-money-bill-wave"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3 class="stat-value">{{ recent_payments }}</h3>
|
||||
<p class="stat-label">Recent Payments</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card stat-pending">
|
||||
<div class="stat-icon">
|
||||
<i class="fas fa-clock"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3 class="stat-value">{{ pending_memberships }}</h3>
|
||||
<p class="stat-label">Pending Approvals</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if app_list %}
|
||||
<div class="dashboard-widget">
|
||||
<div class="dashboard-widget-title">{% trans 'Applications' %}</div>
|
||||
<div class="dashboard-widget-content">
|
||||
<!-- Quick Actions -->
|
||||
<div class="quick-actions">
|
||||
<h2>Quick Actions</h2>
|
||||
<div class="action-buttons">
|
||||
<a href="{% url 'admin:core_user_add' %}" class="action-button primary">
|
||||
<i class="fas fa-user-plus"></i> Add User
|
||||
</a>
|
||||
<a href="{% url 'admin:core_membership_add' %}"
|
||||
class="action-button primary">
|
||||
<i class="fas fa-id-card"></i> Create Membership
|
||||
</a>
|
||||
<a href="{% url 'admin:payments_payment_add' %}"
|
||||
class="action-button primary">
|
||||
<i class="fas fa-money-bill"></i> Record Payment
|
||||
</a>
|
||||
<a href="{% url 'admin:core_formsubmission_changelist' %}"
|
||||
class="action-button">
|
||||
<i class="fas fa-clipboard-list"></i> View Form Submissions
|
||||
</a>
|
||||
<a href="{% url 'admin:member_area_content_add' %}"
|
||||
class="action-button">
|
||||
<i class="fas fa-file-alt"></i> Add Content
|
||||
</a>
|
||||
<a href="{% url 'admin:member_area_eventregistration_changelist' %}"
|
||||
class="action-button">
|
||||
<i class="fas fa-calendar-check"></i> Event Registrations
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Recent Activity -->
|
||||
<div class="recent-activity">
|
||||
<h2>Recent Activity</h2>
|
||||
<ul class="activity-list">
|
||||
{% for log in recent_actions %}
|
||||
<li class="activity-item">
|
||||
<div class="activity-icon">
|
||||
{% if log.is_addition %}
|
||||
<i class="fas fa-plus"></i>
|
||||
{% elif log.is_change %}
|
||||
<i class="fas fa-edit"></i>
|
||||
{% elif log.is_deletion %}
|
||||
<i class="fas fa-trash"></i>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="activity-content">
|
||||
<p class="activity-text">
|
||||
{{ log.user.username }}
|
||||
{% if log.is_addition %}
|
||||
added
|
||||
{% elif log.is_change %}
|
||||
changed
|
||||
{% elif log.is_deletion %}
|
||||
deleted
|
||||
{% endif %}
|
||||
{{ log.content_type.name }}: {{ log.object_repr }}
|
||||
</p>
|
||||
<p class="activity-meta">{{ log.action_time|timesince }} ago</p>
|
||||
</div>
|
||||
{% if log.is_addition or log.is_change %}
|
||||
<a href="{{ log.get_admin_url }}" class="activity-action">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% empty %}
|
||||
<li class="activity-item">
|
||||
<p>No recent activity.</p>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<!-- App List -->
|
||||
<div id="app-list">
|
||||
{% for app in app_list %}
|
||||
<div class="app-{{ app.app_label }} module{% if app.app_url in request.path %} current-app{% endif %}">
|
||||
<table>
|
||||
<caption>
|
||||
<a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a>
|
||||
</caption>
|
||||
<div class="app-section">
|
||||
<h2 class="app-name">{{ app.name }}</h2>
|
||||
<div class="app-models">
|
||||
{% for model in app.models %}
|
||||
<tr class="model-{{ model.object_name|lower }}{% if model.admin_url in request.path %} current-model{% endif %}">
|
||||
<div class="model-item">
|
||||
{% if model.admin_url %}
|
||||
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
|
||||
<a href="{{ model.admin_url }}" class="model-link">
|
||||
<i class="fas fa-table"></i> {{ model.name }}
|
||||
</a>
|
||||
{% else %}
|
||||
<th scope="row">{{ model.name }}</th>
|
||||
<span class="model-link disabled">{{ model.name }}</span>
|
||||
{% endif %}
|
||||
|
||||
{% if model.add_url %}
|
||||
<td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
|
||||
{% else %}
|
||||
<td> </td>
|
||||
{% endif %}
|
||||
|
||||
{% if model.admin_url %}
|
||||
{% if model.view_only %}
|
||||
<td><a href="{{ model.admin_url }}" class="viewlink">{% trans 'View' %}</a></td>
|
||||
{% else %}
|
||||
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
|
||||
<div class="model-actions">
|
||||
{% if model.add_url %}
|
||||
<a href="{{ model.add_url }}" class="add-link">
|
||||
<i class="fas fa-plus"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<td> </td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% if model.admin_url %}
|
||||
<a href="{{ model.admin_url }}" class="change-link">
|
||||
<i class="fas fa-list"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>{% trans "You don't have permission to view or edit anything." %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
{{ block.super }}
|
||||
<div class="dashboard-widget">
|
||||
<div class="dashboard-widget-title">{% trans 'Member Management' %}</div>
|
||||
<div class="dashboard-widget-content">
|
||||
<ul>
|
||||
<li><a href="{% url 'import_member' %}">Import Existing Members</a></li>
|
||||
<li><a href="{% url 'admin:core_membership_changelist' %}">Manage Memberships</a></li>
|
||||
<li><a href="{% url 'admin:core_user_changelist' %}">Manage Users</a></li>
|
||||
</ul>
|
||||
<div id="content-related">
|
||||
<div class="module" id="recent-actions-module">
|
||||
<h2>{% translate 'My Actions' %}</h2>
|
||||
<h3>{% translate 'My Recent Actions' %}</h3>
|
||||
{% load log %}
|
||||
{% get_admin_log 10 as admin_log for_user user %}
|
||||
{% if not admin_log %}
|
||||
<p>{% translate 'None available' %}</p>
|
||||
{% else %}
|
||||
<ul class="actionlist">
|
||||
{% for entry in admin_log %}
|
||||
<li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">
|
||||
{% if entry.is_deletion or not entry.get_admin_url %}
|
||||
{{ entry.object_repr }}
|
||||
{% else %}
|
||||
<a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
|
||||
{% endif %}
|
||||
<br>
|
||||
{% if entry.content_type %}
|
||||
<span class="mini quiet">
|
||||
{% filter capfirst %}
|
||||
{{ entry.content_type.name }}
|
||||
{% endfilter %}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="mini quiet">{% translate 'Unknown content' %}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-widget">
|
||||
<div class="dashboard-widget-title">{% trans 'Content Management' %}</div>
|
||||
<div class="dashboard-widget-content">
|
||||
<ul>
|
||||
<li><a href="{% url 'admin:core_page_changelist' %}">Manage Pages</a></li>
|
||||
<li><a href="{% url 'admin:core_announcement_changelist' %}">Manage Announcements</a></li>
|
||||
<li><a href="{% url 'admin:member_area_content_changelist' %}">Manage Content</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue