Adjusted context to hopefully reflect accurate timezones relative to browser.
All checks were successful
Deploy Frontend / deploy (push) Successful in 40s
All checks were successful
Deploy Frontend / deploy (push) Successful in 40s
This commit is contained in:
17
package-lock.json
generated
17
package-lock.json
generated
@@ -310,6 +310,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@@ -353,6 +354,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz",
|
||||
"integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@@ -1201,6 +1203,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.0.2.tgz",
|
||||
"integrity": "sha512-rjJlJ13+3LdLfobRplkXbjIFEIkn6LgpetgU/Cs3Xd8qINCCQK9qXQIjjQ6P0FXFTPFzEVMj0VgBR1mN+FhOcA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.0",
|
||||
"@mui/core-downloads-tracker": "^7.0.2",
|
||||
@@ -1980,6 +1983,7 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
|
||||
"integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
@@ -2487,6 +2491,7 @@
|
||||
"integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
@@ -2508,6 +2513,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz",
|
||||
"integrity": "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -2517,6 +2523,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
|
||||
"integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.0.0"
|
||||
}
|
||||
@@ -2575,6 +2582,7 @@
|
||||
"integrity": "sha512-8C0+jlNJOwQso2GapCVWWfW/rzaq7Lbme+vGUFKE31djwNncIpgXD7Cd4weEsDdkoZDjH0lwwr3QDQFuyrMg9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.29.0",
|
||||
"@typescript-eslint/types": "8.29.0",
|
||||
@@ -3106,6 +3114,7 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -3576,6 +3585,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001688",
|
||||
"electron-to-chromium": "^1.5.73",
|
||||
@@ -4353,6 +4363,7 @@
|
||||
"integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -4527,6 +4538,7 @@
|
||||
"integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.8",
|
||||
@@ -6977,6 +6989,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.8",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -7077,6 +7090,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -7086,6 +7100,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
|
||||
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.26.0"
|
||||
},
|
||||
@@ -7966,6 +7981,7 @@
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -8225,6 +8241,7 @@
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
|
||||
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
@@ -32,14 +32,24 @@ const ONLINE_THRESHOLD_MINUTES = 1440;
|
||||
|
||||
|
||||
function getStatusColor(lastCheckedIn: string) {
|
||||
const lastSeen = parseISO(lastCheckedIn);
|
||||
// Parse UTC timestamp and treat it correctly as UTC
|
||||
const lastSeen = new Date(lastCheckedIn + 'Z'); // Add Z to indicate UTC
|
||||
const minutesAgo = (Date.now() - lastSeen.getTime()) / 1000 / 60;
|
||||
return minutesAgo < ONLINE_THRESHOLD_MINUTES ? 'green' : 'grey';
|
||||
}
|
||||
|
||||
function formatRelativeTime(lastCheckedIn: string) {
|
||||
try {
|
||||
return formatDistanceToNowStrict(parseISO(lastCheckedIn), { addSuffix: true });
|
||||
// Parse UTC timestamp and treat it correctly as UTC
|
||||
const lastSeen = new Date(lastCheckedIn + 'Z'); // Add Z to indicate UTC
|
||||
|
||||
// Debug: Log what we're getting vs what we should get
|
||||
//const wrongWay = new Date(lastCheckedIn); // Without Z - treats as local
|
||||
//console.log(`🕒 DEBUG: "${lastCheckedIn}"`);
|
||||
//console.log(`🕒 Wrong way (as local): ${wrongWay.toLocaleString()} -> ${formatDistanceToNowStrict(wrongWay, { addSuffix: true })}`);
|
||||
//console.log(`🕒 Right way (as UTC): ${lastSeen.toLocaleString()} -> ${formatDistanceToNowStrict(lastSeen, { addSuffix: true })}`);
|
||||
|
||||
return formatDistanceToNowStrict(lastSeen, { addSuffix: true });
|
||||
} catch {
|
||||
return 'Unknown';
|
||||
}
|
||||
@@ -56,7 +66,7 @@ const DeviceListSidebar: React.FC<DeviceListSidebarProps> = ({
|
||||
onTogglePin,
|
||||
}) => {
|
||||
const [sortAsc, setSortAsc] = React.useState(true);
|
||||
const [searchQuery, setSearchQuery] = React.useState('');
|
||||
const [searchQuery, setSearchQuery] = React.useState('');
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -81,11 +81,12 @@ export default function DeviceTableSection({ initialDevices }: Props) {
|
||||
|
||||
|
||||
const formatLastCheckedIn = (isoString: string): string => {
|
||||
const date = new Date(isoString);
|
||||
// Parse as UTC by adding 'Z' if not present
|
||||
const date = new Date(isoString + (isoString.includes('Z') || isoString.includes('+') ? '' : 'Z'));
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
||||
|
||||
|
||||
const formatted = date.toLocaleString('en-AU', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
@@ -94,7 +95,7 @@ export default function DeviceTableSection({ initialDevices }: Props) {
|
||||
minute: '2-digit',
|
||||
hour12: true,
|
||||
});
|
||||
|
||||
|
||||
return `${formatted} (${diffHours} hours ago)`;
|
||||
};
|
||||
|
||||
|
||||
@@ -127,7 +127,16 @@ export default function DevicesClient() {
|
||||
|
||||
const formatBootTime = (timestamp: string) => {
|
||||
try {
|
||||
const parsed = parseISO(timestamp);
|
||||
// Handle timestamp - could be UTC or already have timezone info
|
||||
let parsed: Date;
|
||||
if (timestamp.includes('+') || timestamp.endsWith('Z')) {
|
||||
// Already has timezone info, parse directly
|
||||
parsed = new Date(timestamp);
|
||||
} else {
|
||||
// Assume UTC, add Z to indicate UTC
|
||||
parsed = new Date(timestamp + 'Z');
|
||||
}
|
||||
|
||||
const formattedDate = format(parsed, 'EEEE do MMMM yyyy');
|
||||
const relative = formatDistanceToNowStrict(parsed, { addSuffix: true });
|
||||
return `${formattedDate} (${relative})`;
|
||||
|
||||
@@ -7,6 +7,13 @@ import { DetailedDevice } from '@/types/devices';
|
||||
import { disableConsoleInProd } from '@/lib/disableConsole';
|
||||
disableConsoleInProd();
|
||||
|
||||
// Helper function to convert UTC timestamps to local time
|
||||
const convertUtcToLocal = (utcString: string | null): string | null => {
|
||||
if (!utcString) return null;
|
||||
// Add 'Z' to indicate it's UTC, then convert to local time
|
||||
return new Date(utcString + 'Z').toLocaleString();
|
||||
};
|
||||
|
||||
|
||||
|
||||
interface DriveInfo {
|
||||
@@ -125,7 +132,7 @@ export const DeviceProvider = ({
|
||||
console.log('✅ Software fetched:', softwareRes.data);
|
||||
console.groupEnd();
|
||||
|
||||
// STEP 1: Set basic data
|
||||
// STEP 1: Set basic data (keep original UTC timestamps for calculations)
|
||||
setDevices(devicesRes.data.devices);
|
||||
setDeviceVulns(devicesRes.data.vulnerabilitiesByDevice);
|
||||
setCachedSoftware(softwareRes.data);
|
||||
|
||||
Reference in New Issue
Block a user