146 lines
4.7 KiB
Python
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",)
|