mirror of
https://github.com/magicbug/Cloudlog
synced 2026-08-13 17:49:35 -04:00
Add new gridsquares by band to monthly report
Introduces per-band breakdown of new gridsquares in the monthly report, including model, controller, and view changes. The report now displays new gridsquares grouped by band, as well as improved details for new DXCCs and satellite activity breakdowns.
This commit is contained in:
parent
4e657094f8
commit
45528ee293
3 changed files with 287 additions and 94 deletions
|
|
@ -121,6 +121,7 @@ class Monthlyreport extends CI_Controller {
|
|||
'new_countries_satellite' => $report['new_dxcc_satellite'],
|
||||
'new_countries_eme' => $report['new_dxcc_eme'],
|
||||
'new_gridsquares' => $report['new_grids'],
|
||||
'new_gridsquares_by_band' => $report['new_grids_by_band'],
|
||||
'new_gridsquares_hf' => $report['new_grids_hf'],
|
||||
'new_gridsquares_satellite' => $report['new_grids_satellite'],
|
||||
'new_gridsquares_eme' => $report['new_grids_eme'],
|
||||
|
|
@ -129,7 +130,8 @@ class Monthlyreport extends CI_Controller {
|
|||
'bands_used' => $report['bands'],
|
||||
'continents_worked' => $report['continents'],
|
||||
'satellite_qsos' => $report['satellite_qsos'],
|
||||
'eme_qsos' => $report['eme_qsos']
|
||||
'eme_qsos' => $report['eme_qsos'],
|
||||
'satellite_breakdown' => $report['satellite_breakdown']
|
||||
),
|
||||
'statistics' => array(
|
||||
'qsos_per_day' => round($report['total_qsos'] / date('t', strtotime("$year-$month-01")), 2),
|
||||
|
|
@ -202,7 +204,15 @@ class Monthlyreport extends CI_Controller {
|
|||
foreach ($report['new_dxcc_by_band'] as $band => $dxcc_list) {
|
||||
$text .= "\n{$band} (" . count($dxcc_list) . " new):\n";
|
||||
foreach ($dxcc_list as $dxcc) {
|
||||
$text .= " - {$dxcc['name']}\n";
|
||||
$text .= " - {$dxcc['name']}";
|
||||
if (!empty($dxcc['callsign'])) {
|
||||
$text .= " (worked {$dxcc['callsign']}";
|
||||
if (!empty($dxcc['mode'])) {
|
||||
$text .= " on {$dxcc['mode']}";
|
||||
}
|
||||
$text .= ")";
|
||||
}
|
||||
$text .= "\n";
|
||||
}
|
||||
}
|
||||
$text .= "\n";
|
||||
|
|
@ -212,7 +222,15 @@ class Monthlyreport extends CI_Controller {
|
|||
$text .= "NEW COUNTRIES VIA SATELLITE\n";
|
||||
$text .= "---------------------------\n";
|
||||
foreach ($report['new_dxcc_satellite'] as $dxcc) {
|
||||
$text .= "- {$dxcc['name']}\n";
|
||||
$text .= "- {$dxcc['name']}";
|
||||
if (!empty($dxcc['callsign'])) {
|
||||
$text .= " (worked {$dxcc['callsign']}";
|
||||
if (!empty($dxcc['mode'])) {
|
||||
$text .= " on {$dxcc['mode']}";
|
||||
}
|
||||
$text .= ")";
|
||||
}
|
||||
$text .= "\n";
|
||||
}
|
||||
$text .= "\n";
|
||||
}
|
||||
|
|
@ -221,7 +239,15 @@ class Monthlyreport extends CI_Controller {
|
|||
$text .= "NEW COUNTRIES VIA EME (MOONBOUNCE)\n";
|
||||
$text .= "----------------------------------\n";
|
||||
foreach ($report['new_dxcc_eme'] as $dxcc) {
|
||||
$text .= "- {$dxcc['name']}\n";
|
||||
$text .= "- {$dxcc['name']}";
|
||||
if (!empty($dxcc['callsign'])) {
|
||||
$text .= " (worked {$dxcc['callsign']}";
|
||||
if (!empty($dxcc['mode'])) {
|
||||
$text .= " on {$dxcc['mode']}";
|
||||
}
|
||||
$text .= ")";
|
||||
}
|
||||
$text .= "\n";
|
||||
}
|
||||
$text .= "\n";
|
||||
}
|
||||
|
|
@ -230,37 +256,27 @@ class Monthlyreport extends CI_Controller {
|
|||
$text .= "NEW GRIDSQUARES THIS MONTH\n";
|
||||
$text .= "--------------------------\n";
|
||||
|
||||
if (count($report['new_grids_hf']) > 0) {
|
||||
$text .= "HF/VHF Terrestrial (" . count($report['new_grids_hf']) . "):\n";
|
||||
$grids_per_line = 10;
|
||||
$grid_list = array_column($report['new_grids_hf'], 'grid');
|
||||
for ($i = 0; $i < count($grid_list); $i += $grids_per_line) {
|
||||
$text .= implode(', ', array_slice($grid_list, $i, $grids_per_line)) . "\n";
|
||||
}
|
||||
$text .= "\n";
|
||||
}
|
||||
|
||||
if (count($report['new_grids_satellite']) > 0) {
|
||||
$text .= "Satellite (" . count($report['new_grids_satellite']) . "):\n";
|
||||
$grids_per_line = 10;
|
||||
$grid_list = array_column($report['new_grids_satellite'], 'grid');
|
||||
for ($i = 0; $i < count($grid_list); $i += $grids_per_line) {
|
||||
$text .= implode(', ', array_slice($grid_list, $i, $grids_per_line)) . "\n";
|
||||
}
|
||||
$text .= "\n";
|
||||
}
|
||||
|
||||
if (count($report['new_grids_eme']) > 0) {
|
||||
$text .= "EME (Moonbounce) (" . count($report['new_grids_eme']) . "):\n";
|
||||
$grids_per_line = 10;
|
||||
$grid_list = array_column($report['new_grids_eme'], 'grid');
|
||||
for ($i = 0; $i < count($grid_list); $i += $grids_per_line) {
|
||||
$text .= implode(', ', array_slice($grid_list, $i, $grids_per_line)) . "\n";
|
||||
if (!empty($report['new_grids_by_band'])) {
|
||||
foreach ($report['new_grids_by_band'] as $band => $grids_list) {
|
||||
$text .= "\n{$band} (" . count($grids_list) . " new):\n";
|
||||
foreach ($grids_list as $grid) {
|
||||
$text .= " {$grid['grid']}";
|
||||
if (!empty($grid['callsign'])) {
|
||||
$text .= " ({$grid['callsign']}";
|
||||
if (!empty($grid['satellite'])) {
|
||||
$text .= " via {$grid['satellite']}";
|
||||
}
|
||||
if (!empty($grid['mode'])) {
|
||||
$text .= " - {$grid['mode']}";
|
||||
}
|
||||
$text .= ")";
|
||||
}
|
||||
$text .= "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$text .= "MODES USED\n";
|
||||
$text .= "\n";
|
||||
} $text .= "MODES USED\n";
|
||||
$text .= "----------\n";
|
||||
foreach ($report['modes'] as $mode => $count) {
|
||||
$text .= sprintf("%-10s %d QSOs\n", $mode, $count);
|
||||
|
|
@ -295,7 +311,15 @@ class Monthlyreport extends CI_Controller {
|
|||
if ($report['satellite_qsos'] > 0) {
|
||||
$text .= "SATELLITE ACTIVITY\n";
|
||||
$text .= "------------------\n";
|
||||
$text .= "Total Satellite QSOs: {$report['satellite_qsos']}\n\n";
|
||||
$text .= "Total Satellite QSOs: {$report['satellite_qsos']}\n";
|
||||
|
||||
if (!empty($report['satellite_breakdown'])) {
|
||||
$text .= "\nQSOs per Satellite:\n";
|
||||
foreach ($report['satellite_breakdown'] as $sat) {
|
||||
$text .= sprintf(" %-20s %3d QSOs (%s%%)\n", $sat['satellite'], $sat['qso_count'], $sat['percentage']);
|
||||
}
|
||||
}
|
||||
$text .= "\n";
|
||||
}
|
||||
|
||||
if ($report['eme_qsos'] > 0) {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ class Monthlyreport_model extends CI_Model {
|
|||
'new_dxcc_satellite' => $this->get_new_dxcc_by_prop($logbooks_locations_array, $start_date, $end_date, $prev_month_end, 'SAT'),
|
||||
'new_dxcc_eme' => $this->get_new_dxcc_by_prop($logbooks_locations_array, $start_date, $end_date, $prev_month_end, 'EME'),
|
||||
'new_grids' => $this->get_new_gridsquares($logbooks_locations_array, $start_date, $end_date, $prev_month_end),
|
||||
'new_grids_by_band' => $this->get_new_gridsquares_by_band($logbooks_locations_array, $start_date, $end_date, $prev_month_end),
|
||||
'new_grids_satellite' => $this->get_new_gridsquares_by_prop($logbooks_locations_array, $start_date, $end_date, $prev_month_end, 'SAT'),
|
||||
'new_grids_eme' => $this->get_new_gridsquares_by_prop($logbooks_locations_array, $start_date, $end_date, $prev_month_end, 'EME'),
|
||||
'new_grids_hf' => $this->get_new_gridsquares_hf($logbooks_locations_array, $start_date, $end_date, $prev_month_end),
|
||||
|
|
@ -432,6 +433,152 @@ class Monthlyreport_model extends CI_Model {
|
|||
return $new_dxcc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get new gridsquares by band
|
||||
*/
|
||||
private function get_new_gridsquares_by_band($locations, $start_date, $end_date, $prev_month_end) {
|
||||
$new_grids_by_band = array();
|
||||
|
||||
// Get all bands worked this month, plus propagation mode to separate SAT/EME
|
||||
$this->db->distinct();
|
||||
$this->db->select('COL_BAND, COL_PROP_MODE');
|
||||
$this->db->from($this->config->item('table_name'));
|
||||
$this->db->where_in('station_id', $locations);
|
||||
$this->db->where('COL_TIME_ON >=', $start_date);
|
||||
$this->db->where('COL_TIME_ON <=', $end_date);
|
||||
$this->db->where('COL_BAND IS NOT NULL');
|
||||
$this->db->where('COL_BAND !=', '');
|
||||
$bands_query = $this->db->get();
|
||||
|
||||
foreach ($bands_query->result() as $band_row) {
|
||||
$band = $band_row->COL_BAND;
|
||||
$prop_mode = $band_row->COL_PROP_MODE;
|
||||
|
||||
// Group SAT and EME separately, not by frequency band
|
||||
if ($prop_mode == 'SAT') {
|
||||
$band_key = 'Satellite';
|
||||
} elseif ($prop_mode == 'EME') {
|
||||
$band_key = 'EME';
|
||||
} else {
|
||||
$band_key = $band;
|
||||
}
|
||||
|
||||
if (!isset($new_grids_by_band[$band_key])) {
|
||||
$new_grids_by_band[$band_key] = array();
|
||||
}
|
||||
|
||||
// Get all gridsquares worked on this band/prop this month
|
||||
$this->db->select('COL_CALL, COL_GRIDSQUARE, COL_VUCC_GRIDS, COL_TIME_ON, COL_MODE, COL_SUBMODE, COL_SAT_NAME');
|
||||
$this->db->from($this->config->item('table_name'));
|
||||
$this->db->where_in('station_id', $locations);
|
||||
$this->db->where('COL_TIME_ON >=', $start_date);
|
||||
$this->db->where('COL_TIME_ON <=', $end_date);
|
||||
$this->db->where('COL_BAND', $band);
|
||||
|
||||
// Filter by propagation mode
|
||||
if ($prop_mode) {
|
||||
$this->db->where('COL_PROP_MODE', $prop_mode);
|
||||
} else {
|
||||
// For terrestrial, exclude SAT and EME
|
||||
$this->db->where('(COL_PROP_MODE IS NULL OR (COL_PROP_MODE != "SAT" AND COL_PROP_MODE != "EME"))', NULL, FALSE);
|
||||
}
|
||||
|
||||
$this->db->order_by('COL_TIME_ON', 'ASC');
|
||||
|
||||
$query = $this->db->get();
|
||||
$grid_data = array();
|
||||
|
||||
foreach ($query->result() as $row) {
|
||||
$mode = !empty($row->COL_SUBMODE) ? $row->COL_SUBMODE : $row->COL_MODE;
|
||||
|
||||
// Process main gridsquare (first 4 characters only)
|
||||
if (!empty($row->COL_GRIDSQUARE) && strlen($row->COL_GRIDSQUARE) >= 4) {
|
||||
$grid_4char = strtoupper(substr($row->COL_GRIDSQUARE, 0, 4));
|
||||
if (!isset($grid_data[$grid_4char])) {
|
||||
$grid_data[$grid_4char] = array(
|
||||
'callsign' => $row->COL_CALL,
|
||||
'satellite' => !empty($row->COL_SAT_NAME) ? $row->COL_SAT_NAME : '',
|
||||
'mode' => $mode,
|
||||
'time' => $row->COL_TIME_ON
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Process VUCC grids (first 4 characters only)
|
||||
if (!empty($row->COL_VUCC_GRIDS)) {
|
||||
$vucc_grids = explode(',', $row->COL_VUCC_GRIDS);
|
||||
foreach ($vucc_grids as $grid) {
|
||||
$grid = trim($grid);
|
||||
if (strlen($grid) >= 4) {
|
||||
$grid_4char = strtoupper(substr($grid, 0, 4));
|
||||
if (!isset($grid_data[$grid_4char])) {
|
||||
$grid_data[$grid_4char] = array(
|
||||
'callsign' => $row->COL_CALL,
|
||||
'satellite' => !empty($row->COL_SAT_NAME) ? $row->COL_SAT_NAME : '',
|
||||
'mode' => $mode,
|
||||
'time' => $row->COL_TIME_ON
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check each grid to see if it was worked before on this band/prop
|
||||
foreach ($grid_data as $grid => $data) {
|
||||
// Check if this grid was worked before this month on this band/prop
|
||||
$this->db->select('COUNT(*) as count');
|
||||
$this->db->from($this->config->item('table_name'));
|
||||
$this->db->where_in('station_id', $locations);
|
||||
$this->db->where('COL_BAND', $band);
|
||||
if ($prop_mode) {
|
||||
$this->db->where('COL_PROP_MODE', $prop_mode);
|
||||
} else {
|
||||
$this->db->where('(COL_PROP_MODE IS NULL OR (COL_PROP_MODE != "SAT" AND COL_PROP_MODE != "EME"))', NULL, FALSE);
|
||||
}
|
||||
$this->db->where('COL_TIME_ON <', $start_date);
|
||||
$this->db->group_start();
|
||||
$this->db->where('UPPER(SUBSTR(COL_GRIDSQUARE, 1, 4)) =', $grid);
|
||||
$this->db->or_like('COL_VUCC_GRIDS', $grid);
|
||||
$this->db->group_end();
|
||||
|
||||
$prev_query = $this->db->get();
|
||||
$prev_row = $prev_query->row();
|
||||
|
||||
// If not worked before on this band/prop, it's new
|
||||
if ($prev_row->count == 0) {
|
||||
// Avoid duplicates in the same band_key
|
||||
$already_added = false;
|
||||
foreach ($new_grids_by_band[$band_key] as $existing) {
|
||||
if ($existing['grid'] == $grid) {
|
||||
$already_added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$already_added) {
|
||||
$new_grids_by_band[$band_key][] = array(
|
||||
'grid' => $grid,
|
||||
'callsign' => $data['callsign'],
|
||||
'satellite' => $data['satellite'],
|
||||
'mode' => $data['mode']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove bands with no new grids
|
||||
$new_grids_by_band = array_filter($new_grids_by_band, function($arr) {
|
||||
return !empty($arr);
|
||||
});
|
||||
|
||||
// Sort bands in proper amateur radio order
|
||||
$new_grids_by_band = $this->sort_bands_array($new_grids_by_band);
|
||||
|
||||
return $new_grids_by_band;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get new gridsquares worked this month (first time ever) - All modes
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -213,77 +213,99 @@
|
|||
|
||||
<?php } // End of new countries section ?>
|
||||
|
||||
<!-- New Gridsquares -->
|
||||
<?php if (count($report['new_grids']) > 0) { ?>
|
||||
<!-- New Gridsquares by Band -->
|
||||
<?php
|
||||
// Filter out Satellite and EME from band list to see if there's anything to display
|
||||
$terrestrial_grids = array();
|
||||
if (!empty($report['new_grids_by_band'])) {
|
||||
foreach ($report['new_grids_by_band'] as $band => $grids_list) {
|
||||
if ($band != 'Satellite' && $band != 'EME') {
|
||||
$terrestrial_grids[$band] = $grids_list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($terrestrial_grids)) {
|
||||
?>
|
||||
<div class="card mb-4 border-info">
|
||||
<div class="card-header bg-info bg-opacity-10 border-info">
|
||||
<h5 class="mb-0"><i class="fas fa-th text-info"></i> New Gridsquares This Month</h5>
|
||||
<h5 class="mb-0"><i class="fas fa-th text-info"></i> New Gridsquares by Band</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<?php if (count($report['new_grids_hf']) > 0) { ?>
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted"><i class="fas fa-tower-broadcast"></i> HF/VHF Terrestrial (<?php echo count($report['new_grids_hf']); ?>)</h6>
|
||||
<div class="row">
|
||||
<?php foreach ($report['new_grids_hf'] as $grid) { ?>
|
||||
<div class="col-md-4 col-lg-3 mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-primary me-2"><?php echo $grid['grid']; ?></span>
|
||||
<small class="text-muted">
|
||||
<?php echo $grid['callsign']; ?>
|
||||
<?php if (!empty($grid['mode'])) echo ' • ' . $grid['mode']; ?>
|
||||
</small>
|
||||
<?php
|
||||
// Bands already sorted in model
|
||||
foreach ($terrestrial_grids as $band => $grids_list) {
|
||||
$icon = '<i class="fas fa-signal"></i>';
|
||||
?>
|
||||
<div class="mb-3">
|
||||
<h6 class="text-muted"><?php echo $icon; ?> <?php echo $band; ?> (<?php echo count($grids_list); ?> new)</h6>
|
||||
<div class="row">
|
||||
<?php foreach ($grids_list as $grid) { ?>
|
||||
<div class="col-md-4 col-lg-3 mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-primary me-2"><?php echo $grid['grid']; ?></span>
|
||||
<small class="text-muted">
|
||||
<?php echo $grid['callsign']; ?>
|
||||
<?php if (!empty($grid['mode'])) echo ' • ' . $grid['mode']; ?>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (count($report['new_grids_satellite']) > 0) { ?>
|
||||
<div class="mb-4">
|
||||
<h6 class="text-muted"><i class="fas fa-satellite-dish"></i> Satellite (<?php echo count($report['new_grids_satellite']); ?>)</h6>
|
||||
<div class="row">
|
||||
<?php foreach ($report['new_grids_satellite'] as $grid) { ?>
|
||||
<div class="col-md-6 col-lg-4 mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-success me-2"><?php echo $grid['grid']; ?></span>
|
||||
<small class="text-muted">
|
||||
<?php echo $grid['callsign']; ?>
|
||||
<?php if (!empty($grid['satellite'])) echo ' • ' . $grid['satellite']; ?>
|
||||
<?php if (!empty($grid['mode'])) echo ' • ' . $grid['mode']; ?>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (count($report['new_grids_eme']) > 0) { ?>
|
||||
<div class="mb-3">
|
||||
<h6 class="text-muted"><i class="fas fa-moon"></i> EME (Moonbounce) (<?php echo count($report['new_grids_eme']); ?>)</h6>
|
||||
<div class="row">
|
||||
<?php foreach ($report['new_grids_eme'] as $grid) { ?>
|
||||
<div class="col-md-4 col-lg-3 mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-warning text-dark me-2"><?php echo $grid['grid']; ?></span>
|
||||
<small class="text-muted">
|
||||
<?php echo $grid['callsign']; ?>
|
||||
<?php if (!empty($grid['mode'])) echo ' • ' . $grid['mode']; ?>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<!-- Modes and Bands -->
|
||||
<!-- New Gridsquares via Satellite -->
|
||||
<?php if (count($report['new_grids_satellite']) > 0) { ?>
|
||||
<div class="card mb-4 border-info">
|
||||
<div class="card-header bg-info bg-opacity-10 border-info">
|
||||
<h5 class="mb-0"><i class="fas fa-satellite-dish text-info"></i> New Gridsquares via Satellite (<?php echo count($report['new_grids_satellite']); ?>)</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<?php foreach ($report['new_grids_satellite'] as $grid) { ?>
|
||||
<div class="col-md-6 col-lg-4 mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-success me-2"><?php echo $grid['grid']; ?></span>
|
||||
<small class="text-muted">
|
||||
<?php echo $grid['callsign']; ?>
|
||||
<?php if (!empty($grid['satellite'])) echo ' • ' . $grid['satellite']; ?>
|
||||
<?php if (!empty($grid['mode'])) echo ' • ' . $grid['mode']; ?>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
|
||||
<!-- New Gridsquares via EME -->
|
||||
<?php if (count($report['new_grids_eme']) > 0) { ?>
|
||||
<div class="card mb-4 border-info">
|
||||
<div class="card-header bg-info bg-opacity-10 border-info">
|
||||
<h5 class="mb-0"><i class="fas fa-moon text-info"></i> New Gridsquares via EME (Moonbounce) (<?php echo count($report['new_grids_eme']); ?>)</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<?php foreach ($report['new_grids_eme'] as $grid) { ?>
|
||||
<div class="col-md-4 col-lg-3 mb-2">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge bg-warning text-dark me-2"><?php echo $grid['grid']; ?></span>
|
||||
<small class="text-muted">
|
||||
<?php echo $grid['callsign']; ?>
|
||||
<?php if (!empty($grid['mode'])) echo ' • ' . $grid['mode']; ?>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?> <!-- Modes and Bands -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="card h-100">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue