Upgrade system admin dashboard to v2
This commit is contained in:
@@ -1,25 +1,28 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% block content %}
|
||||
<section class="space-y-6">
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-6 shadow-soft">
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-brand-600">Platform Utility Centre</p>
|
||||
<h1 class="mt-2 text-2xl font-bold text-slate-900">System Admin Dashboard</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Monitor firms, service catalogue, SMTP, storage, reports, wizards and audit logs from one place.</p>
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.24em] text-brand-600">Platform Control Centre</p>
|
||||
<h1 class="mt-2 text-2xl font-bold text-slate-900">System Admin Dashboard V2</h1>
|
||||
<p class="mt-1 text-sm text-slate-500">Monitor firm readiness, catalogue health, SMTP, storage, billing readiness, reports, wizards and audit logs.</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="/wizards/system/firm/new" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white shadow-soft hover:bg-brand-700">+ Create Firm</a>
|
||||
<a href="/email/platform-smtp" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Platform SMTP</a>
|
||||
<a href="/platform-billing" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Platform Billing</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% set tabs = [
|
||||
('overview', 'Overview'),
|
||||
('firms', 'Firms'),
|
||||
('firm-setup-health', 'Setup Health'),
|
||||
('catalogue', 'Service Catalogue'),
|
||||
('smtp', 'Platform SMTP'),
|
||||
('storage', 'Storage'),
|
||||
('billing', 'Billing Readiness'),
|
||||
('reports', 'Reports'),
|
||||
('wizards', 'Wizards'),
|
||||
('audit-logs', 'Audit Logs')
|
||||
@@ -95,4 +98,3 @@
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
+5
-5
@@ -1,15 +1,15 @@
|
||||
<div class="rounded-3xl border border-slate-200 bg-white shadow-soft">
|
||||
<div class="border-b border-slate-100 p-5"><h2 class="text-lg font-bold text-slate-900">Recent Audit Logs</h2><p class="text-sm text-slate-500">Latest 25 audit events.</p></div>
|
||||
<div class="border-b border-slate-100 p-5"><h2 class="text-lg font-bold text-slate-900">Recent Audit Logs</h2><p class="text-sm text-slate-500">Latest 25 audit events with target tenant and IP details.</p></div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-slate-100 text-sm">
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500"><tr><th class="px-5 py-3">Time</th><th class="px-5 py-3">User</th><th class="px-5 py-3">Action</th><th class="px-5 py-3">Entity</th><th class="px-5 py-3">Status</th></tr></thead>
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500"><tr><th class="px-5 py-3">Time</th><th class="px-5 py-3">User</th><th class="px-5 py-3">Action</th><th class="px-5 py-3">Entity</th><th class="px-5 py-3">Target Tenant</th><th class="px-5 py-3">IP</th><th class="px-5 py-3">Status</th></tr></thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for log in audit_logs %}
|
||||
<tr class="hover:bg-slate-50"><td class="px-5 py-3 whitespace-nowrap">{{ log.created_at_utc }}</td><td class="px-5 py-3">{{ log.actor_email or '-' }}</td><td class="px-5 py-3 font-semibold text-slate-900">{{ log.action }}</td><td class="px-5 py-3">{{ log.entity_type }}{% if log.entity_name %} > {{ log.entity_name }}{% endif %}</td><td class="px-5 py-3"><span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700">{{ log.status }}</span></td></tr>
|
||||
<tr class="hover:bg-slate-50"><td class="px-5 py-3 whitespace-nowrap">{{ log.created_at_utc }}</td><td class="px-5 py-3">{{ log.actor_email or '-' }}</td><td class="px-5 py-3 font-semibold text-slate-900">{{ log.action }}</td><td class="px-5 py-3">{{ log.entity_type }}{% if log.entity_name %} > {{ log.entity_name }}{% endif %}</td><td class="px-5 py-3">{{ log.target_tenant_id or '-' }}</td><td class="px-5 py-3">{{ log.ip_address or '-' }}</td><td class="px-5 py-3"><span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700">{{ log.status }}</span></td></tr>
|
||||
{% else %}
|
||||
<tr><td colspan="5" class="px-5 py-8 text-center text-slate-500">No audit logs found.</td></tr>
|
||||
<tr><td colspan="7" class="px-5 py-8 text-center text-slate-500">No audit logs found.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<div class="space-y-6">
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-6 shadow-soft">
|
||||
<h2 class="text-lg font-bold text-slate-900">Billing Readiness</h2>
|
||||
<p class="mt-1 text-sm text-slate-500">Read-only view of existing platform billing and firm billing setup readiness.</p>
|
||||
{% if not billing.available %}<div class="mt-4 rounded-2xl border border-amber-100 bg-amber-50 p-4 text-sm font-semibold text-amber-800">{{ billing.message }}</div>{% endif %}
|
||||
</div>
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Platform Plans</div><div class="mt-2 text-2xl font-bold">{{ billing.plan_count }}</div><div class="text-xs text-slate-500">{{ billing.active_plan_count }} active</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Firm Accounts</div><div class="mt-2 text-2xl font-bold">{{ billing.audit_firm_account_count }}</div><div class="text-xs text-slate-500">{{ billing.firms_without_account }} firms without account</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Subscriptions</div><div class="mt-2 text-2xl font-bold text-emerald-700">{{ billing.active_subscription_count }}</div><div class="text-xs text-slate-500">{{ billing.expired_subscription_count }} expired/suspended/cancelled</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Firm Billing Settings</div><div class="mt-2 text-2xl font-bold">{{ billing.firm_billing_settings }}</div><div class="text-xs text-slate-500">{{ billing.firms_without_billing_settings }} firms pending</div></div>
|
||||
</div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-6 shadow-soft">
|
||||
<h3 class="font-bold text-slate-900">Actions</h3>
|
||||
<div class="mt-4 flex flex-wrap gap-2"><a href="/platform-billing" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">Open Platform Billing</a><a href="/billing/settings" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Open Firm Billing Settings</a></div>
|
||||
</div>
|
||||
</div>
|
||||
+12
-6
@@ -1,27 +1,33 @@
|
||||
<div class="space-y-6">
|
||||
<div class="grid gap-4 md:grid-cols-4">
|
||||
<div class="grid gap-4 md:grid-cols-3 xl:grid-cols-6">
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Categories</div><div class="mt-2 text-2xl font-bold">{{ catalogue.category_count }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Services</div><div class="mt-2 text-2xl font-bold">{{ catalogue.service_count }}</div><div class="text-xs text-slate-500">{{ catalogue.active_service_count }} active</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Inactive</div><div class="mt-2 text-2xl font-bold">{{ catalogue.inactive_service_count }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Default Tasks</div><div class="mt-2 text-2xl font-bold">{{ catalogue.default_task_count }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Due Rules</div><div class="mt-2 text-2xl font-bold">{{ catalogue.due_rule_count }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">No Tasks</div><div class="mt-2 text-2xl font-bold {% if catalogue.services_without_tasks_count %}text-amber-700{% else %}text-emerald-700{% endif %}">{{ catalogue.services_without_tasks_count }}</div></div>
|
||||
</div>
|
||||
|
||||
{% if catalogue.services_without_tasks_preview %}
|
||||
<div class="rounded-3xl border border-amber-100 bg-amber-50 p-5 shadow-soft"><h3 class="font-bold text-amber-900">Services without default tasks</h3><div class="mt-3 flex flex-wrap gap-2">{% for service in catalogue.services_without_tasks_preview %}<span class="rounded-full bg-white px-3 py-1 text-xs font-semibold text-amber-800">{{ service.code }} - {{ service.name }}</span>{% endfor %}</div></div>
|
||||
{% endif %}
|
||||
|
||||
<div class="rounded-3xl border border-slate-200 bg-white shadow-soft">
|
||||
<div class="flex flex-col gap-3 border-b border-slate-100 p-5 md:flex-row md:items-center md:justify-between">
|
||||
<div><h2 class="text-lg font-bold text-slate-900">Service Catalogue Readiness</h2><p class="text-sm text-slate-500">First 100 catalogue services with default task/rule counts.</p></div>
|
||||
<div><h2 class="text-lg font-bold text-slate-900">Service Catalogue Readiness</h2><p class="text-sm text-slate-500">First 150 catalogue services with default task/rule counts.</p></div>
|
||||
<div class="flex flex-wrap gap-2"><a href="/services/catalogue" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Open Catalogue</a><a href="/services/default-templates" class="rounded-xl border border-slate-200 px-4 py-2 text-sm font-semibold text-slate-700 hover:bg-slate-50">Default Templates</a></div>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-slate-100 text-sm">
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500"><tr><th class="px-5 py-3">Code</th><th class="px-5 py-3">Service</th><th class="px-5 py-3">Category</th><th class="px-5 py-3">Recurrence</th><th class="px-5 py-3 text-right">Tasks</th><th class="px-5 py-3 text-right">Rules</th><th class="px-5 py-3">Status</th></tr></thead>
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500"><tr><th class="px-5 py-3">Code</th><th class="px-5 py-3">Service</th><th class="px-5 py-3">Category</th><th class="px-5 py-3">Recurrence</th><th class="px-5 py-3 text-right">Tasks</th><th class="px-5 py-3 text-right">Rules</th><th class="px-5 py-3">Readiness</th><th class="px-5 py-3">Status</th></tr></thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for service in catalogue.services %}
|
||||
<tr class="hover:bg-slate-50"><td class="px-5 py-3 font-semibold">{{ service.code }}</td><td class="px-5 py-3">{{ service.name }}</td><td class="px-5 py-3">{{ service.category }}</td><td class="px-5 py-3">{{ service.recurrence_type }}</td><td class="px-5 py-3 text-right font-semibold">{{ service.task_count }}</td><td class="px-5 py-3 text-right font-semibold">{{ service.rule_count }}</td><td class="px-5 py-3"><span class="rounded-full px-2 py-1 text-xs font-semibold {% if service.is_active %}bg-emerald-50 text-emerald-700{% else %}bg-slate-100 text-slate-600{% endif %}">{% if service.is_active %}Active{% else %}Inactive{% endif %}</span></td></tr>
|
||||
<tr class="hover:bg-slate-50"><td class="px-5 py-3 font-semibold">{{ service.code }}</td><td class="px-5 py-3">{{ service.name }}</td><td class="px-5 py-3">{{ service.category }}</td><td class="px-5 py-3">{{ service.recurrence_type }}</td><td class="px-5 py-3 text-right font-semibold">{{ service.task_count }}</td><td class="px-5 py-3 text-right font-semibold">{{ service.rule_count }}</td><td class="px-5 py-3"><span class="rounded-full px-2.5 py-1 text-xs font-semibold {% if service.task_count %}bg-emerald-50 text-emerald-700{% else %}bg-amber-50 text-amber-700{% endif %}">{{ service.readiness }}</span></td><td class="px-5 py-3">{% if service.is_active %}<span class="text-emerald-700 font-semibold">Active</span>{% else %}<span class="text-slate-500">Inactive</span>{% endif %}</td></tr>
|
||||
{% else %}
|
||||
<tr><td colspan="7" class="px-5 py-8 text-center text-slate-500">No service catalogue found.</td></tr>
|
||||
<tr><td colspan="8" class="px-5 py-8 text-center text-slate-500">No services found.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
<div class="space-y-6">
|
||||
<div class="grid gap-4 md:grid-cols-3 xl:grid-cols-6">
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Firms</div><div class="mt-2 text-2xl font-bold">{{ health.total }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Ready</div><div class="mt-2 text-2xl font-bold text-emerald-700">{{ health.ready }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">Incomplete</div><div class="mt-2 text-2xl font-bold text-amber-700">{{ health.incomplete }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">No Branch</div><div class="mt-2 text-2xl font-bold">{{ health.missing_branch }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">No FY</div><div class="mt-2 text-2xl font-bold">{{ health.missing_fy }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase text-slate-500">No Services</div><div class="mt-2 text-2xl font-bold">{{ health.missing_services }}</div></div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-3xl border border-slate-200 bg-white shadow-soft">
|
||||
<div class="border-b border-slate-100 p-5"><h2 class="text-lg font-bold text-slate-900">Firm Setup Health Checklist</h2><p class="text-sm text-slate-500">A firm is ready when Branch, Firm Admin, FY, selected services and users are available.</p></div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-slate-100 text-sm">
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500"><tr><th class="px-5 py-3">Firm</th><th class="px-5 py-3">Branch</th><th class="px-5 py-3">Admin</th><th class="px-5 py-3">FY</th><th class="px-5 py-3">Services</th><th class="px-5 py-3">Users</th><th class="px-5 py-3">Branding</th><th class="px-5 py-3">Billing</th><th class="px-5 py-3">Status</th><th class="px-5 py-3">Missing</th></tr></thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for firm in health.rows %}
|
||||
<tr class="hover:bg-slate-50">
|
||||
<td class="px-5 py-3"><div class="font-semibold text-slate-900">{{ firm.name }}</div><div class="text-xs text-slate-500">{{ firm.code }}</div></td>
|
||||
<td class="px-5 py-3">{% if firm.branch_ok %}<span class="text-emerald-700 font-semibold">Done</span>{% else %}<span class="text-red-700 font-semibold">Missing</span>{% endif %}<div class="text-xs text-slate-500">{{ firm.branch_count }}</div></td>
|
||||
<td class="px-5 py-3">{% if firm.admin_ok %}<span class="text-emerald-700 font-semibold">Done</span>{% else %}<span class="text-red-700 font-semibold">Missing</span>{% endif %}<div class="text-xs text-slate-500">{{ firm.primary_admin }}</div></td>
|
||||
<td class="px-5 py-3">{% if firm.fy_ok %}<span class="text-emerald-700 font-semibold">Done</span>{% else %}<span class="text-red-700 font-semibold">Missing</span>{% endif %}<div class="text-xs text-slate-500">{{ firm.fy_count }} FY{% if firm.current_fy_ok %} / current set{% endif %}</div></td>
|
||||
<td class="px-5 py-3">{% if firm.services_ok %}<span class="text-emerald-700 font-semibold">Done</span>{% else %}<span class="text-red-700 font-semibold">Missing</span>{% endif %}<div class="text-xs text-slate-500">{{ firm.selected_service_count }}</div></td>
|
||||
<td class="px-5 py-3">{% if firm.users_ok %}<span class="text-emerald-700 font-semibold">Done</span>{% else %}<span class="text-red-700 font-semibold">Missing</span>{% endif %}<div class="text-xs text-slate-500">{{ firm.user_count }}</div></td>
|
||||
<td class="px-5 py-3">{% if firm.branding_ok %}<span class="text-emerald-700 font-semibold">Started</span>{% else %}<span class="text-amber-700 font-semibold">Pending</span>{% endif %}</td>
|
||||
<td class="px-5 py-3">{% if firm.billing_ok %}<span class="text-emerald-700 font-semibold">Ready</span>{% else %}<span class="text-amber-700 font-semibold">Pending</span>{% endif %}</td>
|
||||
<td class="px-5 py-3"><span class="rounded-full px-2.5 py-1 text-xs font-semibold {% if firm.status == 'Ready' %}bg-emerald-50 text-emerald-700{% else %}bg-amber-50 text-amber-700{% endif %}">{{ firm.status }} {{ firm.score }}/{{ firm.score_total }}</span></td>
|
||||
<td class="px-5 py-3 text-slate-600">{{ firm.missing_text }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="10" class="px-5 py-8 text-center text-slate-500">No firms found.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+9
-8
@@ -1,28 +1,29 @@
|
||||
<div class="rounded-3xl border border-slate-200 bg-white shadow-soft">
|
||||
<div class="flex flex-col gap-3 border-b border-slate-100 p-5 md:flex-row md:items-center md:justify-between">
|
||||
<div><h2 class="text-lg font-bold text-slate-900">Firms / Tenants</h2><p class="text-sm text-slate-500">Latest firms created on the platform.</p></div>
|
||||
<div><h2 class="text-lg font-bold text-slate-900">Firms / Tenants</h2><p class="text-sm text-slate-500">Firm readiness snapshot with admin, branch, FY and service counts.</p></div>
|
||||
<a href="/wizards/system/firm/new" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">+ Create Firm</a>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-slate-100 text-sm">
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">
|
||||
<tr><th class="px-5 py-3">Code</th><th class="px-5 py-3">Firm</th><th class="px-5 py-3">Status</th><th class="px-5 py-3">Firm Admin</th><th class="px-5 py-3 text-right">Branches</th><th class="px-5 py-3 text-right">Users</th><th class="px-5 py-3">Action</th></tr>
|
||||
</thead>
|
||||
<thead class="bg-slate-50 text-left text-xs font-semibold uppercase tracking-wide text-slate-500"><tr><th class="px-5 py-3">Code</th><th class="px-5 py-3">Firm</th><th class="px-5 py-3">Status</th><th class="px-5 py-3">Primary Branch</th><th class="px-5 py-3">Firm Admin</th><th class="px-5 py-3 text-right">Branches</th><th class="px-5 py-3 text-right">Users</th><th class="px-5 py-3 text-right">FY</th><th class="px-5 py-3 text-right">Services</th><th class="px-5 py-3">Action</th></tr></thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for firm in firms %}
|
||||
<tr class="hover:bg-slate-50">
|
||||
<td class="px-5 py-3 font-semibold text-slate-900">{{ firm.code }}</td>
|
||||
<td class="px-5 py-3"><div class="font-medium text-slate-900">{{ firm.name }}</div><div class="text-xs text-slate-500">{{ firm.firm_type or '-' }}{% if firm.contact_email %} > {{ firm.contact_email }}{% endif %}</div></td>
|
||||
<td class="px-5 py-3"><span class="rounded-full px-2.5 py-1 text-xs font-semibold {% if firm.is_active %}bg-emerald-50 text-emerald-700{% else %}bg-slate-100 text-slate-600{% endif %}">{% if firm.is_active %}Active{% else %}Inactive{% endif %}</span></td>
|
||||
<td class="px-5 py-3"><div class="font-medium text-slate-900">{{ firm.name }}</div><div class="text-xs text-slate-500">{{ firm.firm_type or '-' }}{% if firm.contact_email %} > {{ firm.contact_email }}{% endif %}</div></td>
|
||||
<td class="px-5 py-3"><span class="rounded-full px-2.5 py-1 text-xs font-semibold {% if firm.is_active %}bg-emerald-50 text-emerald-700{% else %}bg-slate-100 text-slate-600{% endif %}">{% if firm.is_active %}Active{% else %}Inactive{% endif %}</span>{% if firm.pending_invites %}<div class="mt-1 text-xs font-semibold text-amber-700">{{ firm.pending_invites }} invite pending</div>{% endif %}</td>
|
||||
<td class="px-5 py-3 text-slate-700">{{ firm.primary_branch }}</td>
|
||||
<td class="px-5 py-3 text-slate-700">{{ firm.primary_admin }}</td>
|
||||
<td class="px-5 py-3 text-right font-semibold">{{ firm.branch_count }}</td>
|
||||
<td class="px-5 py-3 text-right font-semibold">{{ firm.user_count }}</td>
|
||||
<td class="px-5 py-3 text-right font-semibold">{{ firm.fy_count }}</td>
|
||||
<td class="px-5 py-3 text-right font-semibold">{{ firm.selected_service_count }}</td>
|
||||
<td class="px-5 py-3"><a href="/system-settings/tenants" class="text-sm font-semibold text-brand-700 hover:text-brand-900">Open Settings</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="7" class="px-5 py-8 text-center text-slate-500">No firms found.</td></tr>
|
||||
<tr><td colspan="10" class="px-5 py-8 text-center text-slate-500">No firms found.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+27
-18
@@ -1,27 +1,36 @@
|
||||
<div class="space-y-6">
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Total Firms</div><div class="mt-2 text-3xl font-bold text-slate-900">{{ overview.total_firms }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Active Firms</div><div class="mt-2 text-3xl font-bold text-emerald-700">{{ overview.active_firms }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Pending Invites</div><div class="mt-2 text-3xl font-bold text-amber-700">{{ overview.pending_invites }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Platform SMTP</div><div class="mt-2 text-lg font-bold {% if overview.smtp.configured and overview.smtp.active %}text-emerald-700{% else %}text-amber-700{% endif %}">{% if overview.smtp.configured and overview.smtp.active %}Ready{% else %}Needs Setup{% endif %}</div><div class="mt-1 text-xs text-slate-500">{{ overview.smtp.message }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Total Firms</div><div class="mt-2 text-3xl font-bold text-slate-900">{{ overview.total_firms }}</div><div class="mt-1 text-xs text-slate-500">{{ overview.active_firms }} active / {{ overview.inactive_firms }} inactive</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Setup Incomplete</div><div class="mt-2 text-3xl font-bold {% if overview.setup_incomplete_firms %}text-amber-700{% else %}text-emerald-700{% endif %}">{{ overview.setup_incomplete_firms }}</div><div class="mt-1 text-xs text-slate-500">Firms needing setup action</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Pending Admin Invites</div><div class="mt-2 text-3xl font-bold {% if overview.pending_invites %}text-amber-700{% else %}text-emerald-700{% endif %}">{{ overview.pending_invites }}</div><div class="mt-1 text-xs text-slate-500">Firm Admin password/invite pending</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Platform SMTP</div><div class="mt-2 text-lg font-bold {% if overview.smtp.configured and overview.smtp.active and overview.smtp.send_auth_emails %}text-emerald-700{% else %}text-amber-700{% endif %}">{% if overview.smtp.configured and overview.smtp.active and overview.smtp.send_auth_emails %}Ready{% else %}Needs Setup{% endif %}</div><div class="mt-1 text-xs text-slate-500">{{ overview.smtp.message }}</div></div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Service Catalogue</div><div class="mt-2 text-2xl font-bold text-slate-900">{{ overview.service_count }}</div><div class="mt-1 text-xs text-slate-500">{{ overview.active_service_count }} active services</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Default Task Templates</div><div class="mt-2 text-2xl font-bold text-slate-900">{{ overview.default_task_count }}</div><div class="mt-1 text-xs text-slate-500">{{ overview.due_rule_count }} due-date rules</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Storage Used</div><div class="mt-2 text-2xl font-bold text-slate-900">{{ overview.storage.used_display }}</div><div class="mt-1 text-xs text-slate-500">Branding files: {{ overview.storage.branding_file_count }}</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Inactive Firms</div><div class="mt-2 text-2xl font-bold text-slate-900">{{ overview.inactive_firms }}</div><div class="mt-1 text-xs text-slate-500">Use Firms tab for details</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Default Task Templates</div><div class="mt-2 text-2xl font-bold text-slate-900">{{ overview.default_task_count }}</div><div class="mt-1 text-xs text-slate-500">{{ overview.services_without_tasks }} services without tasks</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Storage</div><div class="mt-2 text-2xl font-bold {% if overview.storage.storage_root_exists and overview.storage.branding_root_exists %}text-emerald-700{% else %}text-amber-700{% endif %}">{{ overview.storage.status_label }}</div><div class="mt-1 text-xs text-slate-500">{{ overview.storage.used_display }} used</div></div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Billing Readiness</div><div class="mt-2 text-2xl font-bold text-slate-900">{{ overview.billing.active_subscription_count }}</div><div class="mt-1 text-xs text-slate-500">Active platform subscriptions</div></div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-3">
|
||||
<a href="/wizards/system/firm/new" class="rounded-3xl border border-brand-100 bg-white p-5 shadow-soft hover:border-brand-300 hover:bg-brand-50">
|
||||
<div class="text-sm font-bold text-brand-700">Create Firm Wizard</div><p class="mt-2 text-sm text-slate-600">Create tenant, branch, Firm Admin, employee link and invite.</p>
|
||||
</a>
|
||||
<a href="/email/platform-smtp" class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft hover:bg-slate-50">
|
||||
<div class="text-sm font-bold text-slate-900">Platform SMTP</div><p class="mt-2 text-sm text-slate-600">Configure system email for invites and platform notifications.</p>
|
||||
</a>
|
||||
<a href="/system-admin/dashboard?tab=reports" class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft hover:bg-slate-50">
|
||||
<div class="text-sm font-bold text-slate-900">Reports Centre</div><p class="mt-2 text-sm text-slate-600">Open setup, SMTP, storage and audit report cards.</p>
|
||||
</a>
|
||||
<div class="grid gap-6 xl:grid-cols-3">
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft xl:col-span-2">
|
||||
<div class="flex items-center justify-between gap-3"><div><h2 class="text-lg font-bold text-slate-900">Needs Attention</h2><p class="text-sm text-slate-500">Action-first list for System Admin.</p></div><a href="/system-admin/dashboard?tab=firm-setup-health" class="text-sm font-semibold text-brand-700">Open setup health</a></div>
|
||||
<div class="mt-4 grid gap-3 md:grid-cols-2">
|
||||
{% for item in overview.needs_attention %}
|
||||
<a href="/system-admin/dashboard?tab={{ item.tab }}" class="rounded-2xl border border-amber-100 bg-amber-50 p-4 hover:border-amber-200"><div class="flex items-center justify-between gap-3"><span class="font-semibold text-amber-900">{{ item.label }}</span><span class="rounded-full bg-white px-2.5 py-1 text-xs font-bold text-amber-700">{{ item.count }}</span></div></a>
|
||||
{% else %}
|
||||
<div class="rounded-2xl border border-emerald-100 bg-emerald-50 p-4 text-sm font-semibold text-emerald-700 md:col-span-2">No immediate platform setup issues found.</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft">
|
||||
<h2 class="text-lg font-bold text-slate-900">Quick Actions</h2>
|
||||
<div class="mt-4 space-y-3">
|
||||
<a href="/wizards/system/firm/new" class="block rounded-2xl bg-brand-600 px-4 py-3 text-sm font-semibold text-white hover:bg-brand-700">+ Create Firm Wizard</a>
|
||||
<a href="/email/platform-smtp" class="block rounded-2xl border border-slate-200 px-4 py-3 text-sm font-semibold text-slate-700 hover:bg-slate-50">Platform SMTP Settings</a>
|
||||
<a href="/system-admin/dashboard?tab=reports" class="block rounded-2xl border border-slate-200 px-4 py-3 text-sm font-semibold text-slate-700 hover:bg-slate-50">Reports Centre</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+15
-8
@@ -1,9 +1,16 @@
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
{% for card in report_cards %}
|
||||
<a href="{{ card.href }}" class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft hover:border-brand-200 hover:bg-brand-50">
|
||||
<div class="flex items-start justify-between gap-3"><h3 class="font-bold text-slate-900">{{ card.title }}</h3><span class="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-semibold text-slate-700">{{ card.metric }}</span></div>
|
||||
<p class="mt-3 text-sm text-slate-600">{{ card.description }}</p>
|
||||
<div class="mt-4 text-sm font-semibold text-brand-700">View report</div>
|
||||
</a>
|
||||
<div class="space-y-6">
|
||||
{% for group_name, cards in report_groups.items() %}
|
||||
<div>
|
||||
<h2 class="mb-3 text-lg font-bold text-slate-900">{{ group_name }}</h2>
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
{% for card in cards %}
|
||||
<a href="{{ card.href }}" class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft hover:border-brand-200 hover:bg-brand-50">
|
||||
<div class="flex items-start justify-between gap-3"><h3 class="font-bold text-slate-900">{{ card.title }}</h3><span class="rounded-full bg-slate-100 px-2.5 py-1 text-xs font-semibold text-slate-700">{{ card.metric }}</span></div>
|
||||
<p class="mt-3 text-sm text-slate-600">{{ card.description }}</p>
|
||||
<div class="mt-4 text-sm font-semibold text-brand-700">View report</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
+6
-2
@@ -3,10 +3,14 @@
|
||||
<h2 class="text-lg font-bold text-slate-900">Platform SMTP</h2>
|
||||
<p class="mt-1 text-sm text-slate-500">Used for Firm Creation Wizard invites and system-level emails.</p>
|
||||
<dl class="mt-6 grid gap-4 md:grid-cols-2">
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><dt class="text-xs font-semibold uppercase text-slate-500">Status</dt><dd class="mt-1 font-bold {% if smtp.configured and smtp.active %}text-emerald-700{% else %}text-amber-700{% endif %}">{% if smtp.configured and smtp.active %}Ready{% else %}Needs Setup{% endif %}</dd></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><dt class="text-xs font-semibold uppercase text-slate-500">Status</dt><dd class="mt-1 font-bold {% if smtp.configured and smtp.active and smtp.send_auth_emails %}text-emerald-700{% else %}text-amber-700{% endif %}">{% if smtp.configured and smtp.active and smtp.send_auth_emails %}Ready{% else %}Needs Setup{% endif %}</dd></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><dt class="text-xs font-semibold uppercase text-slate-500">Host / Port</dt><dd class="mt-1 font-semibold text-slate-900">{{ smtp.host or '-' }}{% if smtp.port %}:{{ smtp.port }}{% endif %}</dd></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><dt class="text-xs font-semibold uppercase text-slate-500">From Email</dt><dd class="mt-1 font-semibold text-slate-900">{{ smtp.from_email or '-' }}</dd></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><dt class="text-xs font-semibold uppercase text-slate-500">From Name</dt><dd class="mt-1 font-semibold text-slate-900">{{ smtp.from_name or '-' }}</dd></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><dt class="text-xs font-semibold uppercase text-slate-500">Reply To</dt><dd class="mt-1 font-semibold text-slate-900">{{ smtp.reply_to_email or '-' }}</dd></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><dt class="text-xs font-semibold uppercase text-slate-500">Security</dt><dd class="mt-1 font-semibold text-slate-900">{{ smtp.security or '-' }}</dd></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><dt class="text-xs font-semibold uppercase text-slate-500">Active</dt><dd class="mt-1 font-semibold text-slate-900">{% if smtp.active %}Yes{% else %}No{% endif %}</dd></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><dt class="text-xs font-semibold uppercase text-slate-500">Auth Emails</dt><dd class="mt-1 font-semibold text-slate-900">{% if smtp.send_auth_emails %}Enabled{% else %}Disabled{% endif %}</dd></div>
|
||||
</dl>
|
||||
<p class="mt-4 text-sm text-slate-600">{{ smtp.message }}</p>
|
||||
</div>
|
||||
@@ -15,4 +19,4 @@
|
||||
<p class="mt-2 text-sm text-slate-500">Configure or test platform SMTP from the existing settings page.</p>
|
||||
<a href="/email/platform-smtp" class="mt-5 inline-flex rounded-xl bg-brand-600 px-4 py-2 text-sm font-semibold text-white hover:bg-brand-700">Open Platform SMTP</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+3
-3
@@ -8,8 +8,8 @@
|
||||
<div class="rounded-3xl border border-slate-200 bg-white p-6 shadow-soft">
|
||||
<h2 class="text-lg font-bold text-slate-900">Persistent Storage Paths</h2>
|
||||
<div class="mt-4 space-y-3 text-sm">
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><span class="font-semibold text-slate-700">Storage root:</span> <code class="text-slate-800">{{ storage.storage_root }}</code></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><span class="font-semibold text-slate-700">Branding root:</span> <code class="text-slate-800">{{ storage.branding_root }}</code></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><span class="font-semibold text-slate-700">Storage root:</span> <code class="text-slate-800">{{ storage.storage_root }}</code> <span class="ml-2 {% if storage.storage_root_exists %}text-emerald-700{% else %}text-red-700{% endif %}">{% if storage.storage_root_exists %}Ready{% else %}Missing{% endif %}</span></div>
|
||||
<div class="rounded-2xl bg-slate-50 p-4"><span class="font-semibold text-slate-700">Branding root:</span> <code class="text-slate-800">{{ storage.branding_root }}</code> <span class="ml-2 {% if storage.branding_root_exists %}text-emerald-700{% else %}text-red-700{% endif %}">{% if storage.branding_root_exists %}Ready{% else %}Missing{% endif %}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+15
-8
@@ -1,9 +1,16 @@
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
{% for card in wizard_cards %}
|
||||
<a href="{{ card.href }}" class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft hover:border-brand-200 hover:bg-brand-50">
|
||||
<h3 class="font-bold text-slate-900">{{ card.title }}</h3>
|
||||
<p class="mt-3 text-sm text-slate-600">{{ card.description }}</p>
|
||||
<div class="mt-4 inline-flex rounded-xl bg-brand-600 px-3 py-2 text-sm font-semibold text-white">{{ card.action }}</div>
|
||||
</a>
|
||||
<div class="space-y-6">
|
||||
{% for group_name, cards in wizard_groups.items() %}
|
||||
<div>
|
||||
<h2 class="mb-3 text-lg font-bold text-slate-900">{{ group_name }}</h2>
|
||||
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
{% for card in cards %}
|
||||
<a href="{{ card.href }}" class="rounded-3xl border border-slate-200 bg-white p-5 shadow-soft hover:border-brand-200 hover:bg-brand-50">
|
||||
<h3 class="font-bold text-slate-900">{{ card.title }}</h3>
|
||||
<p class="mt-3 text-sm text-slate-600">{{ card.description }}</p>
|
||||
<div class="mt-4 inline-flex rounded-xl bg-brand-600 px-3 py-2 text-sm font-semibold text-white">{{ card.action }}</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user