474 lines
25 KiB
PHP
474 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* admin/countries_settings.php
|
|
*
|
|
* Gestion des pays, de leur centre de carte/cadastre, des operateurs
|
|
* et des mappings bande 4G -> EARFCN par operateur.
|
|
*
|
|
* Cette version inclut la gestion des plages EARFCN par bande pour chaque
|
|
* operateur, car les operateurs utilisent des valeurs differentes meme
|
|
* sur la meme bande (ex: POST B20 != Tango B20).
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/admin_auth.php';
|
|
require_once __DIR__ . '/../lib/db.php';
|
|
require_once __DIR__ . '/_nav.php';
|
|
|
|
require_admin_auth();
|
|
$db = pdo();
|
|
$user = current_admin_username();
|
|
$successMessage = null;
|
|
$errorMessage = null;
|
|
|
|
function nullable_float(array $input, string $key): ?float
|
|
{
|
|
$value = trim((string)($input[$key] ?? ''));
|
|
if ($value === '') {
|
|
return null;
|
|
}
|
|
if (!is_numeric($value)) {
|
|
throw new InvalidArgumentException("$key doit etre un nombre.");
|
|
}
|
|
return (float)$value;
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
require_csrf_token();
|
|
$action = $_POST['action'] ?? '';
|
|
|
|
try {
|
|
if ($action === 'save_country') {
|
|
$countryId = (int)($_POST['country_id'] ?? 0);
|
|
$mcc = trim((string)($_POST['mcc'] ?? ''));
|
|
$iso2 = strtoupper(trim((string)($_POST['iso2'] ?? '')));
|
|
$name = trim((string)($_POST['name'] ?? ''));
|
|
$lat = nullable_float($_POST, 'default_lat');
|
|
$lon = nullable_float($_POST, 'default_lon');
|
|
$zoomRaw = trim((string)($_POST['default_zoom'] ?? ''));
|
|
$zoom = $zoomRaw === '' ? 9 : (int)$zoomRaw;
|
|
$wmsUrl = trim((string)($_POST['cadastre_wms_url'] ?? '')) ?: null;
|
|
$wmsLayer = trim((string)($_POST['cadastre_wms_layer'] ?? '')) ?: null;
|
|
$attribution = trim((string)($_POST['cadastre_attribution_html'] ?? '')) ?: null;
|
|
$srs = trim((string)($_POST['cadastre_srs'] ?? '')) ?: 'EPSG:3857';
|
|
|
|
if (!preg_match('/^\d{3}$/', $mcc)) {
|
|
throw new InvalidArgumentException('MCC : 3 chiffres obligatoires.');
|
|
}
|
|
if (!preg_match('/^[A-Z]{2}$/', $iso2)) {
|
|
throw new InvalidArgumentException('ISO2 : 2 lettres obligatoires.');
|
|
}
|
|
if ($name === '') {
|
|
throw new InvalidArgumentException('Le nom du pays est obligatoire.');
|
|
}
|
|
if (($lat === null) !== ($lon === null)) {
|
|
throw new InvalidArgumentException('Saisissez latitude et longitude ensemble, ou laissez les deux champs vides.');
|
|
}
|
|
if ($lat !== null && ($lat < -90 || $lat > 90)) {
|
|
throw new InvalidArgumentException('La latitude doit etre comprise entre -90 et 90.');
|
|
}
|
|
if ($lon !== null && ($lon < -180 || $lon > 180)) {
|
|
throw new InvalidArgumentException('La longitude doit etre comprise entre -180 et 180.');
|
|
}
|
|
if ($zoom < 1 || $zoom > 19) {
|
|
throw new InvalidArgumentException('Le zoom doit etre compris entre 1 et 19.');
|
|
}
|
|
|
|
if ($countryId > 0) {
|
|
$stmt = $db->prepare(
|
|
'UPDATE countries
|
|
SET mcc = ?, iso2 = ?, name = ?, default_lat = ?, default_lon = ?, default_zoom = ?,
|
|
cadastre_wms_url = ?, cadastre_wms_layer = ?,
|
|
cadastre_attribution_html = ?, cadastre_srs = ?
|
|
WHERE id = ?'
|
|
);
|
|
$stmt->execute([$mcc, $iso2, $name, $lat, $lon, $zoom, $wmsUrl, $wmsLayer, $attribution, $srs, $countryId]);
|
|
if ($stmt->rowCount() === 0) {
|
|
$exists = $db->prepare('SELECT 1 FROM countries WHERE id = ?');
|
|
$exists->execute([$countryId]);
|
|
if (!$exists->fetchColumn()) {
|
|
throw new RuntimeException('Pays introuvable.');
|
|
}
|
|
}
|
|
$successMessage = "Pays \"$name\" mis a jour.";
|
|
} else {
|
|
$stmt = $db->prepare(
|
|
'INSERT INTO countries
|
|
(mcc, iso2, name, default_lat, default_lon, default_zoom,
|
|
cadastre_wms_url, cadastre_wms_layer, cadastre_attribution_html, cadastre_srs)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
|
|
);
|
|
$stmt->execute([$mcc, $iso2, $name, $lat, $lon, $zoom, $wmsUrl, $wmsLayer, $attribution, $srs]);
|
|
$successMessage = "Pays \"$name\" cree.";
|
|
}
|
|
} elseif ($action === 'delete_country') {
|
|
$countryId = (int)($_POST['country_id'] ?? 0);
|
|
if ($countryId <= 0) {
|
|
throw new InvalidArgumentException('Pays invalide.');
|
|
}
|
|
$check = $db->prepare('SELECT COUNT(*) FROM operators WHERE country_id = ?');
|
|
$check->execute([$countryId]);
|
|
if ((int)$check->fetchColumn() > 0) {
|
|
throw new RuntimeException('Des operateurs sont rattaches a ce pays : supprimez-les ou reaffectez-les d\'abord.');
|
|
}
|
|
$db->prepare('DELETE FROM countries WHERE id = ?')->execute([$countryId]);
|
|
$successMessage = 'Pays supprime.';
|
|
} elseif ($action === 'save_operator') {
|
|
$operatorId = (int)($_POST['operator_id'] ?? 0);
|
|
$countryId = (int)($_POST['country_id'] ?? 0);
|
|
$mnc = str_pad(trim((string)($_POST['mnc'] ?? '')), 2, '0', STR_PAD_LEFT);
|
|
$brand = trim((string)($_POST['brand'] ?? ''));
|
|
$fullName = trim((string)($_POST['full_name'] ?? '')) ?: null;
|
|
|
|
if ($countryId <= 0 || !preg_match('/^\d{2,3}$/', $mnc) || $brand === '') {
|
|
throw new InvalidArgumentException('Pays, MNC (2 ou 3 chiffres) et nom commercial sont obligatoires.');
|
|
}
|
|
$mccStmt = $db->prepare('SELECT mcc FROM countries WHERE id = ?');
|
|
$mccStmt->execute([$countryId]);
|
|
$mcc = $mccStmt->fetchColumn();
|
|
if ($mcc === false) {
|
|
throw new InvalidArgumentException('Pays invalide.');
|
|
}
|
|
|
|
if ($operatorId > 0) {
|
|
$stmt = $db->prepare('UPDATE operators SET country_id = ?, mcc = ?, mnc = ?, brand = ?, full_name = ? WHERE id = ?');
|
|
$stmt->execute([$countryId, $mcc, $mnc, $brand, $fullName, $operatorId]);
|
|
$successMessage = "Operateur \"$brand\" mis a jour.";
|
|
} else {
|
|
$stmt = $db->prepare('INSERT INTO operators (country_id, mcc, mnc, brand, full_name) VALUES (?, ?, ?, ?, ?)');
|
|
$stmt->execute([$countryId, $mcc, $mnc, $brand, $fullName]);
|
|
$operatorId = (int)$db->lastInsertId();
|
|
$successMessage = "Operateur \"$brand\" ajoute.";
|
|
}
|
|
} elseif ($action === 'delete_operator') {
|
|
$operatorId = (int)($_POST['operator_id'] ?? 0);
|
|
if ($operatorId <= 0) {
|
|
throw new InvalidArgumentException('Operateur invalide.');
|
|
}
|
|
$check = $db->prepare('SELECT COUNT(*) FROM sites WHERE operator_id = ?');
|
|
$check->execute([$operatorId]);
|
|
if ((int)$check->fetchColumn() > 0) {
|
|
throw new RuntimeException('Des sites valides existent pour cet operateur.');
|
|
}
|
|
$db->prepare('DELETE FROM operators WHERE id = ?')->execute([$operatorId]);
|
|
$successMessage = 'Operateur supprime.';
|
|
} elseif ($action === 'save_band_mapping') {
|
|
$operatorId = (int)($_POST['operator_id'] ?? 0);
|
|
$band = trim((string)($_POST['band'] ?? ''));
|
|
$earfcnStart = (int)($_POST['earfcn_start'] ?? 0);
|
|
$earfcnEnd = (int)($_POST['earfcn_end'] ?? 0);
|
|
|
|
if ($operatorId <= 0 || !preg_match('/^B\d+[a-z]?$/i', $band)) {
|
|
throw new InvalidArgumentException('Operateur et bande (ex: B20, B3, B7) obligatoires.');
|
|
}
|
|
if ($earfcnStart <= 0 || $earfcnEnd < $earfcnStart) {
|
|
throw new InvalidArgumentException('Plage EARFCN invalide (start > 0, end >= start).');
|
|
}
|
|
|
|
$stmt = $db->prepare(
|
|
'INSERT INTO operator_band_mappings (operator_id, band, earfcn_start, earfcn_end)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE earfcn_start = VALUES(earfcn_start), earfcn_end = VALUES(earfcn_end)'
|
|
);
|
|
$stmt->execute([$operatorId, strtoupper($band), $earfcnStart, $earfcnEnd]);
|
|
$successMessage = "Mapping bande $band enregistre pour l'operateur.";
|
|
} elseif ($action === 'delete_band_mapping') {
|
|
$mappingId = (int)($_POST['mapping_id'] ?? 0);
|
|
if ($mappingId <= 0) {
|
|
throw new InvalidArgumentException('Mapping invalide.');
|
|
}
|
|
$db->prepare('DELETE FROM operator_band_mappings WHERE id = ?')->execute([$mappingId]);
|
|
$successMessage = 'Mapping supprime.';
|
|
}
|
|
} catch (InvalidArgumentException|RuntimeException $e) {
|
|
$errorMessage = $e->getMessage();
|
|
} catch (PDOException $e) {
|
|
error_log('[countries_settings] PDOException: ' . $e->getMessage());
|
|
$errorMessage = str_contains($e->getMessage(), 'Duplicate')
|
|
? 'Ce couple MCC/MNC ou ce mapping bande existe deja.'
|
|
: 'Une erreur est survenue. Consultez les journaux du serveur si le probleme persiste.';
|
|
}
|
|
}
|
|
|
|
$countries = $db->query('SELECT * FROM countries ORDER BY name')->fetchAll(PDO::FETCH_ASSOC);
|
|
$operators = $db->query(
|
|
'SELECT o.*, c.name AS country_name FROM operators o
|
|
JOIN countries c ON c.id = o.country_id
|
|
ORDER BY c.name, o.brand'
|
|
)->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$operatorsByCountry = [];
|
|
foreach ($operators as $op) {
|
|
$operatorsByCountry[$op['country_id']][] = $op;
|
|
}
|
|
|
|
// Charger les mappings bande/EARFCN pour chaque operateur
|
|
$bandMappingsByOperator = [];
|
|
$mappings = $db->query('SELECT * FROM operator_band_mappings ORDER BY operator_id, band')->fetchAll(PDO::FETCH_ASSOC);
|
|
foreach ($mappings as $m) {
|
|
$bandMappingsByOperator[$m['operator_id']][] = $m;
|
|
}
|
|
|
|
$editingCountry = null;
|
|
$editCountryId = filter_input(INPUT_GET, 'edit_country', FILTER_VALIDATE_INT);
|
|
if ($editCountryId) {
|
|
foreach ($countries as $country) {
|
|
if ((int)$country['id'] === $editCountryId) {
|
|
$editingCountry = $country;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$editingOperator = null;
|
|
$editOperatorId = filter_input(INPUT_GET, 'edit_operator', FILTER_VALIDATE_INT);
|
|
if ($editOperatorId) {
|
|
foreach ($operators as $operator) {
|
|
if ((int)$operator['id'] === $editOperatorId) {
|
|
$editingOperator = $operator;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function field_value(?array $source, string $key, string $fallback = ''): string
|
|
{
|
|
return htmlspecialchars((string)($source[$key] ?? $fallback), ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
$countryFormTitle = $editingCountry ? 'Modifier le pays : ' . field_value($editingCountry, 'name') : 'Ajouter un pays';
|
|
$operatorFormTitle = $editingOperator ? 'Modifier l\'operateur : ' . field_value($editingOperator, 'brand') : 'Ajouter un operateur';
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>NetMonster Admin - Pays & operateurs</title>
|
|
<link rel="stylesheet" href="assets/theme.css">
|
|
</head>
|
|
<body>
|
|
<?php render_admin_nav('countries_settings.php', $user); ?>
|
|
|
|
<main class="page-wrap" id="top">
|
|
<h1>Configuration des pays & operateurs</h1>
|
|
<p class="page-subtitle">Configurez les pays, le centre de carte, les couches cadastre, les operateurs et les mappings bande/EARFCN.</p>
|
|
|
|
<?php if ($successMessage !== null): ?>
|
|
<p class="msg-ok"><?= htmlspecialchars($successMessage, ENT_QUOTES, 'UTF-8') ?></p>
|
|
<?php endif; ?>
|
|
<?php if ($errorMessage !== null): ?>
|
|
<p class="msg-err"><?= htmlspecialchars($errorMessage, ENT_QUOTES, 'UTF-8') ?></p>
|
|
<?php endif; ?>
|
|
|
|
<section class="card">
|
|
<h2><?= $countryFormTitle ?></h2>
|
|
<form method="post">
|
|
<?= csrf_field() ?>
|
|
<input type="hidden" name="action" value="save_country">
|
|
<input type="hidden" name="country_id" value="<?= (int)($editingCountry['id'] ?? 0) ?>">
|
|
|
|
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:0.9rem;margin-bottom:0.9rem;">
|
|
<label>MCC (3 chiffres)
|
|
<input type="text" name="mcc" pattern="\d{3}" maxlength="3" required value="<?= field_value($editingCountry, 'mcc') ?>" placeholder="ex: 270">
|
|
</label>
|
|
<label>Code ISO2
|
|
<input type="text" name="iso2" pattern="[A-Za-z]{2}" maxlength="2" required value="<?= field_value($editingCountry, 'iso2') ?>" placeholder="ex: LU">
|
|
</label>
|
|
<label>Nom du pays
|
|
<input type="text" name="name" required value="<?= field_value($editingCountry, 'name') ?>" placeholder="ex: Luxembourg">
|
|
</label>
|
|
</div>
|
|
|
|
<h3 style="margin:1rem 0 0.5rem;">Centre de la carte</h3>
|
|
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:0.9rem;margin-bottom:0.9rem;">
|
|
<label>Latitude par defaut
|
|
<input type="number" step="any" min="-90" max="90" name="default_lat" value="<?= field_value($editingCountry, 'default_lat') ?>" placeholder="ex: 49.8153">
|
|
</label>
|
|
<label>Longitude par defaut
|
|
<input type="number" step="any" min="-180" max="180" name="default_lon" value="<?= field_value($editingCountry, 'default_lon') ?>" placeholder="ex: 6.1296">
|
|
</label>
|
|
<label>Zoom par defaut
|
|
<input type="number" name="default_zoom" min="1" max="19" value="<?= field_value($editingCountry, 'default_zoom', '9') ?>">
|
|
</label>
|
|
</div>
|
|
|
|
<h3 style="margin:1rem 0 0.5rem;">Cadastre / WMS (optionnel)</h3>
|
|
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:0.9rem;margin-bottom:0.9rem;">
|
|
<label style="grid-column:1 / -1;">URL du service WMS cadastre
|
|
<input type="url" name="cadastre_wms_url" value="<?= field_value($editingCountry, 'cadastre_wms_url') ?>" placeholder="https://exemple.tld/wms">
|
|
</label>
|
|
<label>Nom / identifiant de la couche WMS
|
|
<input type="text" name="cadastre_wms_layer" value="<?= field_value($editingCountry, 'cadastre_wms_layer') ?>" placeholder="ex: parcelles">
|
|
</label>
|
|
<label>SRS
|
|
<input type="text" name="cadastre_srs" value="<?= field_value($editingCountry, 'cadastre_srs', 'EPSG:3857') ?>" placeholder="EPSG:3857">
|
|
</label>
|
|
<label style="grid-column:1 / -1;">Attribution cadastre
|
|
<input type="text" name="cadastre_attribution_html" value="<?= field_value($editingCountry, 'cadastre_attribution_html') ?>" placeholder="Credit obligatoire de la source de donnees">
|
|
</label>
|
|
</div>
|
|
|
|
<details class="help">
|
|
<summary>Ou trouver une URL WMS ?</summary>
|
|
Cherchez le portail geographique national et son service WMS public, generalement documente par une requete GetCapabilities. Laissez les champs WMS vides si aucune source ouverte n'est disponible : la carte restera utilisable avec OpenStreetMap.
|
|
</details>
|
|
<button type="submit" class="primary"><?= $editingCountry ? 'Enregistrer les modifications' : 'Creer le pays' ?></button>
|
|
<?php if ($editingCountry): ?>
|
|
<a href="countries_settings.php" style="margin-left:.7rem;">Annuler l'edition</a>
|
|
<?php endif; ?>
|
|
</form>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>Pays configures (<?= count($countries) ?>)</h2>
|
|
<table class="data-table">
|
|
<tr><th>Pays</th><th>MCC</th><th>ISO2</th><th>Centre carte</th><th>Cadastre</th><th>Operateurs</th><th>Actions</th></tr>
|
|
<?php foreach ($countries as $country): ?>
|
|
<tr>
|
|
<td><?= field_value($country, 'name') ?></td>
|
|
<td><?= field_value($country, 'mcc') ?></td>
|
|
<td><?= field_value($country, 'iso2') ?></td>
|
|
<td><?= $country['default_lat'] !== null ? field_value($country, 'default_lat') . ', ' . field_value($country, 'default_lon') : '—' ?></td>
|
|
<td>
|
|
<?php if (!empty($country['cadastre_wms_url'])): ?>
|
|
<span class="cadastre-badge">configure</span>
|
|
<?php else: ?>
|
|
<span class="no-cadastre">non configure</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><?= count($operatorsByCountry[$country['id']] ?? []) ?></td>
|
|
<td>
|
|
<a href="countries_settings.php?edit_country=<?= (int)$country['id'] ?>#top">Modifier</a>
|
|
<form method="post" style="display:inline" onsubmit="return confirm('Supprimer ce pays ? Cette action est impossible tant que des operateurs y sont rattaches.');">
|
|
<?= csrf_field() ?>
|
|
<input type="hidden" name="action" value="delete_country">
|
|
<input type="hidden" name="country_id" value="<?= (int)$country['id'] ?>">
|
|
<button type="submit" class="reject-small">Supprimer</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php if (empty($countries)): ?>
|
|
<tr><td colspan="7" class="no-cadastre">Aucun pays configure.</td></tr>
|
|
<?php endif; ?>
|
|
</table>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2><?= $operatorFormTitle ?></h2>
|
|
<form method="post">
|
|
<?= csrf_field() ?>
|
|
<input type="hidden" name="action" value="save_operator">
|
|
<input type="hidden" name="operator_id" value="<?= (int)($editingOperator['id'] ?? 0) ?>">
|
|
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:0.9rem;margin-bottom:0.9rem;">
|
|
<label>Pays
|
|
<select name="country_id" required>
|
|
<option value="">-- choisir --</option>
|
|
<?php foreach ($countries as $country): ?>
|
|
<option value="<?= (int)$country['id'] ?>" <?= (int)($editingOperator['country_id'] ?? 0) === (int)$country['id'] ? 'selected' : '' ?>>
|
|
<?= field_value($country, 'name') ?> (MCC <?= field_value($country, 'mcc') ?>)
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</label>
|
|
<label>MNC
|
|
<input type="text" name="mnc" pattern="\d{2,3}" maxlength="3" required value="<?= field_value($editingOperator, 'mnc') ?>" placeholder="ex: 01">
|
|
</label>
|
|
<label>Nom commercial
|
|
<input type="text" name="brand" required value="<?= field_value($editingOperator, 'brand') ?>" placeholder="ex: POST">
|
|
</label>
|
|
</div>
|
|
<label style="display:block;margin-bottom:0.9rem;">Raison sociale complete
|
|
<input type="text" name="full_name" value="<?= field_value($editingOperator, 'full_name') ?>" placeholder="Optionnel">
|
|
</label>
|
|
<button type="submit" class="primary"><?= $editingOperator ? 'Enregistrer les modifications' : 'Ajouter l\'operateur' ?></button>
|
|
<?php if ($editingOperator): ?>
|
|
<a href="countries_settings.php" style="margin-left:.7rem;">Annuler l'edition</a>
|
|
<?php endif; ?>
|
|
</form>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>Operateurs configures (<?= count($operators) ?>)</h2>
|
|
<table class="data-table">
|
|
<tr><th>Pays</th><th>Brand</th><th>MCC-MNC</th><th>Raison sociale</th><th>Actions</th></tr>
|
|
<?php foreach ($operators as $operator): ?>
|
|
<tr>
|
|
<td><?= field_value($operator, 'country_name') ?></td>
|
|
<td><?= field_value($operator, 'brand') ?></td>
|
|
<td><?= field_value($operator, 'mcc') ?>-<?= field_value($operator, 'mnc') ?></td>
|
|
<td><?= field_value($operator, 'full_name') ?></td>
|
|
<td>
|
|
<a href="countries_settings.php?edit_operator=<?= (int)$operator['id'] ?>#top">Modifier</a>
|
|
<form method="post" style="display:inline" onsubmit="return confirm('Supprimer cet operateur ? Cette action est impossible tant que des sites lui sont rattaches.');">
|
|
<?= csrf_field() ?>
|
|
<input type="hidden" name="action" value="delete_operator">
|
|
<input type="hidden" name="operator_id" value="<?= (int)$operator['id'] ?>">
|
|
<button type="submit" class="reject-small">Supprimer</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php if (empty($operators)): ?>
|
|
<tr><td colspan="5" class="no-cadastre">Aucun operateur configure.</td></tr>
|
|
<?php endif; ?>
|
|
</table>
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>Mappings bande 4G → EARFCN par operateur</h2>
|
|
<p class="page-subtitle">Configurez les plages EARFCN utilisees par chaque operateur sur chaque bande 4G. Ces valeurs sont utilisees lors de l'import CellMapper pour deduire l'EARFCN a partir de la bande selectionnee.</p>
|
|
|
|
<?php foreach ($operators as $operator): ?>
|
|
<div style="margin:1.2rem 0; padding:1rem; border:1px solid var(--border); border-radius:var(--radius);">
|
|
<h3 style="margin:0 0 0.5rem;"><?= field_value($operator, 'brand') ?> (MCC <?= field_value($operator, 'mcc') ?> / MNC <?= field_value($operator, 'mnc') ?>)</h3>
|
|
|
|
<form method="post" style="margin-bottom:0.8rem;">
|
|
<?= csrf_field() ?>
|
|
<input type="hidden" name="action" value="save_band_mapping">
|
|
<input type="hidden" name="operator_id" value="<?= (int)$operator['id'] ?>">
|
|
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:0.7rem;align-items:end;">
|
|
<label>Bande
|
|
<input type="text" name="band" pattern="B\d+[a-z]?" required placeholder="ex: B20" style="width:100%;">
|
|
</label>
|
|
<label>EARFCN debut
|
|
<input type="number" name="earfcn_start" min="0" required placeholder="ex: 6200" style="width:100%;">
|
|
</label>
|
|
<label>EARFCN fin
|
|
<input type="number" name="earfcn_end" min="0" required placeholder="ex: 6349" style="width:100%;">
|
|
</label>
|
|
<button type="submit" class="primary" style="padding:0.5rem 1rem;">Ajouter</button>
|
|
</div>
|
|
</form>
|
|
|
|
<?php if (!empty($bandMappingsByOperator[$operator['id']])): ?>
|
|
<table class="data-table" style="margin-top:0.5rem;">
|
|
<tr><th>Bande</th><th>EARFCN debut</th><th>EARFCN fin</th><th>Actions</th></tr>
|
|
<?php foreach ($bandMappingsByOperator[$operator['id']] as $mapping): ?>
|
|
<tr>
|
|
<td><code><?= field_value($mapping, 'band') ?></code></td>
|
|
<td><?= (int)$mapping['earfcn_start'] ?></td>
|
|
<td><?= (int)$mapping['earfcn_end'] ?></td>
|
|
<td>
|
|
<form method="post" style="display:inline" onsubmit="return confirm('Supprimer ce mapping ?');">
|
|
<?= csrf_field() ?>
|
|
<input type="hidden" name="action" value="delete_band_mapping">
|
|
<input type="hidden" name="mapping_id" value="<?= (int)$mapping['id'] ?>">
|
|
<button type="submit" class="reject-small">Supprimer</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</table>
|
|
<?php else: ?>
|
|
<p class="no-cadastre" style="margin:0.5rem 0;">Aucun mapping configure pour cet operateur.</p>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php if (empty($operators)): ?>
|
|
<p class="no-cadastre">Aucun operateur configure. Ajoutez d'abord un operateur ci-dessus.</p>
|
|
<?php endif; ?>
|
|
</section>
|
|
</main>
|
|
</body>
|
|
</html>
|