Added registrar statistics in CP reports menu

Also fixed small bug in view registrar
This commit is contained in:
Pinga 2024-11-23 13:22:42 +02:00
parent fca7e93306
commit b5e607fdb8
3 changed files with 95 additions and 5 deletions

View file

@ -313,11 +313,11 @@ class RegistrarsController extends Controller
if ($args) { if ($args) {
$args = trim(preg_replace('/\s+/', ' ', $args)); $args = trim(preg_replace('/\s+/', ' ', $args));
if (!preg_match('/^[a-zA-Z0-9\s]+$/', $args)) { if (!preg_match('/^[a-zA-Z0-9\s.\-]+$/', $args)) {
$this->container->get('flash')->addMessage('error', 'Invalid registrar'); $this->container->get('flash')->addMessage('error', 'Invalid registrar');
return $response->withHeader('Location', '/registrars')->withStatus(302); return $response->withHeader('Location', '/registrars')->withStatus(302);
} }
$registrar = $db->selectRow('SELECT * FROM registrar WHERE name = ?', $registrar = $db->selectRow('SELECT * FROM registrar WHERE name = ?',
[ $args ]); [ $args ]);

View file

@ -15,8 +15,42 @@ class ReportsController extends Controller
if ($_SESSION["auth_roles"] != 0) { if ($_SESSION["auth_roles"] != 0) {
return $response->withHeader('Location', '/dashboard')->withStatus(302); return $response->withHeader('Location', '/dashboard')->withStatus(302);
} }
$stats = [];
$currency = $_SESSION['_currency'] ?? 'USD';
$db = $this->container->get('db');
$totalDomains = $db->select('SELECT COUNT(name) as total FROM domain');
$numT = $totalDomains[0]['total'] ?? 1;
return view($response,'admin/reports/index.twig'); $registrars = $db->select('SELECT id, name FROM registrar');
foreach ($registrars as $registrar) {
$domainCount = $db->select(
'SELECT COUNT(name) as count FROM domain WHERE clid = ?',
[$registrar['id']]
);
$earnings = $db->select(
'SELECT SUM(amount) as amt FROM statement WHERE registrar_id = ? AND command <> "deposit"',
[$registrar['id']]
);
$stats[] = [
'id' => $registrar['id'],
'registrar' => $registrar['name'],
'currency' => $currency,
'number' => $domainCount[0]['count'] ?? 0,
'share' => number_format(($domainCount[0]['count'] ?? 0) / $numT * 100, 2),
'earnings' => $earnings[0]['amt'] ?? 0
];
}
usort($stats, function ($a, $b) {
return $b['share'] <=> $a['share'];
});
return view($response,'admin/reports/index.twig', [
'stats' => $stats
]);
} }
public function exportDomains(Request $request, Response $response) public function exportDomains(Request $request, Response $response)

View file

@ -20,11 +20,18 @@
<!-- Page title actions --> <!-- Page title actions -->
<div class="col-auto ms-auto d-print-none"> <div class="col-auto ms-auto d-print-none">
<div class="btn-list"> <div class="btn-list">
<a href="{{route('exportDomains')}}" class="btn btn-info d-none d-sm-inline-block"> <a href="#" class="btn btn-indigo d-none d-sm-inline-block" data-bs-toggle="modal" data-bs-target="#statsModal">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 3v18h18" /><path d="M9 9m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M19 7m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M14 15m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M10.16 10.62l2.34 2.88" /><path d="M15.088 13.328l2.837 -4.586" /></svg>
{{ __('Registrar Statistics') }}
</a>
<a href="{{route('exportDomains')}}" class="btn btn-lime d-none d-sm-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6c0 1.657 3.582 3 8 3s8 -1.343 8 -3s-3.582 -3 -8 -3s-8 1.343 -8 3" /><path d="M4 6v6c0 1.657 3.582 3 8 3c1.118 0 2.183 -.086 3.15 -.241" /><path d="M20 12v-6" /><path d="M4 12v6c0 1.657 3.582 3 8 3c.157 0 .312 -.002 .466 -.005" /><path d="M16 19h6" /><path d="M19 16l3 3l-3 3" /></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6c0 1.657 3.582 3 8 3s8 -1.343 8 -3s-3.582 -3 -8 -3s-8 1.343 -8 3" /><path d="M4 6v6c0 1.657 3.582 3 8 3c1.118 0 2.183 -.086 3.15 -.241" /><path d="M20 12v-6" /><path d="M4 12v6c0 1.657 3.582 3 8 3c.157 0 .312 -.002 .466 -.005" /><path d="M16 19h6" /><path d="M19 16l3 3l-3 3" /></svg>
{{ __('Export Domains') }} {{ __('Export Domains') }}
</a> </a>
<a href="{{route('exportDomains')}}" class="btn btn-info d-sm-none btn-icon" aria-label="{{ __('Export Domains') }}"> <a href="#" class="btn btn-indigo d-sm-none btn-icon" aria-label="{{ __('Registrar Statistics') }}" data-bs-toggle="modal" data-bs-target="#statsModal">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 3v18h18" /><path d="M9 9m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M19 7m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M14 15m-2 0a2 2 0 1 0 4 0a2 2 0 1 0 -4 0" /><path d="M10.16 10.62l2.34 2.88" /><path d="M15.088 13.328l2.837 -4.586" /></svg>
</a>
<a href="{{route('exportDomains')}}" class="btn btn-lime d-sm-none btn-icon" aria-label="{{ __('Export Domains') }}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6c0 1.657 3.582 3 8 3s8 -1.343 8 -3s-3.582 -3 -8 -3s-8 1.343 -8 3" /><path d="M4 6v6c0 1.657 3.582 3 8 3c1.118 0 2.183 -.086 3.15 -.241" /><path d="M20 12v-6" /><path d="M4 12v6c0 1.657 3.582 3 8 3c.157 0 .312 -.002 .466 -.005" /><path d="M16 19h6" /><path d="M19 16l3 3l-3 3" /></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M4 6c0 1.657 3.582 3 8 3s8 -1.343 8 -3s-3.582 -3 -8 -3s-8 1.343 -8 3" /><path d="M4 6v6c0 1.657 3.582 3 8 3c1.118 0 2.183 -.086 3.15 -.241" /><path d="M20 12v-6" /><path d="M4 12v6c0 1.657 3.582 3 8 3c.157 0 .312 -.002 .466 -.005" /><path d="M16 19h6" /><path d="M19 16l3 3l-3 3" /></svg>
</a> </a>
</div> </div>
@ -62,4 +69,53 @@
</div> </div>
{% include 'partials/footer.twig' %} {% include 'partials/footer.twig' %}
</div> </div>
<!-- Modal -->
<div class="modal fade" id="statsModal" tabindex="-1" aria-labelledby="statsModalLabel">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="statsModalLabel">{{ __('Registrar Statistics') }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
<table class="table table-vcenter">
<thead>
<tr>
<th>{{ __('Registrar') }}</th>
<th>{{ __('Number of Domains') }}</th>
<th>{{ __('Market Share') }}</th>
<th>{{ __('Earnings') }}</th>
</tr>
</thead>
<tbody>
{% for stat in stats %}
<tr>
<td>
<div>
<strong>{{ stat.registrar }}</strong>
<a href="/registrar/view/{{ stat.registrar }}" class="ms-1" aria-label="{{ __('Open website') }}" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<path d="M10 14a3.5 3.5 0 0 0 5 0l4 -4a3.5 3.5 0 0 0 -5 -5l-.5 .5" />
<path d="M14 10a3.5 3.5 0 0 0 -5 0l-4 4a3.5 3.5 0 0 0 5 5l.5 -.5" />
</svg>
</a>
</div>
</td>
<td class="text-muted">{{ stat.number }}</td>
<td class="text-muted">{{ stat.share }}%</td>
<td>{{ stat.earnings | number_format(2, '.', ',') }} {{ currency }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ __('Close') }}</button>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}