Files
tecvico-website/apps/projects/admin.py
T
2026-07-19 14:31:28 +03:30

172 lines
5.3 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",
{
"fields": (
"client",
"title",
"category",
"description",
"budget_range",
"desired_deadline",
"reference_links",
),
},
),
(
"Workflow",
{
"fields": (
"status",
"quote_text",
"website_notes",
"submitted_at",
"updated_at",
),
},
),
(
"Client editing locks",
{
"description": (
"Lock individual fields so the client cannot change them while "
"the brief is still editable. 'Lock entire project' blocks all "
"client edits regardless of status."
),
"fields": (
"locked",
"lock_title",
"lock_category",
"lock_description",
"lock_budget_range",
"lock_desired_deadline",
"lock_reference_links",
"lock_attachments",
),
},
),
(
"Website showcase",
{
"description": (
"Control whether this project appears on the public website and "
"which fields are visible on the website card."
),
"fields": (
"show_on_website",
"public_title",
"public_summary",
"public_tags",
"public_image",
"public_project_status",
"public_url",
),
},
),
(
"Website field visibility",
{
"description": (
"Each field can be independently shown or hidden on the website card."
),
"fields": (
"show_title_on_website",
"show_category_on_website",
"show_description_on_website",
"show_budget_on_website",
"show_deadline_on_website",
"show_references_on_website",
"show_attachments_on_website",
),
"classes": ("collapse",),
},
),
)
inlines = [ProjectBriefAttachmentInline, ProjectInternalNoteInline]
date_hierarchy = "submitted_at"
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",)