Files
DevSuite/src/admin/projects.py
T

227 lines
6.3 KiB
Python

from django.contrib import admin
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from src.admin.shared import (
admin_site,
format_badge,
format_currency,
format_placeholder,
format_strong,
)
from src.models.projects import Milestone, Project, Task
class MilestoneInline(admin.TabularInline):
model = Milestone
extra = 1
fields = ("title", "due_date", "is_completed")
show_change_link = True
ordering = ("due_date",)
verbose_name_plural = "Milestones"
class TaskInline(admin.TabularInline):
model = Task
extra = 1
fields = ("title", "status", "due_date")
show_change_link = True
ordering = ("status", "due_date")
verbose_name_plural = "Tasks"
@admin.register(Project, site=admin_site)
class ProjectAdmin(admin.ModelAdmin):
list_display = (
"name_display",
"client_display",
"status_badge",
"start_date",
"deadline",
"budget_display",
)
list_filter = ("status", "client", "start_date", "deadline")
search_fields = ("name", "client__name", "description")
ordering = ("-created_at",)
list_per_page = 25
inlines = [MilestoneInline, TaskInline]
fieldsets = (
(
_("Project Overview"),
{
"fields": ("client", "name", "description", "status"),
},
),
(
_("Timeline"),
{
"fields": ("start_date", "deadline"),
"description": "Start and deadline dates help in tracking project progress.",
},
),
(
_("Financials"),
{
"fields": ("budget",),
},
),
(
_("Timestamps"),
{
"fields": ("created_at", "updated_at"),
"classes": ("collapse",),
},
),
)
readonly_fields = ("created_at", "updated_at")
def name_display(self, obj):
return format_strong(obj.name)
name_display.short_description = "Project"
def client_display(self, obj):
return obj.client.name
client_display.short_description = "Client"
def status_badge(self, obj):
color_map = {
"PLANNING": "#6c757d",
"IN_PROGRESS": "#007bff",
"ON_HOLD": "#ffc107",
"COMPLETED": "#28a745",
"CANCELLED": "#dc3545",
}
color = color_map.get(obj.status, "#999")
return format_badge(obj.get_status_display(), background=color)
status_badge.short_description = "Status"
def budget_display(self, obj):
return format_currency(obj.budget) if obj.budget else format_placeholder()
budget_display.short_description = "Budget"
def save_model(self, request, obj, form, change):
if obj.deadline and obj.start_date and obj.deadline < obj.start_date:
self.message_user(
request,
"⚠️ Deadline cannot be earlier than start date.",
level="warning",
)
super().save_model(request, obj, form, change)
@admin.register(Milestone, site=admin_site)
class MilestoneAdmin(admin.ModelAdmin):
list_display = ("title_display", "project_display", "due_date", "status_badge")
list_filter = ("is_completed", "due_date", "project")
search_fields = ("title", "project__name")
ordering = ("due_date",)
fieldsets = (
(
_("Milestone Info"),
{
"fields": ("project", "title", "due_date", "is_completed"),
},
),
)
def title_display(self, obj):
return format_strong(obj.title)
title_display.short_description = "Title"
def project_display(self, obj):
return obj.project.name
project_display.short_description = "Project"
def status_badge(self, obj):
color = "#28a745" if obj.is_completed else "#dc3545"
text = "Completed" if obj.is_completed else "Pending"
return format_badge(text, background=color)
status_badge.short_description = "Status"
@admin.register(Task, site=admin_site)
class TaskAdmin(admin.ModelAdmin):
list_display = (
"title_display",
"project_display",
"status_badge",
"due_date_display",
)
list_filter = ("status", "due_date", "project")
search_fields = ("title", "project__name", "description")
ordering = ("status", "due_date")
fieldsets = (
(
_("Task Info"),
{
"fields": ("project", "title", "description", "status"),
},
),
(
_("Schedule"),
{
"fields": ("due_date",),
"description": "Due date cannot be in the past.",
},
),
(
_("Timestamps"),
{
"fields": ("created_at", "updated_at"),
"classes": ("collapse",),
},
),
)
readonly_fields = ("created_at", "updated_at")
def title_display(self, obj):
return format_strong(obj.title)
title_display.short_description = "Title"
def project_display(self, obj):
return obj.project.name
project_display.short_description = "Project"
def due_date_display(self, obj):
if not obj.due_date:
return format_placeholder(None, "No due date", "#999")
if obj.due_date < timezone.localdate():
return format_badge(obj.due_date, background="#dc3545")
if obj.due_date == timezone.localdate():
return format_badge("Today", background="#f39c12")
return obj.due_date
due_date_display.short_description = "Due Date"
def status_badge(self, obj):
color_map = {
"TODO": "#6c757d",
"IN_PROGRESS": "#007bff",
"REVIEW": "#ffc107",
"DONE": "#28a745",
}
color = color_map.get(obj.status, "#333")
return format_badge(obj.get_status_display(), background=color)
status_badge.short_description = "Status"
def save_model(self, request, obj, form, change):
if obj.due_date and obj.due_date < timezone.localdate():
self.message_user(
request, "⚠️ Due date cannot be in the past.", level="warning"
)
super().save_model(request, obj, form, change)