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( '{}', 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",)