Этот коммит содержится в:
viktor138irk
2025-07-11 22:17:56 +09:00
коммит произвёл GitHub
родитель f5dc4a3506
Коммит 89ccb7fa63
14 изменённых файлов: 7265 добавлений и 0 удалений
+405
Просмотреть файл
@@ -0,0 +1,405 @@
<?php
$file = __DIR__ . '/db.json';
$datFile = __DIR__ . '/dmrid.dat';
$json = json_decode(file_get_contents($file), true) ?? ['count' => 0, 'results' => []];
$data = $json['results'];
if (isset($_GET['ajax_check'])) {
$id = $_GET['id'] ?? '';
$callsign = strtoupper(trim($_GET['callsign'] ?? ''));
$response = [
'idInvalid' => !preg_match('/^\d{6,7}$/', $id),
'idExists' => false,
'callsignInvalid' => !preg_match('/^[A-Z0-9]{4,7}$/', $callsign),
'callsignExists' => false
];
foreach ($data as $row) {
if ($row['id'] == $id) $response['idExists'] = true;
if (strtoupper($row['callsign']) === $callsign) $response['callsignExists'] = true;
}
header('Content-Type: application/json');
echo json_encode($response);
exit;
}
if (isset($_GET['sync'])) {
$confirmed = [];
foreach ($data as $item) {
if (!empty($item['confirmed'])) {
$confirmed["{$item['id']};{$item['callsign']};"] = true;
}
}
$lines = file($datFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$filtered = array_filter($lines, function($line) use ($confirmed) {
return isset($confirmed[trim($line)]);
});
$existingLines = array_flip(array_map('trim', $lines));
foreach ($confirmed as $entry => $val) {
if (!isset($existingLines[$entry])) {
$filtered[] = $entry;
}
}
file_put_contents($datFile, implode(PHP_EOL, $filtered) . PHP_EOL);
header("Location: " . strtok($_SERVER["REQUEST_URI"], '?'));
exit;
}
if (isset($_GET['confirm'])) {
$confirmId = (int)$_GET['confirm'];
foreach ($data as &$item) {
if ($item['id'] === $confirmId) {
$item['confirmed'] = true;
$lines = file($datFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$entry = "{$item['id']};{$item['callsign']};";
if (!in_array($entry, $lines)) {
file_put_contents($datFile, $entry . PHP_EOL, FILE_APPEND);
}
break;
}
}
unset($item);
$json['results'] = $data;
$json['count'] = count($data);
file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
header("Location: " . strtok($_SERVER["REQUEST_URI"], '?'));
exit;
}
if (isset($_GET['block'])) {
$confirmId = (int)$_GET['block'];
foreach ($data as &$item) {
if ($item['id'] === $confirmId) {
$item['confirmed'] = false;
$lines = file($datFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$entry = "{$item['id']};{$item['callsign']};";
$filtered = array_filter($lines, function($line) use ($entry) {
return trim($line) !== $entry;
});
file_put_contents($datFile, implode(PHP_EOL, $filtered) . PHP_EOL);
break;
}
}
unset($item);
$json['results'] = $data;
$json['count'] = count($data);
file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
header("Location: " . strtok($_SERVER["REQUEST_URI"], '?'));
exit;
}
if (isset($_GET['delete'])) {
$deleteId = (int)$_GET['delete'];
$callsign = null;
foreach ($data as $index => $item) {
if ($item['id'] === $deleteId) {
$callsign = $item['callsign'];
unset($data[$index]);
break;
}
}
$data = array_values($data);
if ($callsign === null) {
header("Location: " . strtok($_SERVER["REQUEST_URI"], '?'));
exit;
}
$json['results'] = $data;
$json['count'] = count($data);
file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
$lines = file($datFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$filtered = array_filter($lines, function($line) use ($deleteId, $callsign) {
return trim($line) !== "{$deleteId};{$callsign};";
});
file_put_contents($datFile, implode(PHP_EOL, $filtered) . PHP_EOL);
header("Location: " . strtok($_SERVER["REQUEST_URI"], '?'));
exit;
}
$form_error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$new_id = (int)$_POST['new_id'];
$callsign = strtoupper(trim($_POST['new_callsign']));
$fname = trim($_POST['fname'] ?? '');
$surname = trim($_POST['surname'] ?? '');
$city = trim($_POST['city'] ?? '');
$state = trim($_POST['state'] ?? '');
$country = trim($_POST['country'] ?? '');
$remarks = trim($_POST['remarks'] ?? '');
if ($new_id < 100000 || $new_id > 9999999) {
$form_error = 'ID должен быть 6–7 цифр.';
} elseif (!preg_match('/^[A-Z0-9]{4,7}$/', $callsign)) {
$form_error = 'Позывной: 4–7 латинских символов/цифр.';
} elseif (array_filter($data, fn($r) => $r['id'] == $new_id || strtoupper($r['callsign']) === $callsign)) {
$form_error = 'Такой ID или позывной уже существует.';
} elseif (preg_match('/[^a-zA-Z\s]/', $fname . $surname . $city . $state . $country . $remarks)) {
$form_error = 'Дополнительные поля должны содержать только латиницу.';
} else {
$data[] = [
'id' => $new_id,
'callsign' => $callsign,
'fname' => $fname,
'surname' => $surname,
'city' => $city,
'state' => $state,
'country' => $country,
'remarks' => $remarks,
'confirmed' => true
];
$json['results'] = $data;
$json['count'] = count($data);
file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
$dat_line = "{$new_id};{$callsign};" . PHP_EOL;
file_put_contents($datFile, $dat_line, FILE_APPEND);
header("Location: admin.php");
exit;
}
}
$confirmed = array_values(array_filter($data, fn($d) => !empty($d['confirmed'])));
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 15;
$total = count($confirmed);
$totalPages = ceil($total / $perPage);
$offset = ($page - 1) * $perPage;
$currentPageData = array_slice($confirmed, $offset, $perPage);
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Админка DMR</title>
<style>
* { box-sizing: border-box; }
body {
font-family: Arial, sans-serif;
background: #f3f4f6;
margin: 0;
padding: 0;
}
header {
background: #1f2937;
color: #fff;
padding: 15px 30px;
font-size: 20px;
font-weight: bold;
}
.container {
display: flex;
padding: 30px;
gap: 30px;
align-items: flex-start;
}
.left-section { flex: 2; }
.right-section {
flex: 1;
position: sticky;
top: 30px;
background: #fff;
border-radius: 10px;
padding: 20px;
box-shadow: 0 0 10px rgba(0,0,0,0.05);
}
.card {
background: #fff;
padding: 15px;
margin-bottom: 10px;
border-radius: 8px;
box-shadow: 0 0 4px rgba(0,0,0,0.05);
display: flex;
justify-content: space-between;
align-items: center;
}
.card span { font-size: 14px; }
.confirm { color: #10b981; font-weight: bold; text-decoration: none; }
.delete { color: #ef4444; text-decoration: none; }
.add-form input {
padding: 10px;
margin-bottom: 10px;
width: 100%;
border-radius: 5px;
border: 1px solid #ccc;
}
.add-form button {
padding: 10px;
background: #3b82f6;
color: white;
border: none;
border-radius: 5px;
font-weight: bold;
width: 100%;
cursor: pointer;
}
.form-error {
color: red;
font-size: 13px;
margin-top: -8px;
margin-bottom: 10px;
}
.sync-button {
background: white;
color: #1f2937;
border: none;
padding: 8px 14px;
font-size: 14px;
border-radius: 5px;
cursor: pointer;
}
.pagination { margin-top: 15px; text-align: center; }
.pagination a { margin: 0 5px; text-decoration: none; padding: 5px 10px; background: #eee; border-radius: 5px; }
.pagination .current { background: #1f2937; color: #fff; }
h2, h3 { margin-top: 0; }
</style>
</head>
<body>
<header>Панель администратора DMR
<form method="get" style="display:inline-block; float: right;" onsubmit="return confirm('Синхронизировать список с JSON?')">
<input type="hidden" name="sync" value="1">
<button type="submit" class="sync-button">Синхронизировать</button>
</form>
</header>
<div class="container">
<div class="left-section">
<h2>Заявки</h2>
<div id="cards">
<?php foreach ($currentPageData as $row): ?>
<div class="card">
<span> <b>ID:</b> <?= $row['id'] ?></br>
<b>Позывной:</b> <a href="https://xlxsmk.ru/index.php?subaction=userinfo&user=<?= htmlspecialchars($row['callsign']) ?>" class="confirm"><?= htmlspecialchars($row['callsign']) ?></a></br>
<b>Имя:</b> <?= htmlspecialchars($row['fname']) ?></br>
<b>Фамилия:</b> <?= htmlspecialchars($row['surname']) ?></br>
<b>Город:</b> <?= htmlspecialchars($row['city']) ?></br>
<b>Регион:</b> <?= htmlspecialchars($row['state']) ?></br>
<b>Страна:</b> <?= htmlspecialchars($row['country']) ?></br>
<b>Примечание:</b> <?= htmlspecialchars($row['remarks']) ?></br>
</span>
<span>
<a href="?delete=<?= $row['id'] ?>" class="delete" onclick="return confirm('Удалить эту запись?')">Удалить</a>
<a href="?block=<?= $row['id'] ?>" class="delete">Заблокировать</a>
</span>
</div>
<?php endforeach; ?>
</div>
<?php if ($totalPages > 1): ?>
<div class="pagination">
<?php for ($p = 1; $p <= $totalPages; $p++): ?>
<a href="?page=<?= $p ?>" class="<?= $p == $page ? 'current' : '' ?>"><?= $p ?></a>
<?php endfor; ?>
</div>
<?php endif; ?>
</div>
<div class="right-section">
<h3>Добавить вручную</h3>
<?php if (!empty($form_error)): ?>
<div class="form-error"><?= htmlspecialchars($form_error) ?></div>
<?php endif; ?>
<form method="post" class="add-form">
<input type="number" name="new_id" placeholder="DMR ID" required>
<div class="form-error" id="id-error"></div>
<input oninput="this.value = this.value.toUpperCase()" type="text" name="new_callsign" placeholder="Позывной" required>
<div class="form-error" id="callsign-error"></div>
<input type="text" name="fname" placeholder="Имя (латиница)">
<input type="text" name="surname" placeholder="Фамилия (латиница)">
<input type="text" name="city" placeholder="Город (латиница)">
<input type="text" name="state" placeholder="Регион (латиница)">
<input type="text" name="country" placeholder="Страна (латиница)">
<input type="text" name="remarks" placeholder="Примечание (латиница)">
<div class="form-error" id="latin-error"></div>
<button type="submit" id="submit-btn">Добавить</button>
</form>
<div id="unconfirmed-cards" style="margin-top: 30px;">
<h4>Ожидают подтверждения</h4>
<?php foreach ($data as $row): ?>
<?php if (empty($row['confirmed'])): ?>
<div class="card">
<span>⏳ <?= htmlspecialchars($row['callsign']) ?> </span>
<a href="?confirm=<?= $row['id'] ?>" class="confirm">Подтвердить</a>
<a href="?delete=<?= $row['id'] ?>" class="delete" onclick="return confirm('Удалить эту запись?')">Удалить</a>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
const idInput = document.querySelector('input[name="new_id"]');
const csInput = document.querySelector('input[name="new_callsign"]');
const submitBtn = document.getElementById('submit-btn');
const idError = document.getElementById('id-error');
const csError = document.getElementById('callsign-error');
const latinError = document.getElementById('latin-error');
const optionalInputs = document.querySelectorAll('.add-form input[type="text"]:not([name="new_callsign"])');
async function check() {
const id = idInput.value.trim();
const callsign = csInput.value.trim();
idError.textContent = '';
csError.textContent = '';
latinError.textContent = '';
submitBtn.disabled = false;
const res = await fetch(`?ajax_check=1&id=${encodeURIComponent(id)}&callsign=${encodeURIComponent(callsign)}`);
const json = await res.json();
if (json.idInvalid) {
idError.textContent = 'ID должен быть 6–7 цифр.';
} else if (json.idExists) {
idError.textContent = 'Такой ID уже существует.';
}
if (json.callsignInvalid) {
csError.textContent = 'Позывной: 4–7 латинских символов/цифр.';
} else if (json.callsignExists) {
csError.textContent = 'Такой позывной уже существует.';
}
if (json.idInvalid || json.idExists || json.callsignInvalid || json.callsignExists) {
submitBtn.disabled = true;
}
for (const input of optionalInputs) {
if (input.value && !/^[a-zA-Z\s]*$/.test(input.value)) {
latinError.textContent = 'Дополнительные поля должны содержать только латиницу.';
submitBtn.disabled = true;
break;
}
}
}
idInput.addEventListener('input', check);
csInput.addEventListener('input', check);
optionalInputs.forEach(input => input.addEventListener('input', check));
});
</script>
</body>
</html>
Разница между файлами не показана из-за своего большого размера Загрузить разницу
+224
Просмотреть файл
@@ -0,0 +1,224 @@
1400001;ADMIN;
140002;XLXYKT;
140003;XLXARK;
140004;XLXPDS;
1412262;SEVERY;
1410461;TESTI;
670001;XLXB;
670003;PODOLSK;
6700002;DVSB;
6700003;DVSF;
1030176;KASKAD;
1400077;DYNAMIT;
1030174;LEKTRON;
2501655;ICEBERG;
1030169;RAMON;
1030171;ZELLO;
1030170;FRNLINK;
1030167;LESHIY;
1030166;FAGOT;
1030165;STALKER;
1030164;NIRVANA;
2501501;IRON;
1030153;USER2;
1030154;USER3;
1030155;USER4;
1030156;USER5;
1030157;BARMI;
1030158;USER7;
1030159;USER8;
1030160;USER9;
1030161;USER10;
2500460;RYBAK;
2500779;R7KAY;
7801620;MUTABOR;
1030148;JUPITER;
5000941;KIRPICH;
1030144;EGERY;
2840338;HBLINK;
1030136;RYBACH;
1030137;LUDZA;
1030138;BUY27;
1030139;DEMBI;
1030140;KOLDA;
1030141;GRAS;
1030142;GRAS;
1030143;KOPPI;
2501513;SWIFT;
1030134;REGION;
1030133;VOLNA;
1030132;SUMRAK;
2501517;R2BRI;
7804951;DUNAY;
1030129;RUBEZH;
2503228;KARDAN;
1030127;HAUX;
1030126;NEBO2;
1030125;LUCKY;
6550059;GITARIST;
1030123;DIZEL;
1030122;AKRA;
2503128;SIMBA;
7807808;YKCYC;
2501250;ADVOKAT;
1030118;KARAT;
2509132;BARISTA;
1030106;CHAMBAR;
2501316;NARCISS;
6550001;ARENDAT;
6550002;UMBRELA;
6550003;MIHA111;
6550004;KAREL;
6550006;ZS6DLK;
6550008;ZS6RES;
6550009;EVGEN;
6550010;MAGUS;
1030104;ARENDAT;
2500352;EGORYCH;
2570200;STRELA;
1030103;MALOY;
2501349;FOXMAN;
1030098;NARCIS;
1030097;SHTURMN;
2501301;GRACH;
1030040;ARKTEST;
1030096;BARMI;
1030092;ALEXBUT;
1030091;MASHUK;
1030088;AKCENT;
1000780;P25;
1030086;NET1;
1030087;NET2;
6555027;BOGDAN;
1030084;BOGDAN;
1030083;KARAS;
2501190;IGORH;
1030081;AZGARD;
1030080;MORSKOY;
2501231;PENZA;
1030078;VORON;
1030100;DOCTOR;
2864293;MIHA111;
2508125;MEHANIK;
1030074;GRANIT;
2501366;SASHKO;
1030072;FRNLINK;
2518001;KORUS;
1030070;SLITER;
4700830;FDR830;
1030068;ALEX203;
2502228;PRORAB;
1030066;PRORAB;
2501099;KIPARIS;
7800056;AYVENGO;
6700067;VIRAJ;
1030061;SAN428;
2501182;POOH;
1030059;DVINA;
2570193;KANAPA;
1030057;AYDAR;
1030003;MIHA111;
1030056;GNOM;
2500358;NAIL44;
1230311;YUG311;
2500780;MAX03;
2500945;DIMGRAD;
1030178;RYAZAN;
1030049;LEXUS;
2500944;KALASH;
7700023;BARON;
1030002;UMBREL;
1030046;VETER;
1030004;VOSTOK;
1030005;ELISTA;
1030006;ARTEK;
1030007;VLAD770;
1030008;GITARIS;
1030009;TRI333;
1030011;URAL;
1030013;AYAKS;
2500263;NOVAK;
1030016;DMITROV;
1030012;VOST555;
1030018;SENATOR;
1030030;STELS;
1030021;DANTIST;
2502181;DMIT920;
2570148;MASTER;
2500887;ALEX;
1030027;KARE;
2502168;BUMER;
2506145;GURZA;
1030032;ARTEM48;
1030033;LESNIK;
5973777;SOKOL;
2500222;VOLKIS;
6555016;URAL;
4600403;ARTEK;
6550007;VLAD770;
6555008;NAVIRUS;
2503316;GITARIS;
2500247;SOKOL;
1030038;MAZAY;
2502072;GRANIT;
1030001;NAVIRUS;
2501668;NEMOI;
1400841;YAKUT;
2500893;BOREY;
2501457;ALEX77;
1030182;BRONIX;
1030183;KALITKA;
1030184;0350;
5600001;VLAD46;
2501726;PATRIOT;
2509122;DOMINIK;
1030188;AVER89;
1030189;MAGNAT;
1030192;OCTOBER;
2501757;TRIDVA;
1030194;LIMAN;
1111777;TRAKTOR;
1030196;UDM697;
2500773;ITMAN;
2500500;PORU4IK;
2501815;KAREL;
7900001;DIMA55;
1030201;ANTIK;
2501712;KLIM45;
1030203;DENKENG;
1030204;1908;
1030205;NUMARK;
1030207;IGOR377;
2500866;PRAD0SC;
2501222;ENOT;
1030212;RUSSIA;
1030214;GROZA;
2501263;REDDI77;
1030216;BOTCHI;
2501789;BURAN88;
2500810;SLON;
5500055;ROSTIK;
1030230;DANAG;
2501891;MEXAHNK;
1030232;ZEZERON;
1030233;PEAKTOP;
1030234;ZATON;
1030235;ROKOT40;
2501931;ENGINER;
1030239;DENNM;
1030240;FDMA;
1030241;GAGIN;
2501918;IRTISH;
4000001;AVIATOR;
2502062;TRUBACH;
1030249;AMUR;
123123;SDDD;
2508009;KORUS2;
2500591;VLAD;
1419867;JDJDDJ;
1419418;TESTTES;
1417126;TESTTE1;
1416712;TESTTE1;
670002;ECHO1;
2501956;MARCUS;
7900002;SHULTZ;
+227
Просмотреть файл
@@ -0,0 +1,227 @@
<?php
$file = __DIR__ . '/db.json';
$datFile = __DIR__ . '/dmrid.dat';
$json = json_decode(file_get_contents($file), true) ?? ['count' => 0, 'results' => []];
$data = $json['results'];
if (isset($_GET['ajax_check'])) {
$callsign = strtoupper(trim($_GET['callsign'] ?? ''));
$response = [
'callsignInvalid' => !preg_match('/^[A-Z0-9]{4,7}$/', $callsign),
'callsignExists' => false
];
foreach ($data as $row) {
if (strtoupper($row['callsign']) === $callsign) $response['callsignExists'] = true;
}
header('Content-Type: application/json');
echo json_encode($response);
exit;
}
if (isset($_GET['sync'])) {
$lines = [];
foreach ($data as $item) {
if (!empty($item['confirmed'])) {
$id = trim($item['id']);
$callsign = strtoupper(trim($item['callsign']));
$lines[] = "{$id};{$callsign};";
}
}
file_put_contents($datFile, implode(PHP_EOL, $lines) . PHP_EOL);
header("Location: admin.php");
exit;
}
if (isset($_GET['confirm'])) {
$confirmId = (int)$_GET['confirm'];
foreach ($data as &$item) {
if ($item['id'] === $confirmId) {
$item['confirmed'] = true;
$lines = file($datFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$entry = "{$item['id']};{$item['callsign']};";
if (!in_array($entry, $lines)) {
file_put_contents($datFile, $entry . PHP_EOL, FILE_APPEND);
}
break;
}
}
unset($item);
$json['results'] = $data;
$json['count'] = count($data);
file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
header("Location: admin.php");
exit;
}
if (isset($_GET['block'])) {
$confirmId = (int)$_GET['block'];
foreach ($data as &$item) {
if ($item['id'] === $confirmId) {
$item['confirmed'] = false;
$lines = file($datFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$entry = "{$item['id']};{$item['callsign']};";
$filtered = array_filter($lines, function($line) use ($entry) {
return trim($line) !== $entry;
});
file_put_contents($datFile, implode(PHP_EOL, $filtered) . PHP_EOL);
break;
}
}
unset($item);
$json['results'] = $data;
$json['count'] = count($data);
file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
header("Location: admin.php");
exit;
}
if (isset($_GET['delete'])) {
$deleteId = (int)$_GET['delete'];
$callsign = null;
foreach ($data as $index => $item) {
if ($item['id'] === $deleteId) {
$callsign = $item['callsign'];
unset($data[$index]);
break;
}
}
$data = array_values($data);
if ($callsign === null) {
header("Location: admin.php");
exit;
}
$json['results'] = $data;
$json['count'] = count($data);
file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
$lines = file($datFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$filtered = array_filter($lines, function($line) use ($deleteId, $callsign) {
return trim($line) !== "{$deleteId};{$callsign};";
});
file_put_contents($datFile, implode(PHP_EOL, $filtered) . PHP_EOL);
header("Location: admin.php");
exit;
}
$form_error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$new_id = (int)$_POST['new_id'];
do {
$new_id = rand(1410000, 1419999);
$used = false;
foreach ($data as $row) {
if ($row['id'] == $newId) {
$used = true;
break;
}
}
} while ($used);
$callsign = strtoupper(trim($_POST['new_callsign']));
$fname = trim($_POST['fname'] ?? '');
$surname = trim($_POST['surname'] ?? '');
$city = trim($_POST['city'] ?? '');
$state = trim($_POST['state'] ?? '');
$country = trim($_POST['country'] ?? '');
$remarks = trim($_POST['remarks'] ?? '');
if ($new_id < 100000 || $new_id > 9999999) {
$form_error = 'ID должен быть 6–7 цифр.';
} elseif (!preg_match('/^[A-Z0-9]{4,7}$/', $callsign)) {
$form_error = 'Позывной: 4–7 латинских символов/цифр.';
} elseif (array_filter($data, fn($r) => $r['id'] == $new_id || strtoupper($r['callsign']) === $callsign)) {
$form_error = 'Такой ID или позывной уже существует.';
} elseif (preg_match('/[^a-zA-Z\s]/', $fname . $surname . $city . $state . $country . $remarks)) {
$form_error = 'Дополнительные поля должны содержать только латиницу.';
} else {
$data[] = [
'id' => $new_id,
'callsign' => $callsign,
'fname' => $fname,
'surname' => $surname,
'city' => $city,
'state' => $state,
'country' => $country,
'remarks' => $remarks,
'confirmed' => true
];
$json['results'] = $data;
$json['count'] = count($data);
file_put_contents($file, json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
$dat_line = "{$new_id};{$callsign};" . PHP_EOL;
file_put_contents($datFile, $dat_line, FILE_APPEND);
header("Location: admin.php");
exit;
}
}
$searchQuery = trim($_GET['search'] ?? '');
$confirmed = array_values(array_filter($data, fn($d) => !empty($d['confirmed'])));
if ($searchQuery !== '') {
$confirmed = array_filter($confirmed, function($row) use ($searchQuery) {
foreach (['id', 'callsign', 'fname', 'surname', 'city', 'state', 'country', 'remarks'] as $field) {
if (stripos((string)($row[$field] ?? ''), $searchQuery) !== false) {
return true;
}
}
return false;
});
}
$validSortKeys = ['id', 'callsign', 'fname', 'surname', 'city', 'state', 'country', 'remarks'];
$sortKey = $_GET['sort'] ?? 'id';
$sortOrder = $_GET['order'] ?? 'asc';
if (!in_array($sortKey, $validSortKeys)) {
$sortKey = 'id';
}
usort($confirmed, function($a, $b) use ($sortKey, $sortOrder) {
$valA = $a[$sortKey] ?? '';
$valB = $b[$sortKey] ?? '';
$result = is_numeric($valA) && is_numeric($valB)
? $valA <=> $valB
: strcasecmp((string)$valA, (string)$valB);
return $sortOrder === 'desc' ? -$result : $result;
});
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 15;
$total = count($confirmed);
$totalPages = ceil($total / $perPage);
$offset = ($page - 1) * $perPage;
$currentPageData = array_slice($confirmed, $offset, $perPage);
function sortLink(string $key, string $label): string {
$currentSort = $_GET['sort'] ?? 'id';
$currentOrder = $_GET['order'] ?? 'asc';
$nextOrder = ($currentSort === $key && $currentOrder === 'asc') ? 'desc' : 'asc';
$page = $_GET['page'] ?? 1;
return "<a href=\"?sort=$key&order=$nextOrder&page=$page\">$label</a>";
}
?>