Files
tecvico-website/apps/projects/admin.py
T
2026-07-19 15:03:27 +03:30

146 lines
4.7 KiB
Python

from django.contrib import admin
from django.utils.html import format_html
from .models import ProjectBrief, ProjectBriefAttachment, ProjectInternalNote
class ProjectBriefAttachmentInline(admin.TabularInline):
model = ProjectBriefAttachment
extra = 0
readonly_fields = ("original_filename", "file", "uploaded_at")
can_delete = True
class ProjectInternalNoteInline(admin.TabularInline):
model = ProjectInternalNote
extra = 1
fields = ("author", "note", "created_at")
readonly_fields = ("created_at",)
@admin.register(ProjectBrief)
class ProjectBriefAdmin(admin.ModelAdmin):
list_display = (
"title",
"client",
"category",
"status_badge",
"locked",
"show_on_website",
"budget_range",
"submitted_at",
)
list_filter = (
"status",
"category",
"budget_range",
"locked",
"show_on_website",
"submitted_at",
)
search_fields = (
"title",
"description",
"client__username",
"client__email",
"client__first_name",
"client__last_name",
)
readonly_fields = ("client", "submitted_at", "updated_at")
fieldsets = (
(
"Brief",
{
"description": (
"Each field has its own lock and website visibility controls. "
"Lock prevents client edits; Show on website includes the field "
"on the public card when the project is published."
),
"fields": (
"client",
"locked",
"show_on_website",
("title", "lock_title", "show_title_on_website"),
("category", "lock_category", "show_category_on_website"),
"description",
("lock_description", "show_description_on_website"),
("budget_range", "lock_budget_range", "show_budget_on_website"),
("desired_deadline", "lock_desired_deadline", "show_deadline_on_website"),
"reference_links",
("lock_reference_links", "show_references_on_website"),
("lock_attachments", "show_attachments_on_website"),
),
},
),
(
"Workflow",
{
"fields": (
"status",
"quote_text",
"website_notes",
"submitted_at",
"updated_at",
),
},
),
(
"Website showcase overrides",
{
"description": (
"Optional public-facing content. Leave blank to fall back to the "
"brief field values above."
),
"fields": (
"public_title",
"public_summary",
"public_tags",
"public_image",
"public_project_status",
"public_url",
),
},
),
)
inlines = [ProjectBriefAttachmentInline, ProjectInternalNoteInline]
date_hierarchy = "submitted_at"
class Media:
css = {"all": ("css/project-brief-admin.css",)}
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
if isinstance(instance, ProjectInternalNote) and not instance.author_id:
instance.author = request.user
instance.save()
formset.save_m2m()
for obj in formset.deleted_objects:
obj.delete()
@admin.display(description="Status", boolean=False)
def status_badge(self, obj):
colors = {
ProjectBrief.STATUS_SUBMITTED: "#3b82f6",
ProjectBrief.STATUS_UNDER_REVIEW: "#8b5cf6",
ProjectBrief.STATUS_QUOTED: "#f59e0b",
ProjectBrief.STATUS_ACCEPTED: "#10b981",
ProjectBrief.STATUS_DECLINED: "#ef4444",
ProjectBrief.STATUS_IN_PROGRESS: "#06b6d4",
ProjectBrief.STATUS_DELIVERED: "#22c55e",
ProjectBrief.STATUS_CLOSED: "#6b7280",
}
color = colors.get(obj.status, "#6b7280")
return format_html(
'<span style="padding:2px 8px;border-radius:999px;background:{};color:#fff;font-size:12px;">{}</span>',
color,
obj.get_status_display(),
)
@admin.register(ProjectInternalNote)
class ProjectInternalNoteAdmin(admin.ModelAdmin):
list_display = ("brief", "author", "created_at")
search_fields = ("brief__title", "note", "author__username")
readonly_fields = ("created_at",)