/** * Facility status (comments) manager for adding, removing and editing user comments. */ // Create a namespace to avoid global scope conflicts with facilityData.js const CommentsManager = { // Initialization states state: { isInitializing: false, isInitialised: false, isDomReady: false, isAuthReady: false }, /** * initialise status functionality */ initialise() { if (this.state.isInitialised) return; console.log('Initializing comments...'); // initialise comment modal handlers this.initialiseCommentModals(); // Set up form handlers this.setupCommentFormHandlers(); console.log('Comments initialised with auth state:', { isAuthenticated: this.isAuthenticated(), user: window.auth.getUser() }); this.state.isInitialised = true; }, /** * Check if initialisation possible */ checkinitialise() { if (this.state.isDomReady && this.state.isAuthReady && !this.state.isInitializing) { this.state.isInitializing = true; this.initialise(); this.state.isInitializing = false; } }, /** * Check if user is authenticated */ isAuthenticated() { return window.auth && window.auth.isAuthenticated(); }, /** * initialise comment modals */ initialiseCommentModals() { // Status modal (comments view) const statusModal = document.getElementById('statusModal'); if (statusModal) { statusModal.addEventListener('show.bs.modal', (event) => { // Get facility ID from either the button or the modal's data attribute let facilityId; // First try to get it from the button that triggered the modal if (event.relatedTarget) { facilityId = event.relatedTarget.getAttribute('data-facility-id'); } // If not found in button, try the modal's data attribute if (!facilityId && statusModal.hasAttribute('data-facility-id')) { facilityId = statusModal.getAttribute('data-facility-id'); } if (!facilityId) { console.error('No facility ID found for comments'); return; } // Store the facility ID on the modal for later use statusModal.setAttribute('data-facility-id', facilityId); // Load facility comments this.loadFacilityComments(facilityId); }); } // Edit comment modal const editCommentModal = document.getElementById('editCommentModal'); if (editCommentModal) { editCommentModal.addEventListener('show.bs.modal', (event) => { const button = event.relatedTarget; const commentId = button.getAttribute('data-comment-id'); const commentText = button.getAttribute('data-comment-text'); // Set the comment ID and text in the form const editForm = document.getElementById('editCommentForm'); if (editForm) { const commentIdInput = editForm.querySelector('#editCommentId'); const commentTextArea = editForm.querySelector('#editCommentText'); if (commentIdInput && commentTextArea) { commentIdInput.value = commentId; commentTextArea.value = commentText; } } }); } }, /** * Set up comment form handlers */ setupCommentFormHandlers() { // Comment form handler const commentForm = document.getElementById('commentForm'); if (commentForm) { this.setupCommentFormHandler(commentForm); } // Edit comment form handler const editCommentForm = document.getElementById('editCommentForm'); if (editCommentForm) { this.setupEditCommentFormHandler(editCommentForm); } }, /** * Set up a single comment form handler */ setupCommentFormHandler(commentForm) { commentForm.addEventListener('submit', async (e) => { e.preventDefault(); // Prevent duplicate submissions if (commentForm.submitting) { return; } commentForm.submitting = true; // Check if user is authenticated if (!this.isAuthenticated()) { alert('You must be logged in to add comments'); commentForm.submitting = false; return; } const formData = new FormData(commentForm); // Get form data and ensure proper types const statusComment = formData.get('commentText'); const facilityId = formData.get('facilityId'); // Validate form data if (!facilityId) { console.error('No facility ID found in form'); alert('Error: No facility ID found'); commentForm.submitting = false; return; } if (!statusComment) { alert('Please enter a comment'); commentForm.submitting = false; return; } try { // Use the API client to add a status comment const data = await window.api.addFacilityStatus(facilityId.toString(), statusComment); if (data.success) { // Reset the form commentForm.reset(); // Reload comments to show the new one this.loadFacilityComments(facilityId.toString()); } else { console.error('Comment failed:', data.error); alert(data.error || 'Failed to add comment'); } } catch (error) { console.error('Error adding comment:', error); alert('Failed to add comment: ' + error.message); } finally { commentForm.submitting = false; } }); }, /** * Set up a single edit comment form handler */ setupEditCommentFormHandler(editCommentForm) { editCommentForm.addEventListener('submit', async (e) => { e.preventDefault(); // Prevent duplicate submissions if (editCommentForm.submitting) { return; } editCommentForm.submitting = true; // Check if user is authenticated if (!this.isAuthenticated()) { alert('You must be logged in to edit comments'); editCommentForm.submitting = false; return; } const formData = new FormData(editCommentForm); // Get form data const commentText = formData.get('editCommentText'); const commentId = formData.get('commentId'); const facilityId = document.getElementById('commentFacilityId').value; console.log('Edit comment form data:', { commentId, facilityId, commentText }); try { console.log('Sending edit comment request...'); // Use the API client to update a status comment const data = await window.api.updateFacilityStatus(commentId, commentText, facilityId); console.log('Edit comment response:', data); if (data.success) { console.log('Comment edited successfully'); // Close the edit modal const editModal = bootstrap.Modal.getInstance(document.getElementById('editCommentModal')); if (editModal) { editModal.hide(); } // Reload comments to show the updated one this.loadFacilityComments(facilityId); } else { console.error('Edit comment failed:', data.error); alert(data.error || 'Failed to edit comment'); } } catch (error) { console.error('Error editing comment:', error); alert('Failed to edit comment: ' + error.message); } finally { editCommentForm.submitting = false; } }); }, /** * Creates a comment form dynamically for authenticated users */ createCommentFormForAuthenticatedUser(facilityId) { // First check if auth is available if (!window.auth) { return `
Loading authentication status...
`; } // Validate authentication state try { const token = window.auth.getToken(); const user = window.auth.getUser(); const isAuthenticated = window.auth.isAuthenticated(); if (!isAuthenticated || !token || !user) { return `
Please login to add comments.
`; } // User is authenticated, create the comment form return `
`; } catch (error) { console.error('Error checking authentication:', error); return `
Error checking authentication status. Please try refreshing the page.
`; } }, /** * Loads facility comments from the server */ async loadFacilityComments(facilityId) { try { if (!facilityId) { throw new Error('No facility ID provided'); } // Ensure facilityId is a string facilityId = facilityId.toString(); // Show loading indicator const commentsContainer = document.getElementById('commentsContainer'); if (!commentsContainer) { throw new Error('Comments container not found'); } commentsContainer.innerHTML = `
Loading...

Loading comments...

`; // Use the API client to get facility statuses const data = await window.api.getFacilityStatuses(facilityId); // Validate the response if (!data || typeof data !== 'object') { throw new Error('Invalid response from server'); } if (!data.success) { throw new Error(data.error || 'Failed to load comments'); } if (!Array.isArray(data.statuses)) { throw new Error('Invalid comments data format'); } // Render the comments this.renderComments(data.statuses, facilityId); } catch (error) { console.error('Error loading comments:', error); const commentsContainer = document.getElementById('commentsContainer'); if (commentsContainer) { commentsContainer.innerHTML = `
${error.message}
`; } } }, /** * Renders comments in the comments container */ renderComments(comments, facilityId) { const commentsContainer = document.getElementById('commentsContainer'); if (!commentsContainer) { console.error('Comments container not found'); return; } // Clear the container commentsContainer.innerHTML = ''; // Add the comment form for authenticated users commentsContainer.innerHTML = this.createCommentFormForAuthenticatedUser(facilityId); // Re-initialise the comment form handler immediately after creating the form const commentForm = document.getElementById('commentForm'); if (commentForm) { this.setupCommentFormHandler(commentForm); } // If no comments, show a message if (!comments || comments.length === 0) { const noCommentsDiv = document.createElement('div'); noCommentsDiv.className = 'alert alert-light mt-3'; noCommentsDiv.innerHTML = ` No comments yet. Be the first to add a comment! `; commentsContainer.appendChild(noCommentsDiv); return; } // Create the comments list const commentsList = document.createElement('div'); commentsList.className = 'comments-list mt-4'; // Add each comment comments.forEach(comment => { const commentElement = document.createElement('div'); commentElement.className = 'comment-item card mb-3 border-0 shadow-sm'; // Check if the current user is the comment author or an admin const canEdit = this.isAdmin() || this.isCurrentUser(comment.username); commentElement.innerHTML = `
${this.escapeHtml(comment.username)}
${canEdit ? ` ` : ''}

${this.escapeHtml(comment.statusComment)}

`; commentsList.appendChild(commentElement); }); commentsContainer.appendChild(commentsList); }, /** * Deletes a comment */ async deleteComment(commentId, facilityId) { // Confirm deletion if (!confirm('Are you sure you want to delete this comment?')) { return; } try { // Use the API client to delete a status comment const data = await window.api.deleteFacilityStatus(commentId, facilityId); if (data.success) { // Reload comments to reflect the deletion this.loadFacilityComments(facilityId); } else { console.error('Delete comment failed:', data.error); alert(data.error || 'Failed to delete comment'); } } catch (error) { console.error('Error deleting comment:', error); alert('Failed to delete comment: ' + error.message); } }, /** * Checks if the current user is an admin */ isAdmin() { return window.auth && window.auth.isAdmin(); }, /** * Checks if the given username matches the current user */ isCurrentUser(username) { const user = window.auth && window.auth.getUser(); return user && user.username === username; }, /** * Safely escapes HTML special characters to prevent XSS attacks */ escapeHtml(unsafe) { if (unsafe === null || unsafe === undefined) { return ''; } return unsafe .toString() .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } }; // Listen for DOM ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { CommentsManager.state.isDomReady = true; CommentsManager.checkinitialise(); }); } else { CommentsManager.state.isDomReady = true; CommentsManager.checkinitialise(); } // Listen for auth ready if (window.auth) { CommentsManager.state.isAuthReady = true; CommentsManager.checkinitialise(); } else { window.addEventListener('authReady', () => { console.log('auth is now ready'); CommentsManager.state.isAuthReady = true; CommentsManager.checkinitialise(); }); // Fallback timeout in case the event doesn't fire setTimeout(() => { if (!CommentsManager.state.isAuthReady && window.auth) { console.log('auth found via timeout check'); CommentsManager.state.isAuthReady = true; CommentsManager.checkinitialise(); } }, 1000); } // Export the CommentsManager to the window window.CommentsManager = CommentsManager;