mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-22 13:31:18 -05:00
Enhance moderation dashboard UI and UX:
- Add HTMX-powered filtering with instant updates - Add smooth transitions and loading states - Improve visual hierarchy and styling - Add review notes functionality - Add confirmation dialogs for actions - Make navigation sticky - Add hover effects and visual feedback - Improve dark mode support
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-03 19:59
|
||||
# Generated by Django 5.1.3 on 2024-11-12 18:07
|
||||
|
||||
import django.db.models.deletion
|
||||
import history_tracking.mixins
|
||||
import simple_history.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@@ -12,12 +9,12 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Park",
|
||||
name="HistoricalSlug",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
@@ -28,49 +25,26 @@ class Migration(migrations.Migration):
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=200)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="HistoricalPark",
|
||||
fields=[
|
||||
("object_id", models.PositiveIntegerField()),
|
||||
("slug", models.SlugField(max_length=255)),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
(
|
||||
"id",
|
||||
models.BigIntegerField(
|
||||
auto_created=True, blank=True, db_index=True, verbose_name="ID"
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=200)),
|
||||
("history_id", models.AutoField(primary_key=True, serialize=False)),
|
||||
("history_date", models.DateTimeField(db_index=True)),
|
||||
("history_change_reason", models.CharField(max_length=100, null=True)),
|
||||
(
|
||||
"history_type",
|
||||
models.CharField(
|
||||
choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")],
|
||||
max_length=1,
|
||||
),
|
||||
),
|
||||
(
|
||||
"history_user",
|
||||
"content_type",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="+",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="contenttypes.contenttype",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "historical park",
|
||||
"verbose_name_plural": "historical parks",
|
||||
"ordering": ("-history_date", "-history_id"),
|
||||
"get_latest_by": ("history_date", "history_id"),
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["content_type", "object_id"],
|
||||
name="history_tra_content_63013c_idx",
|
||||
),
|
||||
models.Index(fields=["slug"], name="history_tra_slug_f843aa_idx"),
|
||||
],
|
||||
"unique_together": {("content_type", "slug")},
|
||||
},
|
||||
bases=(
|
||||
history_tracking.mixins.HistoricalChangeMixin,
|
||||
simple_history.models.HistoricalChanges,
|
||||
models.Model,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-04 00:17
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("history_tracking", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="historicalpark",
|
||||
name="history_user",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name="Park",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name="HistoricalPark",
|
||||
),
|
||||
]
|
||||
@@ -1,54 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-05 20:44
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("contenttypes", "0002_remove_content_type_name"),
|
||||
(
|
||||
"history_tracking",
|
||||
"0002_remove_historicalpark_history_user_delete_park_and_more",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="HistoricalSlug",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("object_id", models.PositiveIntegerField()),
|
||||
("slug", models.SlugField(max_length=255)),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
(
|
||||
"content_type",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="contenttypes.contenttype",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["content_type", "object_id"],
|
||||
name="history_tra_content_63013c_idx",
|
||||
),
|
||||
models.Index(fields=["slug"], name="history_tra_slug_f843aa_idx"),
|
||||
],
|
||||
"unique_together": {("content_type", "slug")},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,12 +1,30 @@
|
||||
# history_tracking/mixins.py
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
|
||||
class HistoricalChangeMixin(models.Model):
|
||||
"""Mixin for historical models to track changes"""
|
||||
id = models.BigIntegerField(db_index=True, auto_created=True, blank=True)
|
||||
history_date = models.DateTimeField()
|
||||
history_id = models.AutoField(primary_key=True)
|
||||
history_type = models.CharField(max_length=1)
|
||||
history_user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='+'
|
||||
)
|
||||
history_change_reason = models.CharField(max_length=100, null=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
ordering = ['-history_date', '-history_id']
|
||||
|
||||
class HistoricalChangeMixin:
|
||||
@property
|
||||
def prev_record(self):
|
||||
"""Get the previous record for this instance"""
|
||||
try:
|
||||
return type(self).objects.filter(
|
||||
return self.__class__.objects.filter(
|
||||
history_date__lt=self.history_date,
|
||||
id=self.id
|
||||
).order_by('-history_date').first()
|
||||
@@ -28,12 +46,29 @@ class HistoricalChangeMixin:
|
||||
"history_user_id",
|
||||
"history_change_reason",
|
||||
"history_type",
|
||||
"id",
|
||||
"_state",
|
||||
"_history_user_cache"
|
||||
] and not field.startswith("_"):
|
||||
try:
|
||||
old_value = getattr(prev_record, field)
|
||||
new_value = getattr(self, field)
|
||||
if old_value != new_value:
|
||||
changes[field] = {"old": old_value, "new": new_value}
|
||||
changes[field] = {"old": str(old_value), "new": str(new_value)}
|
||||
except AttributeError:
|
||||
continue
|
||||
return changes
|
||||
|
||||
@property
|
||||
def history_user_display(self):
|
||||
"""Get a display name for the history user"""
|
||||
if hasattr(self, 'history_user') and self.history_user:
|
||||
return str(self.history_user)
|
||||
return None
|
||||
|
||||
def get_instance(self):
|
||||
"""Get the model instance this history record represents"""
|
||||
try:
|
||||
return self.__class__.objects.get(id=self.id)
|
||||
except self.__class__.DoesNotExist:
|
||||
return None
|
||||
|
||||
@@ -5,12 +5,17 @@ from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from simple_history.models import HistoricalRecords
|
||||
from .mixins import HistoricalChangeMixin
|
||||
from typing import Any, Type, TypeVar, cast
|
||||
from django.db.models import QuerySet
|
||||
|
||||
T = TypeVar('T', bound=models.Model)
|
||||
|
||||
class HistoricalModel(models.Model):
|
||||
"""Abstract base class for models with history tracking"""
|
||||
history: HistoricalRecords = HistoricalRecords(inherit=True)
|
||||
id = models.BigAutoField(primary_key=True)
|
||||
history: HistoricalRecords = HistoricalRecords(
|
||||
inherit=True,
|
||||
bases=(HistoricalChangeMixin,)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@@ -20,6 +25,11 @@ class HistoricalModel(models.Model):
|
||||
"""Get the history model class"""
|
||||
return cast(Type[T], self.history.model) # type: ignore
|
||||
|
||||
def get_history(self) -> QuerySet:
|
||||
"""Get all history records for this instance"""
|
||||
model = self._history_model
|
||||
return model.objects.filter(id=self.pk).order_by('-history_date')
|
||||
|
||||
class HistoricalSlug(models.Model):
|
||||
"""Track historical slugs for models"""
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
|
||||
Reference in New Issue
Block a user