mirror of
https://github.com/getnamingo/registry.git
synced 2025-05-14 00:27:03 +02:00
Work on issue 192
This commit is contained in:
parent
60b682c402
commit
561f8f514e
5 changed files with 262 additions and 76 deletions
189
cp/app/Controllers/DapiController.php
Normal file
189
cp/app/Controllers/DapiController.php
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
|
||||||
|
class DapiController extends Controller
|
||||||
|
{
|
||||||
|
public function listDomains(Request $request, Response $response): Response
|
||||||
|
{
|
||||||
|
$params = $request->getQueryParams();
|
||||||
|
$db = $this->container->get('db');
|
||||||
|
|
||||||
|
// Map fields to fully qualified columns
|
||||||
|
$allowedFieldsMap = [
|
||||||
|
'name' => 'd.name',
|
||||||
|
'crdate' => 'd.crdate',
|
||||||
|
'exdate' => 'd.exdate',
|
||||||
|
'registrant_identifier' => 'c.identifier'
|
||||||
|
];
|
||||||
|
|
||||||
|
// --- SORTING ---
|
||||||
|
$sortField = 'd.crdate'; // default
|
||||||
|
$sortDir = 'desc';
|
||||||
|
if (!empty($params['order'])) {
|
||||||
|
$orderParts = explode(',', $params['order']);
|
||||||
|
if (count($orderParts) === 2) {
|
||||||
|
$fieldCandidate = preg_replace('/[^a-zA-Z0-9_]/', '', $orderParts[0]);
|
||||||
|
if (array_key_exists($fieldCandidate, $allowedFieldsMap)) {
|
||||||
|
$sortField = $allowedFieldsMap[$fieldCandidate];
|
||||||
|
}
|
||||||
|
$sortDir = strtolower($orderParts[1]) === 'asc' ? 'asc' : 'desc';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- PAGINATION ---
|
||||||
|
$page = 1;
|
||||||
|
$size = 10;
|
||||||
|
if (!empty($params['page'])) {
|
||||||
|
$pageParts = explode(',', $params['page']);
|
||||||
|
if (count($pageParts) === 2) {
|
||||||
|
$pageNum = (int)$pageParts[0];
|
||||||
|
$pageSize = (int)$pageParts[1];
|
||||||
|
if ($pageNum > 0) {
|
||||||
|
$page = $pageNum;
|
||||||
|
}
|
||||||
|
if ($pageSize > 0) {
|
||||||
|
$size = $pageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$offset = ($page - 1) * $size;
|
||||||
|
|
||||||
|
// --- FILTERING ---
|
||||||
|
$whereClauses = [];
|
||||||
|
$bindParams = [];
|
||||||
|
foreach ($params as $key => $value) {
|
||||||
|
if (preg_match('/^filter\d+$/', $key)) {
|
||||||
|
$fParts = explode(',', $value);
|
||||||
|
if (count($fParts) === 3) {
|
||||||
|
list($fField, $fOp, $fVal) = $fParts;
|
||||||
|
$fField = preg_replace('/[^a-zA-Z0-9_]/', '', $fField);
|
||||||
|
|
||||||
|
// Ensure the field is allowed and fully qualify it
|
||||||
|
if (!array_key_exists($fField, $allowedFieldsMap)) {
|
||||||
|
// Skip unknown fields
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$column = $allowedFieldsMap[$fField];
|
||||||
|
|
||||||
|
switch ($fOp) {
|
||||||
|
case 'eq':
|
||||||
|
$whereClauses[] = "$column = :f_{$key}";
|
||||||
|
$bindParams["f_{$key}"] = $fVal;
|
||||||
|
break;
|
||||||
|
case 'cs':
|
||||||
|
// If searching in 'name' and user might enter Cyrillic
|
||||||
|
if ($fField === 'name') {
|
||||||
|
// Convert to punycode
|
||||||
|
$punyVal = idn_to_ascii($fVal, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
if ($punyVal !== false && $punyVal !== $fVal) {
|
||||||
|
// Search for both punycode and original term
|
||||||
|
// (d.name LIKE '%cyrillic%' OR d.name LIKE '%punycode%')
|
||||||
|
$whereClauses[] = "($column LIKE :f_{$key}_original OR $column LIKE :f_{$key}_puny)";
|
||||||
|
$bindParams["f_{$key}_original"] = "%$fVal%";
|
||||||
|
$bindParams["f_{$key}_puny"] = "%$punyVal%";
|
||||||
|
} else {
|
||||||
|
// Just search normally
|
||||||
|
$whereClauses[] = "$column LIKE :f_{$key}";
|
||||||
|
$bindParams["f_{$key}"] = "%$fVal%";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Non-name field, just search as usual
|
||||||
|
$whereClauses[] = "$column LIKE :f_{$key}";
|
||||||
|
$bindParams["f_{$key}"] = "%$fVal%";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'sw':
|
||||||
|
$whereClauses[] = "$column LIKE :f_{$key}";
|
||||||
|
$bindParams["f_{$key}"] = "$fVal%";
|
||||||
|
break;
|
||||||
|
case 'ew':
|
||||||
|
$whereClauses[] = "$column LIKE :f_{$key}";
|
||||||
|
$bindParams["f_{$key}"] = "%$fVal";
|
||||||
|
break;
|
||||||
|
// Add other cases if needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base SQL
|
||||||
|
$sqlBase = "
|
||||||
|
FROM domain d
|
||||||
|
LEFT JOIN contact c ON d.registrant = c.id
|
||||||
|
LEFT JOIN domain_status ds ON d.id = ds.domain_id
|
||||||
|
";
|
||||||
|
|
||||||
|
$sqlWhere = '';
|
||||||
|
if (!empty($whereClauses)) {
|
||||||
|
$sqlWhere = "WHERE " . implode(" OR ", $whereClauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count total results
|
||||||
|
$totalSql = "SELECT COUNT(DISTINCT d.id) AS total $sqlBase $sqlWhere";
|
||||||
|
$totalCount = $db->selectValue($totalSql, $bindParams);
|
||||||
|
|
||||||
|
// Data query
|
||||||
|
$selectFields = "
|
||||||
|
d.id,
|
||||||
|
d.name,
|
||||||
|
d.crdate,
|
||||||
|
d.exdate,
|
||||||
|
d.rgpstatus,
|
||||||
|
c.identifier AS registrant_identifier,
|
||||||
|
GROUP_CONCAT(ds.status) AS domain_status
|
||||||
|
";
|
||||||
|
|
||||||
|
$dataSql = "
|
||||||
|
SELECT $selectFields
|
||||||
|
$sqlBase
|
||||||
|
$sqlWhere
|
||||||
|
GROUP BY d.id
|
||||||
|
ORDER BY $sortField $sortDir
|
||||||
|
LIMIT $offset, $size
|
||||||
|
";
|
||||||
|
|
||||||
|
$records = $db->select($dataSql, $bindParams);
|
||||||
|
|
||||||
|
// Ensure records is always an array
|
||||||
|
if (!$records) {
|
||||||
|
$records = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format API results
|
||||||
|
foreach ($records as &$row) {
|
||||||
|
// Check if name is punycode by checking if it starts with 'xn--'
|
||||||
|
if (stripos($row['name'], 'xn--') === 0) {
|
||||||
|
// Convert punycode to Unicode and store it in 'name'
|
||||||
|
$unicode_name = idn_to_utf8($row['name'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
$row['name_o'] = $row['name']; // Keep the original punycode in 'name_o'
|
||||||
|
$row['name'] = $unicode_name; // Store the Unicode version in 'name'
|
||||||
|
} else {
|
||||||
|
// For regular names, both 'name' and 'name_o' are the same
|
||||||
|
$row['name_o'] = $row['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format domain_status as array of {status: '...'} objects
|
||||||
|
if (!empty($row['domain_status'])) {
|
||||||
|
$statuses = explode(',', $row['domain_status']);
|
||||||
|
$row['domain_status'] = array_map(function($status) {
|
||||||
|
return ['status' => $status];
|
||||||
|
}, $statuses);
|
||||||
|
} else {
|
||||||
|
$row['domain_status'] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'records' => $records,
|
||||||
|
'results' => $totalCount
|
||||||
|
];
|
||||||
|
|
||||||
|
$response = $response->withHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
|
$response->getBody()->write(json_encode($payload, JSON_UNESCAPED_UNICODE));
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,24 +21,6 @@
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusFormatter(cell) {
|
|
||||||
var statusArray = cell.getValue();
|
|
||||||
var rowData = cell.getRow().getData(); // Get the entire row data
|
|
||||||
|
|
||||||
// Function to create a badge
|
|
||||||
function createBadge(text, badgeClass) {
|
|
||||||
return `<span class="status status-${badgeClass}">${text}</span>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if statusArray is empty or not
|
|
||||||
if (statusArray && Array.isArray(statusArray) && statusArray.length > 0) {
|
|
||||||
return statusArray.map(item => createBadge(item.status, 'azure')).join(' ');
|
|
||||||
} else {
|
|
||||||
// Fallback to rgpstatus column if statusArray is empty
|
|
||||||
return rowData.rgpstatus ? createBadge(rowData.rgpstatus, 'lime') : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchTerm = ""; // global variable to hold the search term
|
var searchTerm = ""; // global variable to hold the search term
|
||||||
|
|
||||||
function updateSearchTerm(term) {
|
function updateSearchTerm(term) {
|
||||||
|
@ -50,21 +32,29 @@
|
||||||
pagination: true,
|
pagination: true,
|
||||||
paginationMode: "remote",
|
paginationMode: "remote",
|
||||||
paginationSize: 10,
|
paginationSize: 10,
|
||||||
|
sortMode: "remote",
|
||||||
ajaxURL: "/api/records/contact",
|
ajaxURL: "/api/records/contact",
|
||||||
ajaxParams: {
|
|
||||||
join: "contact_status"
|
|
||||||
},
|
|
||||||
ajaxURLGenerator: function(url, config, params) {
|
ajaxURLGenerator: function(url, config, params) {
|
||||||
var queryParts = ["join=contact_status"];
|
var queryParts = [];
|
||||||
|
|
||||||
// Handle search term
|
// Handle search term
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
queryParts.push("filter1=identifier,cs," + encodeURIComponent(searchTerm));
|
queryParts.push("filter1=identifier,cs," + encodeURIComponent(searchTerm));
|
||||||
queryParts.push("filter2=email,cs," + encodeURIComponent(searchTerm));
|
queryParts.push("filter2=email,cs," + encodeURIComponent(searchTerm));
|
||||||
queryParts.push("filter3=voice,cs," + encodeURIComponent(searchTerm));
|
queryParts.push("filter3=voice,cs," + encodeURIComponent(searchTerm));
|
||||||
|
queryParts.push("filter4=crdate,cs," + encodeURIComponent(searchTerm));
|
||||||
}
|
}
|
||||||
|
|
||||||
queryParts.push("order=id");
|
// Handle sorting from Tabulator
|
||||||
|
if (params.sort && params.sort.length > 0) {
|
||||||
|
var sorter = params.sort[0]; // single-column sorting
|
||||||
|
var sortField = encodeURIComponent(sorter.field);
|
||||||
|
var sortDir = (sorter.dir === "asc" ? "asc" : "desc");
|
||||||
|
queryParts.push("order=" + sortField + "," + sortDir);
|
||||||
|
} else {
|
||||||
|
// fallback default order if no sorters
|
||||||
|
queryParts.push("order=id,desc");
|
||||||
|
}
|
||||||
|
|
||||||
// Include pagination parameters
|
// Include pagination parameters
|
||||||
if (params.page) {
|
if (params.page) {
|
||||||
|
@ -85,9 +75,6 @@
|
||||||
return { last_page: 1, data: [] };
|
return { last_page: 1, data: [] };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dataReceiveParams: {
|
|
||||||
"last_page": "results", // Mapping 'results' to 'last_page'
|
|
||||||
},
|
|
||||||
layout:"fitDataFill",
|
layout:"fitDataFill",
|
||||||
responsiveLayout: "collapse",
|
responsiveLayout: "collapse",
|
||||||
responsiveLayoutCollapseStartOpen:false,
|
responsiveLayoutCollapseStartOpen:false,
|
||||||
|
@ -95,10 +82,10 @@
|
||||||
placeholder: "{{ __('No Data') }}",
|
placeholder: "{{ __('No Data') }}",
|
||||||
columns:[
|
columns:[
|
||||||
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
||||||
{title:"{{ __('Identifier') }}", field:"identifier", width:250, resizable:false, headerSort:false, formatter: contactLinkFormatter, responsive:0},
|
{title:"{{ __('Identifier') }}", field:"identifier", width:250, resizable:false, headerSort:true, formatter: contactLinkFormatter, responsive:0},
|
||||||
{title:"{{ __('Email') }}", field:"email", width:300, minWidth:200, resizable:false, headerSort:false, responsive:2},
|
{title:"{{ __('Email') }}", field:"email", width:300, minWidth:200, resizable:false, headerSort:true, responsive:2},
|
||||||
{title:"{{ __('Phone') }}", field:"voice", width:300, minWidth:200, resizable:false, headerSort:false, responsive:2},
|
{title:"{{ __('Phone') }}", field:"voice", width:200, minWidth:100, resizable:false, headerSort:true, responsive:2},
|
||||||
{title:"{{ __('Status') }}", field:"contact_status", width:200, minWidth:100, formatter: statusFormatter, resizable:false, headerSort:false, download:false, responsive:2},
|
{title:"{{ __('Creation Date') }}", field:"crdate", width:280, minWidth:100, resizable:true, headerSort:true, responsive:2},
|
||||||
{title: "{{ __('Actions') }}", formatter: actionsFormatter, resizable:false, headerSort:false, download:false, hozAlign: "center", responsive:0, cellClick: function(e, cell){
|
{title: "{{ __('Actions') }}", formatter: actionsFormatter, resizable:false, headerSort:false, download:false, hozAlign: "center", responsive:0, cellClick: function(e, cell){
|
||||||
if (e.target.closest('.delete-btn')) {
|
if (e.target.closest('.delete-btn')) {
|
||||||
e.preventDefault(); // Prevent the default link behavior
|
e.preventDefault(); // Prevent the default link behavior
|
||||||
|
@ -117,8 +104,13 @@
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
var searchInput = document.getElementById("search-input");
|
var searchInput = document.getElementById("search-input");
|
||||||
|
let searchTimeout;
|
||||||
|
|
||||||
searchInput.addEventListener("input", function () {
|
searchInput.addEventListener("input", function () {
|
||||||
|
clearTimeout(searchTimeout);
|
||||||
|
searchTimeout = setTimeout(() => {
|
||||||
updateSearchTerm(searchInput.value);
|
updateSearchTerm(searchInput.value);
|
||||||
|
}, 300); // 300ms delay
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -47,10 +47,10 @@
|
||||||
|
|
||||||
// Check if statusArray is empty or not
|
// Check if statusArray is empty or not
|
||||||
if (statusArray && Array.isArray(statusArray) && statusArray.length > 0) {
|
if (statusArray && Array.isArray(statusArray) && statusArray.length > 0) {
|
||||||
return statusArray.map(item => createBadge(item.status, 'azure')).join(' ');
|
return statusArray.map(item => createBadge(item.status, 'green')).join(' ');
|
||||||
} else if (rowData.rgpstatus) {
|
} else if (rowData.rgpstatus) {
|
||||||
// Fallback to rgpstatus column if statusArray is empty
|
// Fallback to rgpstatus column if statusArray is empty
|
||||||
return createBadge(rowData.rgpstatus, 'lime');
|
return createBadge(rowData.rgpstatus, 'info');
|
||||||
} else {
|
} else {
|
||||||
// Display 'ok' status with info badge if both statusArray and rgpstatus are empty
|
// Display 'ok' status with info badge if both statusArray and rgpstatus are empty
|
||||||
return createBadge('ok', 'info');
|
return createBadge('ok', 'info');
|
||||||
|
@ -68,22 +68,30 @@
|
||||||
pagination: true,
|
pagination: true,
|
||||||
paginationMode: "remote",
|
paginationMode: "remote",
|
||||||
paginationSize: 10,
|
paginationSize: 10,
|
||||||
ajaxURL: "/api/records/domain",
|
sortMode: "remote",
|
||||||
ajaxParams: {
|
ajaxURL: "/dapi/domains",
|
||||||
join: "contact",
|
|
||||||
join: "domain_status"
|
|
||||||
},
|
|
||||||
ajaxURLGenerator: function(url, config, params) {
|
ajaxURLGenerator: function(url, config, params) {
|
||||||
var queryParts = ["join=contact", "join=domain_status"];
|
var queryParts = [];
|
||||||
|
|
||||||
// Handle search term
|
// Handle search term
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
queryParts.push("filter1=name,cs," + encodeURIComponent(searchTerm));
|
queryParts.push("filter1=name,cs," + encodeURIComponent(searchTerm));
|
||||||
queryParts.push("filter2=crdate,cs," + encodeURIComponent(searchTerm));
|
queryParts.push("filter2=crdate,cs," + encodeURIComponent(searchTerm));
|
||||||
queryParts.push("filter3=exdate,cs," + encodeURIComponent(searchTerm));
|
queryParts.push("filter3=exdate,cs," + encodeURIComponent(searchTerm));
|
||||||
|
queryParts.push("filter4=registrant_identifier,cs," + encodeURIComponent(searchTerm));
|
||||||
|
queryParts.push("filter5=name_o,cs," + encodeURIComponent(searchTerm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle sorting from Tabulator
|
||||||
|
if (params.sort && params.sort.length > 0) {
|
||||||
|
var sorter = params.sort[0]; // single-column sorting
|
||||||
|
var sortField = encodeURIComponent(sorter.field);
|
||||||
|
var sortDir = (sorter.dir === "asc" ? "asc" : "desc");
|
||||||
|
queryParts.push("order=" + sortField + "," + sortDir);
|
||||||
|
} else {
|
||||||
|
// fallback default order if no sorters
|
||||||
queryParts.push("order=crdate,desc");
|
queryParts.push("order=crdate,desc");
|
||||||
|
}
|
||||||
|
|
||||||
// Include pagination parameters
|
// Include pagination parameters
|
||||||
if (params.page) {
|
if (params.page) {
|
||||||
|
@ -104,9 +112,6 @@
|
||||||
return { last_page: 1, data: [] };
|
return { last_page: 1, data: [] };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dataReceiveParams: {
|
|
||||||
"last_page": "results", // Mapping 'results' to 'last_page'
|
|
||||||
},
|
|
||||||
layout:"fitDataFill",
|
layout:"fitDataFill",
|
||||||
responsiveLayout: "collapse",
|
responsiveLayout: "collapse",
|
||||||
responsiveLayoutCollapseStartOpen:false,
|
responsiveLayoutCollapseStartOpen:false,
|
||||||
|
@ -114,11 +119,11 @@
|
||||||
placeholder: "{{ __('No Data') }}",
|
placeholder: "{{ __('No Data') }}",
|
||||||
columns:[
|
columns:[
|
||||||
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
||||||
{title:"{{ __('Name') }}", field:"name", width:200, resizable:false, headerSort:false, formatter: domainLinkFormatter, responsive:0},
|
{title:"{{ __('Name') }}", field:"name", width:200, resizable:false, headerSort:true, formatter: domainLinkFormatter, responsive:0},
|
||||||
{title:"{{ __('Registrant') }}", width:200, field:"registrant.identifier", resizable:false, headerSort:false, responsive:2},
|
{title:"{{ __('Registrant') }}", width:200, field:"registrant_identifier", resizable:false, headerSort:true, responsive:2},
|
||||||
{title:"{{ __('Creation Date') }}", width:250, minWidth:150, field:"crdate", resizable:false, headerSort:false, responsive:2},
|
{title:"{{ __('Creation Date') }}", width:250, minWidth:150, field:"crdate", resizable:false, headerSort:true, responsive:2},
|
||||||
{title:"{{ __('Expiration Date') }}", width:250, minWidth:150, field:"exdate", resizable:false, headerSort:false, responsive:2},
|
{title:"{{ __('Expiration Date') }}", width:250, minWidth:150, field:"exdate", resizable:false, headerSort:true, responsive:2},
|
||||||
{title:"{{ __('Status') }}", width:150, field:"domain_status", formatter: statusFormatter, resizable:false, headerSort:false, download:false, responsive:2},
|
{title:"{{ __('Status') }}", width:150, field:"domain_status", formatter: statusFormatter, resizable:false, headerSort:true, download:false, responsive:2},
|
||||||
{title: "{{ __('Actions') }}", formatter: actionsFormatter, resizable:false, headerSort:false, download:false, hozAlign: "center", responsive:0, cellClick: function(e, cell){
|
{title: "{{ __('Actions') }}", formatter: actionsFormatter, resizable:false, headerSort:false, download:false, hozAlign: "center", responsive:0, cellClick: function(e, cell){
|
||||||
if (e.target.closest('.delete-btn')) {
|
if (e.target.closest('.delete-btn')) {
|
||||||
e.preventDefault(); // Prevent the default link behavior
|
e.preventDefault(); // Prevent the default link behavior
|
||||||
|
@ -161,8 +166,13 @@
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
var searchInput = document.getElementById("search-input");
|
var searchInput = document.getElementById("search-input");
|
||||||
|
let searchTimeout;
|
||||||
|
|
||||||
searchInput.addEventListener("input", function () {
|
searchInput.addEventListener("input", function () {
|
||||||
|
clearTimeout(searchTimeout);
|
||||||
|
searchTimeout = setTimeout(() => {
|
||||||
updateSearchTerm(searchInput.value);
|
updateSearchTerm(searchInput.value);
|
||||||
|
}, 300); // 300ms delay
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -21,24 +21,6 @@
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusFormatter(cell) {
|
|
||||||
var statusArray = cell.getValue();
|
|
||||||
var rowData = cell.getRow().getData(); // Get the entire row data
|
|
||||||
|
|
||||||
// Function to create a badge
|
|
||||||
function createBadge(text, badgeClass) {
|
|
||||||
return `<span class="status status-${badgeClass}">${text}</span>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if statusArray is empty or not
|
|
||||||
if (statusArray && Array.isArray(statusArray) && statusArray.length > 0) {
|
|
||||||
return statusArray.map(item => createBadge(item.status, 'azure')).join(' ');
|
|
||||||
} else {
|
|
||||||
// Fallback to rgpstatus column if statusArray is empty
|
|
||||||
return rowData.rgpstatus ? createBadge(rowData.rgpstatus, 'lime') : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchTerm = ""; // global variable to hold the search term
|
var searchTerm = ""; // global variable to hold the search term
|
||||||
|
|
||||||
function updateSearchTerm(term) {
|
function updateSearchTerm(term) {
|
||||||
|
@ -50,20 +32,28 @@
|
||||||
pagination: true,
|
pagination: true,
|
||||||
paginationMode: "remote",
|
paginationMode: "remote",
|
||||||
paginationSize: 10,
|
paginationSize: 10,
|
||||||
|
sortMode: "remote",
|
||||||
ajaxURL: "/api/records/host",
|
ajaxURL: "/api/records/host",
|
||||||
ajaxParams: {
|
|
||||||
join: "host_status"
|
|
||||||
},
|
|
||||||
ajaxURLGenerator: function(url, config, params) {
|
ajaxURLGenerator: function(url, config, params) {
|
||||||
var queryParts = ["join=host_status"];
|
var queryParts = [];
|
||||||
|
|
||||||
// Handle search term
|
// Handle search term
|
||||||
if (searchTerm) {
|
if (searchTerm) {
|
||||||
queryParts.push("filter1=name,cs," + encodeURIComponent(searchTerm));
|
queryParts.push("filter1=name,cs," + encodeURIComponent(searchTerm));
|
||||||
queryParts.push("filter2=crdate,cs," + encodeURIComponent(searchTerm));
|
queryParts.push("filter2=crdate,cs," + encodeURIComponent(searchTerm));
|
||||||
|
queryParts.push("filter3=lastupdate,cs," + encodeURIComponent(searchTerm));
|
||||||
}
|
}
|
||||||
|
|
||||||
queryParts.push("order=id");
|
// Handle sorting from Tabulator
|
||||||
|
if (params.sort && params.sort.length > 0) {
|
||||||
|
var sorter = params.sort[0]; // single-column sorting
|
||||||
|
var sortField = encodeURIComponent(sorter.field);
|
||||||
|
var sortDir = (sorter.dir === "asc" ? "asc" : "desc");
|
||||||
|
queryParts.push("order=" + sortField + "," + sortDir);
|
||||||
|
} else {
|
||||||
|
// fallback default order if no sorters
|
||||||
|
queryParts.push("order=name,asc");
|
||||||
|
}
|
||||||
|
|
||||||
// Include pagination parameters
|
// Include pagination parameters
|
||||||
if (params.page) {
|
if (params.page) {
|
||||||
|
@ -84,9 +74,6 @@
|
||||||
return { last_page: 1, data: [] };
|
return { last_page: 1, data: [] };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dataReceiveParams: {
|
|
||||||
"last_page": "results", // Mapping 'results' to 'last_page'
|
|
||||||
},
|
|
||||||
layout:"fitDataFill",
|
layout:"fitDataFill",
|
||||||
responsiveLayout: "collapse",
|
responsiveLayout: "collapse",
|
||||||
responsiveLayoutCollapseStartOpen:false,
|
responsiveLayoutCollapseStartOpen:false,
|
||||||
|
@ -94,9 +81,9 @@
|
||||||
placeholder: "{{ __('No Data') }}",
|
placeholder: "{{ __('No Data') }}",
|
||||||
columns:[
|
columns:[
|
||||||
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
{formatter:"responsiveCollapse", width:30, minWidth:30, hozAlign:"center", resizable:false, headerSort:false, responsive:0},
|
||||||
{title:"{{ __('Host Name') }}", field:"name", width:300, resizable:false, headerSort:false, formatter: hostLinkFormatter, responsive:0},
|
{title:"{{ __('Host Name') }}", field:"name", width:300, minWidth:150, resizable:false, headerSort:true, formatter: hostLinkFormatter, responsive:0},
|
||||||
{title:"{{ __('Creation Date') }}", field:"crdate", width:300, minWidth:200, resizable:false, headerSort:false, responsive:2},
|
{title:"{{ __('Creation Date') }}", field:"crdate", width:300, minWidth:200, resizable:false, headerSort:true, responsive:2},
|
||||||
{title:"{{ __('Status') }}", field:"host_status", width:300, minWidth:200, formatter: statusFormatter, resizable:false, headerSort:false, download:false, responsive:2},
|
{title:"{{ __('Last Updated') }}", field:"lastupdate", width:300, minWidth:200, resizable:false, headerSort:true, responsive:2},
|
||||||
{title: "{{ __('Actions') }}", formatter: actionsFormatter, resizable:false, headerSort:false, download:false, hozAlign: "center", responsive:0, cellClick: function(e, cell){
|
{title: "{{ __('Actions') }}", formatter: actionsFormatter, resizable:false, headerSort:false, download:false, hozAlign: "center", responsive:0, cellClick: function(e, cell){
|
||||||
if (e.target.closest('.delete-btn')) {
|
if (e.target.closest('.delete-btn')) {
|
||||||
e.preventDefault(); // Prevent the default link behavior
|
e.preventDefault(); // Prevent the default link behavior
|
||||||
|
@ -116,8 +103,13 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
var searchInput = document.getElementById("search-input");
|
var searchInput = document.getElementById("search-input");
|
||||||
|
let searchTimeout;
|
||||||
|
|
||||||
searchInput.addEventListener("input", function () {
|
searchInput.addEventListener("input", function () {
|
||||||
|
clearTimeout(searchTimeout);
|
||||||
|
searchTimeout = setTimeout(() => {
|
||||||
updateSearchTerm(searchInput.value);
|
updateSearchTerm(searchInput.value);
|
||||||
|
}, 300); // 300ms delay
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ use App\Controllers\ReportsController;
|
||||||
use App\Controllers\ProfileController;
|
use App\Controllers\ProfileController;
|
||||||
use App\Controllers\SystemController;
|
use App\Controllers\SystemController;
|
||||||
use App\Controllers\SupportController;
|
use App\Controllers\SupportController;
|
||||||
|
use App\Controllers\DapiController;
|
||||||
use App\Middleware\AuthMiddleware;
|
use App\Middleware\AuthMiddleware;
|
||||||
use App\Middleware\GuestMiddleware;
|
use App\Middleware\GuestMiddleware;
|
||||||
use Slim\Exception\HttpNotFoundException;
|
use Slim\Exception\HttpNotFoundException;
|
||||||
|
@ -147,6 +148,8 @@ $app->group('', function ($route) {
|
||||||
$route->get('/lang', HomeController::class .':lang')->setName('lang');
|
$route->get('/lang', HomeController::class .':lang')->setName('lang');
|
||||||
$route->get('/logout', AuthController::class . ':logout')->setName('logout');
|
$route->get('/logout', AuthController::class . ':logout')->setName('logout');
|
||||||
$route->post('/change-password', PasswordController::class . ':changePassword')->setName('change.password');
|
$route->post('/change-password', PasswordController::class . ':changePassword')->setName('change.password');
|
||||||
|
|
||||||
|
$route->get('/dapi/domains', [DapiController::class, 'listDomains']);
|
||||||
})->add(new AuthMiddleware($container));
|
})->add(new AuthMiddleware($container));
|
||||||
|
|
||||||
$app->any('/api[/{params:.*}]', function (
|
$app->any('/api[/{params:.*}]', function (
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue