diff --git a/web/index.html b/web/index.html index e05f57b..270fcc5 100644 --- a/web/index.html +++ b/web/index.html @@ -5,28 +5,27 @@ IP History - - +

IP History

- +
- - - + + +
TimestampIPv4 AddressIPv6 AddressTimestampIPv4 AddressIPv6 Address
diff --git a/web/script.js b/web/script.js index d943438..7c77c02 100644 --- a/web/script.js +++ b/web/script.js @@ -1,43 +1,59 @@ -let ipHistory = [], - filteredHistory = [], - currentPage = 1, - entriesPerPage = 10; +let ipHistory = []; +let filteredHistory = []; +let currentPage = 1; +let entriesPerPage = 10; -$(document).ready(function() { - $.getJSON('/history', function(data) { - ipHistory = data; - filteredHistory = ipHistory; - displayTable(); - updateTotalIPs(); - }).fail(function() { - console.error('Error fetching IP history'); - }); +document.addEventListener('DOMContentLoaded', () => { + fetch('/history') + .then(response => response.json()) + .then(data => { + ipHistory = data; + filteredHistory = ipHistory; + displayTable(); + updateTotalIPs(); + }) + .catch(error => console.error('Error fetching IP history:', error)); - $('#searchBar').on('input', function() { - const query = $(this).val().toLowerCase(); - filteredHistory = ipHistory.filter(entry => - entry.timestamp.toLowerCase().includes(query) || - (entry.ipv4 && entry.ipv4.toLowerCase().includes(query)) || - (entry.ipv6 && entry.ipv6.toLowerCase().includes(query)) - ); - currentPage = 1; - displayTable(); - updateTotalIPs(); - }); + const searchBar = document.getElementById('searchBar'); + searchBar.addEventListener('input', handleSearch); + + document.getElementById('prevBtn').addEventListener('click', prevPage); + document.getElementById('nextBtn').addEventListener('click', nextPage); + document.getElementById('exportBtn').addEventListener('click', exportToCSV); + document.getElementById('sortTimestamp').addEventListener('click', () => sortTable('timestamp')); + document.getElementById('sortIPv4').addEventListener('click', () => sortTable('ipv4')); + document.getElementById('sortIPv6').addEventListener('click', () => sortTable('ipv6')); }); + +function handleSearch() { + const query = this.value.toLowerCase(); + filteredHistory = ipHistory.filter(entry => + entry.timestamp.toLowerCase().includes(query) || + (entry.ipv4 && entry.ipv4.toLowerCase().includes(query)) || + (entry.ipv6 && entry.ipv6.toLowerCase().includes(query)) + ); + currentPage = 1; + displayTable(); + updateTotalIPs(); +} + function displayTable() { - const tbody = $('#ipTable tbody').empty(); + const tbody = document.querySelector('#ipTable tbody'); + tbody.innerHTML = ''; + const start = (currentPage - 1) * entriesPerPage; const end = start + entriesPerPage; const currentEntries = filteredHistory.slice(start, end); currentEntries.forEach(entry => { - const tr = $(''); - tr.append($('').text(entry.timestamp)); - tr.append($('').text(entry.ipv4 || 'N/A').css('min-width', '120px')); // Set static width - tr.append($('').text(entry.ipv6 || 'N/A').css('min-width', '180px')); // Set static width - tbody.append(tr); + const row = document.createElement('tr'); + row.innerHTML = ` + ${entry.timestamp} + ${entry.ipv4 || 'N/A'} + ${entry.ipv6 || 'N/A'} + `; + tbody.appendChild(row); }); updatePaginationButtons(); updateTotalIPs(); @@ -45,12 +61,15 @@ function displayTable() { function updateTotalIPs() { const totalIPs = filteredHistory.length; - $('#searchBar').attr('placeholder', `Search IP history... (Total IPs: ${totalIPs})`); + const searchBar = document.getElementById('searchBar'); + searchBar.placeholder = `Search IP history... (Total IPs: ${totalIPs})`; } function updatePaginationButtons() { - $('#prevBtn').prop('disabled', currentPage === 1); - $('#nextBtn').prop('disabled', currentPage === Math.ceil(filteredHistory.length / entriesPerPage)); + const prevBtn = document.getElementById('prevBtn'); + const nextBtn = document.getElementById('nextBtn'); + prevBtn.disabled = currentPage === 1; + nextBtn.disabled = currentPage === Math.ceil(filteredHistory.length / entriesPerPage); } function nextPage() { @@ -68,18 +87,12 @@ function prevPage() { } function sortTable(field) { - const isAscending = $(`#ipTable th:contains(${field.charAt(0).toUpperCase() + field.slice(1)})`).hasClass('sort-asc'); - const orderModifier = isAscending ? 1 : -1; + const isAscending = document.getElementById(`sort${capitalizeFirstLetter(field)}`).classList.contains('sort-asc'); + filteredHistory.sort((a, b) => (a[field] && b[field]) ? a[field].localeCompare(b[field]) * (isAscending ? 1 : -1) : 0); - filteredHistory.sort((a, b) => { - if (a[field] && b[field]) { - return (a[field].localeCompare(b[field])) * orderModifier; - } - return 0; - }); - - $('#ipTable th').removeClass('sort-asc sort-desc'); - $(`#ipTable th:contains(${field.charAt(0).toUpperCase() + field.slice(1)})`).addClass(isAscending ? 'sort-desc' : 'sort-asc'); + // Toggle the sort direction class + document.querySelectorAll('th').forEach(th => th.classList.remove('sort-asc', 'sort-desc')); + document.getElementById(`sort${capitalizeFirstLetter(field)}`).classList.add(isAscending ? 'sort-desc' : 'sort-asc'); displayTable(); } @@ -89,14 +102,14 @@ function exportToCSV() { ['Timestamp', 'IPv4 Address', 'IPv6 Address'], ...filteredHistory.map(entry => [entry.timestamp, entry.ipv4 || '', entry.ipv6 || '']) ]; - const csvContent = "data:text/csv;charset=utf-8," + rows.map(e => e.join(",")).join("\n"); - const encodedUri = encodeURI(csvContent); - const link = $('').attr({ - href: encodedUri, - download: 'ip_history.csv' - }); - - $('body').append(link); - link[0].click(); - link.remove(); + const csvContent = rows.map(row => row.join(',')).join('\n'); + const blob = new Blob([csvContent], { type: 'text/csv' }); + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = 'ip_history.csv'; + link.click(); +} + +function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1); } diff --git a/web/style.css b/web/style.css index 44e45e9..d8d168f 100644 --- a/web/style.css +++ b/web/style.css @@ -1,148 +1,221 @@ +/* General */ * { - -webkit-tap-highlight-color: transparent; - -webkit-touch-callout: none; - touch-action: manipulation; + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Poppins', Arial, sans-serif; + transition: all 0.3s ease; } body { - font-family: Arial, sans-serif; - background-color: #222; - color: #fff; - margin: 0; - padding: 0; + background-color: #121212; + color: #e0e0e0; display: flex; flex-direction: column; align-items: center; + padding: 20px; + min-height: 100vh; } + +/* Typography */ h1 { - text-align: center; - margin: 20px 0; -} -h1 a { + font-size: 2.5rem; + margin-bottom: 20px; color: #007bff; + text-align: center; + font-weight: 700; +} + +h1 a { + color: inherit; text-decoration: none; } + h1 a:hover { - text-decoration: underline; + color: #0056b3; } + +/* Controls */ .controls { - width: 90%; - max-width: 800px; + width: 100%; + max-width: 900px; display: flex; justify-content: space-between; align-items: center; - margin: 20px 0; -} -.export, .search-bar { - padding: 10px 20px; - border-radius: 5px; - border: 1px solid #444; - font-size: 16px; - line-height: 24px; + margin-bottom: 20px; + flex-wrap: wrap; } + .export { background-color: #007bff; + border: 1px solid #007bff; color: #fff; + padding: 12px 24px; + border-radius: 5px; + font-size: 16px; cursor: pointer; - flex-shrink: 0; + flex: 0 1 30%; text-align: center; - border: none; + transition: background-color 0.2s; } + .export:hover { background-color: #0056b3; + border-color: #0056b3; } + .search-bar { - flex-grow: 1; - margin-left: 20px; + flex: 0 1 65%; + padding: 12px; + font-size: 16px; + border-radius: 5px; background-color: #333; - color: #fff; - border-color: #444; + color: #e0e0e0; + border: 1px solid #444; } + .search-bar:focus { outline: none; border-color: #007bff; } + +/* Table */ table { - width: 90%; - max-width: 1200px; + width: 100%; + max-width: 900px; border-collapse: collapse; - margin: 0 auto 20px; + margin: 20px auto; + background-color: #1e1e1e; border-radius: 10px; overflow: hidden; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); - background-color: #333; - table-layout: fixed; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4); } + th, td { - border: 1px solid #444; - padding: 12px; + padding: 16px 20px; text-align: left; - word-wrap: break-word; - overflow-wrap: break-word; + border-bottom: 1px solid #333; + font-size: 16px; + word-break: break-all; } + th { - cursor: pointer; background-color: #007bff; color: #fff; - border: none; -} -th.sort-asc::after { - content: " (newest first)"; - color: #fff; + font-weight: 600; + cursor: pointer; + position: relative; } + +th.sort-asc::after, th.sort-desc::after { - content: " (oldest first)"; + content: " ▼"; + font-size: 12px; + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); color: #fff; } -tbody tr:nth-child(even) { - background-color: #444; + +th.sort-asc::after { + content: " ▲"; } + +tr:nth-child(even) { + background-color: #292929; +} + +tr:hover { + background-color: #333; + transition: background-color 0.2s ease; +} + +/* Pagination */ .pagination { - margin: 20px; + display: flex; + justify-content: center; + margin: 20px 0; } + .pagination button { background-color: #007bff; color: #fff; border: none; - padding: 8px 16px; - cursor: pointer; - border-radius: 5px; + padding: 10px 20px; margin: 0 5px; + border-radius: 5px; + cursor: pointer; + font-size: 16px; } -.pagination button:hover { - background-color: #0056b3; -} + .pagination button:disabled { background-color: #555; cursor: not-allowed; } -@media (max-width: 600px) { - th, td { - padding: 8px; - font-size: 14px; - } - table { - font-size: 14px; - } - .export, .pagination button { - padding: 8px 12px; - } - .search-bar { - margin-left: 10px; - padding: 8px; - font-size: 14px; +.pagination button:hover:not(:disabled) { + background-color: #0056b3; +} + +/* Responsive Design */ +@media (max-width: 768px) { + h1 { + font-size: 2rem; } + .controls { flex-direction: column; - align-items: stretch; + align-items: center; } - .export { + + .export, .search-bar { width: 100%; margin-bottom: 10px; } - .search-bar { - width: 100%; - margin-left: 0; + + th, td { + padding: 10px; + font-size: 14px; + } + + table { + font-size: 14px; + } +} + +@media (max-width: 480px) { + h1 { + font-size: 1.8rem; + } + + .controls { + width: 100%; + text-align: center; + } + + .export, .search-bar { + width: 100%; + font-size: 14px; + padding: 10px; + margin-bottom: 10px; + } + + table { + font-size: 12px; + } + + th, td { + font-size: 12px; + padding: 8px 10px; + } + + th, td { + word-wrap: break-word; + word-break: break-all; + } + + .pagination button { + padding: 8px 12px; } }