84 lines
3.2 KiB
HTML
84 lines
3.2 KiB
HTML
{% extends "admin/layouts/base.html" %}
|
|
{% block title %}Platform Analytics{% endblock %}
|
|
{% block content %}
|
|
<h4 class="fw-bold mb-4"><i class="bi bi-bar-chart me-2"></i>Platform Analytics</h4>
|
|
|
|
<div class="row g-3 mb-4">
|
|
{% set kpis = [
|
|
('Total Tenants', stats.total_tenants, 'building', 'primary'),
|
|
('Active', stats.active_count, 'check-circle', 'success'),
|
|
('Trial', stats.trial_count, 'hourglass-split', 'info'),
|
|
('Suspended/Cancelled', stats.suspended_count + stats.cancelled_count, 'x-circle', 'danger'),
|
|
('MRR', '$' ~ "%.2f"|format(stats.mrr), 'currency-dollar', 'success'),
|
|
('Revenue (30d)', '$' ~ "%.2f"|format(stats.revenue_30d), 'graph-up', 'primary'),
|
|
('New Tenants (30d)', stats.new_tenants_30d, 'person-plus', 'secondary'),
|
|
('Conversions (30d)', stats.converted_30d, 'arrow-up-circle', 'success'),
|
|
('Churn (30d)', stats.churned_30d, 'arrow-down-circle', 'danger'),
|
|
] %}
|
|
{% for label, value, icon, color in kpis %}
|
|
<div class="col-md-4 col-lg-3">
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-body d-flex align-items-center gap-3">
|
|
<div class="rounded-circle bg-{{ color }} bg-opacity-10 p-3">
|
|
<i class="bi bi-{{ icon }} fs-4 text-{{ color }}"></i>
|
|
</div>
|
|
<div>
|
|
<div class="fs-5 fw-bold">{{ value }}</div>
|
|
<div class="text-muted small">{{ label }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<!-- Tenants by plan -->
|
|
<div class="col-md-5">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-header fw-semibold">Active Tenants by Plan</div>
|
|
<div class="card-body">
|
|
{% for plan_name, count in stats.plan_counts %}
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span>{{ plan_name }}</span>
|
|
<span class="badge bg-primary rounded-pill">{{ count }}</span>
|
|
</div>
|
|
{% else %}
|
|
<p class="text-muted small">No data yet.</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Trials expiring soon -->
|
|
<div class="col-md-7">
|
|
<div class="card shadow-sm h-100">
|
|
<div class="card-header fw-semibold text-warning-emphasis">
|
|
<i class="bi bi-hourglass me-1"></i>Trials Expiring in 3 Days
|
|
</div>
|
|
{% if expiring_soon %}
|
|
<div class="table-responsive">
|
|
<table class="table table-sm mb-0">
|
|
<thead><tr><th>Tenant</th><th>Plan</th><th>Trial Ends</th><th></th></tr></thead>
|
|
<tbody>
|
|
{% for t in expiring_soon %}
|
|
<tr>
|
|
<td><a href="{{ url_for('tenants.detail', tenant_id=t.id) }}">{{ t.name }}</a></td>
|
|
<td>{{ t.plan.name if t.plan else '—' }}</td>
|
|
<td class="text-warning small fw-semibold">{{ t.trial_ends_at.strftime('%Y-%m-%d %H:%M') }}</td>
|
|
<td>
|
|
<a href="{{ url_for('tenants.set_status', tenant_id=t.id) }}"
|
|
class="btn btn-success btn-sm" style="display:inline-block;">Convert</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="card-body text-muted small">No trials expiring in the next 3 days.</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %} |