Template
Populate the client website template with catalog feature flags.
Extract always-on public/portal/UTM plus optional email_sms, directmail, blog, payments, social, and social_ai so new client sites can be bootstrapped from this seed. Refs #1 Refs #2 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DashboardConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "dashboard"
|
||||
|
||||
def ready(self):
|
||||
from core.registry import register_portal_nav
|
||||
|
||||
register_portal_nav(
|
||||
section="dashboard",
|
||||
label="Dashboard",
|
||||
url_name="dashboard:home",
|
||||
group="Overview",
|
||||
order=10,
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
{% extends "portal_base.html" %}
|
||||
{% block title %}Dashboard · Portal{% endblock %}
|
||||
{% block topbar_title %}Dashboard{% endblock %}
|
||||
{% block portal_content %}
|
||||
<div class="stat-row">
|
||||
<div class="stat-card">
|
||||
<div class="label">New leads</div>
|
||||
<div class="value">{{ lead_count }}</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">Open pipeline</div>
|
||||
<div class="value">{{ open_pipeline }}</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="label">Mailing list</div>
|
||||
<div class="value">{{ contact_count }}</div>
|
||||
</div>
|
||||
{% if 'social' in enabled_features %}
|
||||
<div class="stat-card">
|
||||
<div class="label">Scheduled posts</div>
|
||||
<div class="value">{{ scheduled_posts|default:0 }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if 'payments' in enabled_features %}
|
||||
<div class="stat-card">
|
||||
<div class="label">Open invoices</div>
|
||||
<div class="value">{{ open_invoices|default:0 }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="split">
|
||||
<div class="panel">
|
||||
<div class="panel-h">
|
||||
<h2>Recent leads</h2>
|
||||
<a class="btn btn-sm btn-ghost" href="{% url 'leads:list' %}">View all</a>
|
||||
</div>
|
||||
<div class="panel-b" style="padding:0">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Source</th><th>Status</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for lead in recent_leads %}
|
||||
<tr>
|
||||
<td><a href="{% url 'leads:detail' lead.pk %}">{{ lead.contact }}</a></td>
|
||||
<td>{% if lead.attribution %}{{ lead.attribution.utm_source|default:"direct" }}{% if lead.attribution.utm_campaign %} / {{ lead.attribution.utm_campaign }}{% endif %}{% else %}—{% endif %}</td>
|
||||
<td><span class="badge badge-{{ lead.status }}">{{ lead.get_status_display }}</span></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="3" class="empty-state">No leads yet.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% if 'email_sms' in enabled_features %}
|
||||
<div class="panel">
|
||||
<div class="panel-h">
|
||||
<h2>Upcoming outreach</h2>
|
||||
<a class="btn btn-sm btn-ghost" href="{% url 'email_sms:campaign_list' %}">Compose</a>
|
||||
</div>
|
||||
<div class="panel-b">
|
||||
{% for campaign in upcoming_campaigns %}
|
||||
<p style="margin:0 0 12px;font-size:14px">
|
||||
<strong><a href="{% url 'email_sms:campaign_detail' campaign.pk %}">{{ campaign.name }}</a></strong>
|
||||
— {{ campaign.get_channel_display }} · {{ campaign.get_status_display }}
|
||||
{% if campaign.scheduled_for %} · {{ campaign.scheduled_for }}{% endif %}
|
||||
</p>
|
||||
{% empty %}
|
||||
<p class="empty-state" style="margin:0">No campaigns yet.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,9 @@
|
||||
from django.urls import path
|
||||
|
||||
from dashboard import views
|
||||
|
||||
app_name = "dashboard"
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.home, name="home"),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render
|
||||
|
||||
from contacts.models import Contact
|
||||
from core.registry import collect_dashboard_context
|
||||
from leads.models import Lead
|
||||
|
||||
|
||||
@login_required
|
||||
def home(request):
|
||||
context = {
|
||||
"lead_count": Lead.objects.filter(status=Lead.Status.NEW).count(),
|
||||
"open_pipeline": Lead.objects.filter(
|
||||
status__in=[Lead.Status.NEW, Lead.Status.CONTACTED]
|
||||
).count(),
|
||||
"contact_count": Contact.objects.count(),
|
||||
"recent_leads": Lead.objects.select_related("contact", "attribution").all()[:8],
|
||||
}
|
||||
context.update(collect_dashboard_context(request))
|
||||
return render(request, "dashboard/home.html", context)
|
||||
Reference in New Issue
Block a user