diff --git a/public/admin.html b/public/admin.html index 73bf0f9..9f3b853 100644 --- a/public/admin.html +++ b/public/admin.html @@ -558,6 +558,7 @@ + diff --git a/public/admin.js b/public/admin.js index 5a20dce..a616e0d 100644 --- a/public/admin.js +++ b/public/admin.js @@ -492,28 +492,7 @@ document.addEventListener('DOMContentLoaded', () => { } }; - function getTimeAgo(timestamp) { - const now = new Date(); - // Ensure timestamp is treated as UTC if it doesn't have timezone info - const reportTime = parseUTCDate(timestamp); - const diffInMinutes = Math.floor((now - reportTime) / (1000 * 60)); - - if (diffInMinutes < 1) return 'just now'; - if (diffInMinutes < 60) return `${diffInMinutes} minute${diffInMinutes !== 1 ? 's' : ''} ago`; - - const diffInHours = Math.floor(diffInMinutes / 60); - if (diffInHours < 24) return `${diffInHours} hour${diffInHours !== 1 ? 's' : ''} ago`; - - const diffInDays = Math.floor(diffInHours / 24); - if (diffInDays < 7) return `${diffInDays} day${diffInDays !== 1 ? 's' : ''} ago`; - - return reportTime.toLocaleDateString(); - } - - // Helper function to parse UTC date - function parseUTCDate(timestamp) { - return new Date(timestamp.includes('T') ? timestamp : timestamp + 'Z'); - } + // getTimeAgo and parseUTCDate functions are now available from utils.js // Profanity management functions async function loadProfanityWords() { diff --git a/public/app-google.js b/public/app-google.js index 1db2981..411efe8 100644 --- a/public/app-google.js +++ b/public/app-google.js @@ -82,25 +82,7 @@ document.addEventListener('DOMContentLoaded', async () => { }); }; - // Helper function to parse UTC date - const parseUTCDate = (timestamp) => { - return new Date(timestamp.includes('T') ? timestamp : timestamp + 'Z'); - }; - - const getTimeAgo = (timestamp) => { - const now = new Date(); - // Ensure timestamp is treated as UTC if it doesn't have timezone info - const reportTime = parseUTCDate(timestamp); - const diffInMinutes = Math.floor((now - reportTime) / (1000 * 60)); - - if (diffInMinutes < 1) return 'just now'; - if (diffInMinutes < 60) return `${diffInMinutes} minute${diffInMinutes !== 1 ? 's' : ''} ago`; - - const diffInHours = Math.floor(diffInMinutes / 60); - if (diffInHours < 24) return `${diffInHours} hour${diffInHours !== 1 ? 's' : ''} ago`; - - return 'over a day ago'; - }; + // getTimeAgo function is now available from utils.js const refreshLocations = () => { fetch('/api/locations') diff --git a/public/app-mapbox.js b/public/app-mapbox.js index dcff11f..55d74d6 100644 --- a/public/app-mapbox.js +++ b/public/app-mapbox.js @@ -93,25 +93,7 @@ document.addEventListener('DOMContentLoaded', async () => { }); }; - // Helper function to parse UTC date - const parseUTCDate = (timestamp) => { - return new Date(timestamp.includes('T') ? timestamp : timestamp + 'Z'); - }; - - const getTimeAgo = (timestamp) => { - const now = new Date(); - // Ensure timestamp is treated as UTC if it doesn't have timezone info - const reportTime = parseUTCDate(timestamp); - const diffInMinutes = Math.floor((now - reportTime) / (1000 * 60)); - - if (diffInMinutes < 1) return 'just now'; - if (diffInMinutes < 60) return `${diffInMinutes} minute${diffInMinutes !== 1 ? 's' : ''} ago`; - - const diffInHours = Math.floor(diffInMinutes / 60); - if (diffInHours < 24) return `${diffInHours} hour${diffInHours !== 1 ? 's' : ''} ago`; - - return 'over a day ago'; - }; + // getTimeAgo function is now available from utils.js // Toggle between map and table view const switchView = (viewType) => { @@ -171,47 +153,7 @@ document.addEventListener('DOMContentLoaded', async () => { tableLocationCount.textContent = `${locations.length} active report${locations.length !== 1 ? 's' : ''}`; }; - // Calculate time remaining until 48-hour expiration - const getTimeRemaining = (timestamp, persistent = false) => { - if (persistent) { - return 'Persistent'; - } - - const now = new Date(); - // Ensure timestamp is treated as UTC if it doesn't have timezone info - const reportTime = parseUTCDate(timestamp); - const expirationTime = new Date(reportTime.getTime() + 48 * 60 * 60 * 1000); - const remaining = expirationTime - now; - - if (remaining <= 0) return 'Expired'; - - const hours = Math.floor(remaining / (1000 * 60 * 60)); - const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60)); - - if (hours > 0) { - return `${hours}h ${minutes}m`; - } else { - return `${minutes}m`; - } - }; - - // Get CSS class for time remaining - const getRemainingClass = (timestamp, persistent = false) => { - if (persistent) { - return 'normal'; // Use normal styling for persistent reports - } - - const now = new Date(); - // Ensure timestamp is treated as UTC if it doesn't have timezone info - const reportTime = parseUTCDate(timestamp); - const expirationTime = new Date(reportTime.getTime() + 48 * 60 * 60 * 1000); - const remaining = expirationTime - now; - const hoursRemaining = remaining / (1000 * 60 * 60); - - if (hoursRemaining <= 1) return 'urgent'; - if (hoursRemaining <= 6) return 'warning'; - return 'normal'; - }; + // getTimeRemaining and getRemainingClass functions are now available from utils.js const refreshLocations = () => { fetch('/api/locations') diff --git a/public/app.js b/public/app.js index 84d0933..23d1c07 100644 --- a/public/app.js +++ b/public/app.js @@ -74,25 +74,7 @@ document.addEventListener('DOMContentLoaded', () => { }); }; - // Helper function to parse UTC date - const parseUTCDate = (timestamp) => { - return new Date(timestamp.includes('T') ? timestamp : timestamp + 'Z'); - }; - - const getTimeAgo = (timestamp) => { - const now = new Date(); - // Ensure timestamp is treated as UTC if it doesn't have timezone info - const reportTime = parseUTCDate(timestamp); - const diffInMinutes = Math.floor((now - reportTime) / (1000 * 60)); - - if (diffInMinutes < 1) return 'just now'; - if (diffInMinutes < 60) return `${diffInMinutes} minute${diffInMinutes !== 1 ? 's' : ''} ago`; - - const diffInHours = Math.floor(diffInMinutes / 60); - if (diffInHours < 24) return `${diffInHours} hour${diffInHours !== 1 ? 's' : ''} ago`; - - return 'over a day ago'; - }; + // getTimeAgo function is now available from utils.js const refreshLocations = () => { fetch('/api/locations') diff --git a/public/index.html b/public/index.html index eeb5baf..61bbf3c 100644 --- a/public/index.html +++ b/public/index.html @@ -109,6 +109,7 @@ placeholder="Enter address, intersection (e.g., Main St & Second St, City), or l + diff --git a/public/utils.js b/public/utils.js new file mode 100644 index 0000000..3a0427b --- /dev/null +++ b/public/utils.js @@ -0,0 +1,96 @@ +/** + * Shared utility functions for the Great Lakes Ice Report frontend + */ + +/** + * Helper function to parse UTC date consistently across all frontend scripts + * Ensures timestamp is treated as UTC if it doesn't have timezone info + * @param {string} timestamp - The timestamp string to parse + * @returns {Date} - Parsed Date object with proper UTC interpretation + */ +const parseUTCDate = (timestamp) => { + return new Date(timestamp.includes('T') ? timestamp : timestamp + 'Z'); +}; + +/** + * Calculate human-readable time ago string + * @param {string} timestamp - The timestamp to calculate from + * @returns {string} - Human-readable time difference + */ +const getTimeAgo = (timestamp) => { + const now = new Date(); + const reportTime = parseUTCDate(timestamp); + const diffInMinutes = Math.floor((now - reportTime) / (1000 * 60)); + + if (diffInMinutes < 1) return 'just now'; + if (diffInMinutes < 60) return `${diffInMinutes} minute${diffInMinutes !== 1 ? 's' : ''} ago`; + + const diffInHours = Math.floor(diffInMinutes / 60); + if (diffInHours < 24) return `${diffInHours} hour${diffInHours !== 1 ? 's' : ''} ago`; + + const diffInDays = Math.floor(diffInHours / 24); + if (diffInDays < 7) return `${diffInDays} day${diffInDays !== 1 ? 's' : ''} ago`; + + return reportTime.toLocaleDateString(); +}; + +/** + * Calculate time remaining until 48-hour expiration + * @param {string} timestamp - The creation timestamp + * @param {boolean} persistent - Whether the report is persistent + * @returns {string} - Time remaining string + */ +const getTimeRemaining = (timestamp, persistent = false) => { + if (persistent) { + return 'Persistent'; + } + + const now = new Date(); + const reportTime = parseUTCDate(timestamp); + const expirationTime = new Date(reportTime.getTime() + 48 * 60 * 60 * 1000); + const remaining = expirationTime - now; + + if (remaining <= 0) return 'Expired'; + + const hours = Math.floor(remaining / (1000 * 60 * 60)); + const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60)); + + if (hours > 0) { + return `${hours}h ${minutes}m`; + } else { + return `${minutes}m`; + } +}; + +/** + * Get CSS class for time remaining styling + * @param {string} timestamp - The creation timestamp + * @param {boolean} persistent - Whether the report is persistent + * @returns {string} - CSS class name + */ +const getRemainingClass = (timestamp, persistent = false) => { + if (persistent) { + return 'normal'; // Use normal styling for persistent reports + } + + const now = new Date(); + const reportTime = parseUTCDate(timestamp); + const expirationTime = new Date(reportTime.getTime() + 48 * 60 * 60 * 1000); + const remaining = expirationTime - now; + const hoursRemaining = remaining / (1000 * 60 * 60); + + if (hoursRemaining <= 1) return 'urgent'; + if (hoursRemaining <= 6) return 'warning'; + return 'normal'; +}; + +// Export functions for module usage (if using ES6 modules in the future) +// For now, functions are available globally when script is included +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + parseUTCDate, + getTimeAgo, + getTimeRemaining, + getRemainingClass + }; +}