added events to event system
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,7 +3,7 @@ from flask_login import login_user, logout_user, login_required, current_user
|
|||||||
from models import db, User
|
from models import db, User
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from utils import log_event
|
from utils import log_event, create_notification
|
||||||
|
|
||||||
def require_password_change(f):
|
def require_password_change(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
@@ -94,6 +94,18 @@ def init_routes(auth_bp):
|
|||||||
db.session.add(new_user)
|
db.session.add(new_user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create notification for the new user
|
||||||
|
create_notification(
|
||||||
|
notif_type='account_created',
|
||||||
|
user_id=new_user.id,
|
||||||
|
details={
|
||||||
|
'message': 'Welcome to DocuPulse! Your account has been created successfully.',
|
||||||
|
'username': new_user.username,
|
||||||
|
'email': new_user.email,
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Log successful registration
|
# Log successful registration
|
||||||
log_event(
|
log_event(
|
||||||
event_type='user_create',
|
event_type='user_create',
|
||||||
@@ -169,6 +181,16 @@ def init_routes(auth_bp):
|
|||||||
|
|
||||||
current_user.set_password(new_password)
|
current_user.set_password(new_password)
|
||||||
|
|
||||||
|
# Create password change notification
|
||||||
|
create_notification(
|
||||||
|
notif_type='password_changed',
|
||||||
|
user_id=current_user.id,
|
||||||
|
details={
|
||||||
|
'message': 'Your password has been changed successfully.',
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Log successful password change
|
# Log successful password change
|
||||||
log_event(
|
log_event(
|
||||||
event_type='user_update',
|
event_type='user_update',
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ from forms import UserForm
|
|||||||
from flask import abort
|
from flask import abort
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
from routes.auth import require_password_change
|
from routes.auth import require_password_change
|
||||||
from utils import log_event
|
from utils import log_event, create_notification
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
contacts_bp = Blueprint('contacts', __name__, url_prefix='/contacts')
|
contacts_bp = Blueprint('contacts', __name__, url_prefix='/contacts')
|
||||||
|
|
||||||
@@ -122,6 +123,20 @@ def new_contact():
|
|||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create notification for the new user
|
||||||
|
create_notification(
|
||||||
|
notif_type='account_created',
|
||||||
|
user_id=user.id,
|
||||||
|
sender_id=current_user.id, # Admin who created the account
|
||||||
|
details={
|
||||||
|
'message': 'Your DocuPulse account has been created by an administrator.',
|
||||||
|
'username': user.username,
|
||||||
|
'email': user.email,
|
||||||
|
'created_by': f"{current_user.username} {current_user.last_name}",
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Log user creation event
|
# Log user creation event
|
||||||
log_event(
|
log_event(
|
||||||
event_type='user_create',
|
event_type='user_create',
|
||||||
@@ -304,6 +319,26 @@ def edit_contact(id):
|
|||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create notification for the user being updated
|
||||||
|
create_notification(
|
||||||
|
notif_type='account_updated',
|
||||||
|
user_id=user.id,
|
||||||
|
sender_id=current_user.id,
|
||||||
|
details={
|
||||||
|
'message': 'Your account has been updated by an administrator.',
|
||||||
|
'updated_by': f"{current_user.username} {current_user.last_name}",
|
||||||
|
'changes': {
|
||||||
|
'name': f"{user.username} {user.last_name}",
|
||||||
|
'email': user.email,
|
||||||
|
'phone': user.phone,
|
||||||
|
'company': user.company,
|
||||||
|
'position': user.position,
|
||||||
|
'password_changed': password_changed
|
||||||
|
},
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Log user update event
|
# Log user update event
|
||||||
log_event(
|
log_event(
|
||||||
event_type='user_update',
|
event_type='user_update',
|
||||||
@@ -342,6 +377,18 @@ def delete_contact(id):
|
|||||||
flash('You cannot delete your own account.', 'error')
|
flash('You cannot delete your own account.', 'error')
|
||||||
return redirect(url_for('contacts.contacts_list'))
|
return redirect(url_for('contacts.contacts_list'))
|
||||||
|
|
||||||
|
# Create notification for the user being deleted
|
||||||
|
create_notification(
|
||||||
|
notif_type='account_deleted',
|
||||||
|
user_id=user.id,
|
||||||
|
sender_id=current_user.id,
|
||||||
|
details={
|
||||||
|
'message': 'Your account has been deleted by an administrator.',
|
||||||
|
'deleted_by': f"{current_user.username} {current_user.last_name}",
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Log user deletion event
|
# Log user deletion event
|
||||||
log_event(
|
log_event(
|
||||||
event_type='user_delete',
|
event_type='user_delete',
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from flask_login import login_required, current_user
|
|||||||
from models import db, Conversation, User, Message, MessageAttachment
|
from models import db, Conversation, User, Message, MessageAttachment
|
||||||
from forms import ConversationForm
|
from forms import ConversationForm
|
||||||
from routes.auth import require_password_change
|
from routes.auth import require_password_change
|
||||||
from utils import log_event
|
from utils import log_event, create_notification
|
||||||
import os
|
import os
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -177,6 +177,20 @@ def add_member(conversation_id):
|
|||||||
conversation.members.append(user)
|
conversation.members.append(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create notification for the invited user
|
||||||
|
create_notification(
|
||||||
|
notif_type='conversation_invite',
|
||||||
|
user_id=user.id,
|
||||||
|
sender_id=current_user.id,
|
||||||
|
details={
|
||||||
|
'message': f'You have been invited to join conversation "{conversation.name}"',
|
||||||
|
'conversation_id': conversation.id,
|
||||||
|
'conversation_name': conversation.name,
|
||||||
|
'invited_by': f"{current_user.username} {current_user.last_name}",
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Log member addition
|
# Log member addition
|
||||||
log_event(
|
log_event(
|
||||||
event_type='conversation_member_add',
|
event_type='conversation_member_add',
|
||||||
@@ -186,7 +200,7 @@ def add_member(conversation_id):
|
|||||||
'added_by': current_user.id,
|
'added_by': current_user.id,
|
||||||
'added_by_name': f"{current_user.username} {current_user.last_name}",
|
'added_by_name': f"{current_user.username} {current_user.last_name}",
|
||||||
'added_user_id': user.id,
|
'added_user_id': user.id,
|
||||||
'added_by_name': f"{current_user.username} {current_user.last_name}",
|
'added_user_name': f"{user.username} {user.last_name}",
|
||||||
'added_user_email': user.email
|
'added_user_email': user.email
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -215,6 +229,20 @@ def remove_member(conversation_id, user_id):
|
|||||||
conversation.members.remove(user)
|
conversation.members.remove(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create notification for the removed user
|
||||||
|
create_notification(
|
||||||
|
notif_type='conversation_invite_removed',
|
||||||
|
user_id=user.id,
|
||||||
|
sender_id=current_user.id,
|
||||||
|
details={
|
||||||
|
'message': f'You have been removed from conversation "{conversation.name}"',
|
||||||
|
'conversation_id': conversation.id,
|
||||||
|
'conversation_name': conversation.name,
|
||||||
|
'removed_by': f"{current_user.username} {current_user.last_name}",
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Log member removal
|
# Log member removal
|
||||||
log_event(
|
log_event(
|
||||||
event_type='conversation_member_remove',
|
event_type='conversation_member_remove',
|
||||||
@@ -437,6 +465,25 @@ def send_message(conversation_id):
|
|||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create notifications for all conversation members except the sender
|
||||||
|
for member in conversation.members:
|
||||||
|
if member.id != current_user.id:
|
||||||
|
create_notification(
|
||||||
|
notif_type='conversation_message',
|
||||||
|
user_id=member.id,
|
||||||
|
sender_id=current_user.id,
|
||||||
|
details={
|
||||||
|
'message': f'New message in conversation "{conversation.name}"',
|
||||||
|
'conversation_id': conversation.id,
|
||||||
|
'conversation_name': conversation.name,
|
||||||
|
'sender': f"{current_user.username} {current_user.last_name}",
|
||||||
|
'message_preview': message_content[:100] + ('...' if len(message_content) > 100 else ''),
|
||||||
|
'has_attachments': len(attachments) > 0,
|
||||||
|
'attachment_count': len(attachments),
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Log message creation
|
# Log message creation
|
||||||
log_event(
|
log_event(
|
||||||
event_type='message_create',
|
event_type='message_create',
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import logging
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from forms import CompanySettingsForm
|
from forms import CompanySettingsForm
|
||||||
from utils import log_event
|
from utils import log_event, create_notification
|
||||||
|
|
||||||
# Set up logging to show in console
|
# Set up logging to show in console
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -55,7 +55,7 @@ def init_routes(main_bp):
|
|||||||
Event.user_id == current_user.id, # User's own actions
|
Event.user_id == current_user.id, # User's own actions
|
||||||
db.and_(
|
db.and_(
|
||||||
Event.event_type.in_(['conversation_create', 'message_create']), # Conversation-related events
|
Event.event_type.in_(['conversation_create', 'message_create']), # Conversation-related events
|
||||||
Event.details['conversation_id'].astext.in_(
|
Event.details['conversation_id'].cast(db.Integer).in_(
|
||||||
db.session.query(Conversation.id)
|
db.session.query(Conversation.id)
|
||||||
.join(Conversation.members)
|
.join(Conversation.members)
|
||||||
.filter(User.id == current_user.id)
|
.filter(User.id == current_user.id)
|
||||||
@@ -370,6 +370,17 @@ def init_routes(main_bp):
|
|||||||
flash('Passwords do not match.', 'error')
|
flash('Passwords do not match.', 'error')
|
||||||
return render_template('profile/profile.html')
|
return render_template('profile/profile.html')
|
||||||
current_user.set_password(new_password)
|
current_user.set_password(new_password)
|
||||||
|
|
||||||
|
# Create password change notification
|
||||||
|
create_notification(
|
||||||
|
notif_type='password_changed',
|
||||||
|
user_id=current_user.id,
|
||||||
|
details={
|
||||||
|
'message': 'Your password has been changed successfully.',
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
flash('Password updated successfully.', 'success')
|
flash('Password updated successfully.', 'success')
|
||||||
elif confirm_password:
|
elif confirm_password:
|
||||||
flash('Please enter a new password.', 'error')
|
flash('Please enter a new password.', 'error')
|
||||||
@@ -522,7 +533,8 @@ def init_routes(main_bp):
|
|||||||
'details': notif.details,
|
'details': notif.details,
|
||||||
'sender': {
|
'sender': {
|
||||||
'id': notif.sender.id,
|
'id': notif.sender.id,
|
||||||
'username': notif.sender.username
|
'username': notif.sender.username,
|
||||||
|
'last_name': notif.sender.last_name
|
||||||
} if notif.sender else None
|
} if notif.sender else None
|
||||||
} for notif in notifications.items],
|
} for notif in notifications.items],
|
||||||
'total_pages': total_pages,
|
'total_pages': total_pages,
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
from flask import Blueprint, jsonify, request, abort
|
from flask import Blueprint, jsonify, request, abort
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from models import db, Room, User, RoomMemberPermission
|
from models import db, Room, User, RoomMemberPermission
|
||||||
from utils import user_has_permission, log_event
|
from utils import user_has_permission, log_event, create_notification
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
room_members_bp = Blueprint('room_members', __name__)
|
room_members_bp = Blueprint('room_members', __name__)
|
||||||
|
|
||||||
@@ -70,6 +71,21 @@ def add_room_member(room_id):
|
|||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create notification for the invited user
|
||||||
|
create_notification(
|
||||||
|
notif_type='room_invite',
|
||||||
|
user_id=user_id,
|
||||||
|
sender_id=current_user.id,
|
||||||
|
details={
|
||||||
|
'message': f'You have been invited to join room "{room.name}"',
|
||||||
|
'room_id': room_id,
|
||||||
|
'room_name': room.name,
|
||||||
|
'invited_by': f"{current_user.username} {current_user.last_name}",
|
||||||
|
'permissions': permissions,
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
log_event(
|
log_event(
|
||||||
event_type='room_member_add',
|
event_type='room_member_add',
|
||||||
details={
|
details={
|
||||||
@@ -77,8 +93,7 @@ def add_room_member(room_id):
|
|||||||
'room_name': room.name,
|
'room_name': room.name,
|
||||||
'added_user_id': user_id,
|
'added_user_id': user_id,
|
||||||
'added_user_name': f"{user.username} {user.last_name}",
|
'added_user_name': f"{user.username} {user.last_name}",
|
||||||
'added_by': f"{current_user.username} {current_user.last_name}",
|
'added_by': f"{current_user.username} {current_user.last_name}"
|
||||||
'permissions': permissions
|
|
||||||
},
|
},
|
||||||
user_id=current_user.id
|
user_id=current_user.id
|
||||||
)
|
)
|
||||||
@@ -105,6 +120,20 @@ def remove_room_member(room_id, user_id):
|
|||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
# Create notification for the removed user
|
||||||
|
create_notification(
|
||||||
|
notif_type='room_invite_removed',
|
||||||
|
user_id=user_id,
|
||||||
|
sender_id=current_user.id,
|
||||||
|
details={
|
||||||
|
'message': f'You have been removed from room "{room.name}"',
|
||||||
|
'room_id': room_id,
|
||||||
|
'room_name': room.name,
|
||||||
|
'removed_by': f"{current_user.username} {current_user.last_name}",
|
||||||
|
'timestamp': datetime.utcnow().isoformat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
log_event(
|
log_event(
|
||||||
event_type='room_member_remove',
|
event_type='room_member_remove',
|
||||||
details={
|
details={
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const notifDetailsModal = document.getElementById('notifDetailsModal');
|
const notifDetailsModal = document.getElementById('notifDetailsModal');
|
||||||
const notifDetailsContent = document.getElementById('notifDetailsContent');
|
const notifDetailsContent = document.getElementById('notifDetailsContent');
|
||||||
|
|
||||||
|
// Get CSRF token from meta tag
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||||||
|
|
||||||
// Function to update URL with current filters
|
// Function to update URL with current filters
|
||||||
function updateURL() {
|
function updateURL() {
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
@@ -31,7 +34,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to fetch notifications
|
// Function to fetch notifications
|
||||||
function fetchNotifications() {
|
async function fetchNotifications() {
|
||||||
if (isFetching) return;
|
if (isFetching) return;
|
||||||
isFetching = true;
|
isFetching = true;
|
||||||
|
|
||||||
@@ -45,54 +48,26 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
ajax: 'true'
|
ajax: 'true'
|
||||||
});
|
});
|
||||||
|
|
||||||
fetch(`${window.location.pathname}?${params.toString()}`, {
|
try {
|
||||||
headers: {
|
const response = await fetch(`${window.location.pathname}?${params.toString()}`, {
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
headers: {
|
||||||
}
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
})
|
}
|
||||||
.then(response => {
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Network response was not ok');
|
throw new Error('Network response was not ok');
|
||||||
}
|
}
|
||||||
return response.text();
|
|
||||||
})
|
|
||||||
.then(html => {
|
|
||||||
const parser = new DOMParser();
|
|
||||||
const doc = parser.parseFromString(html, 'text/html');
|
|
||||||
const newTableBody = doc.getElementById('notifsTableBody');
|
|
||||||
|
|
||||||
if (newTableBody) {
|
const data = await response.json();
|
||||||
notifsTableBody.innerHTML = newTableBody.innerHTML;
|
updateNotificationsTable(data.notifications);
|
||||||
|
updatePagination(data.total_pages, data.current_page);
|
||||||
// Update pagination
|
} catch (error) {
|
||||||
const newCurrentPage = parseInt(doc.getElementById('currentPage').textContent);
|
|
||||||
const newTotalPages = parseInt(doc.getElementById('totalPages').textContent);
|
|
||||||
currentPage = newCurrentPage;
|
|
||||||
totalPages = newTotalPages;
|
|
||||||
currentPageSpan.textContent = currentPage;
|
|
||||||
totalPagesSpan.textContent = totalPages;
|
|
||||||
|
|
||||||
// Update pagination buttons
|
|
||||||
prevPageBtn.disabled = currentPage === 1;
|
|
||||||
nextPageBtn.disabled = currentPage === totalPages;
|
|
||||||
|
|
||||||
// Update URL
|
|
||||||
updateURL();
|
|
||||||
|
|
||||||
// Reattach event listeners
|
|
||||||
attachEventListeners();
|
|
||||||
} else {
|
|
||||||
console.error('Could not find notifications table in response');
|
|
||||||
notifsTableBody.innerHTML = '<tr><td colspan="6" class="text-center">Error loading notifications</td></tr>';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error fetching notifications:', error);
|
console.error('Error fetching notifications:', error);
|
||||||
notifsTableBody.innerHTML = '<tr><td colspan="6" class="text-center">Error loading notifications</td></tr>';
|
notifsTableBody.innerHTML = '<tr><td colspan="6" class="text-center">Error loading notifications</td></tr>';
|
||||||
})
|
} finally {
|
||||||
.finally(() => {
|
|
||||||
isFetching = false;
|
isFetching = false;
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to get notification type badge
|
// Function to get notification type badge
|
||||||
@@ -130,80 +105,123 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to mark notification as read
|
// Function to mark notification as read
|
||||||
function markAsRead(notifId) {
|
async function markAsRead(notifId) {
|
||||||
fetch(`/api/notifications/${notifId}/read`, {
|
try {
|
||||||
method: 'POST',
|
const response = await fetch(`/api/notifications/${notifId}/read`, {
|
||||||
headers: {
|
method: 'POST',
|
||||||
'X-Requested-With': 'XMLHttpRequest',
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
'X-CSRFToken': csrfToken,
|
||||||
})
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
.then(response => {
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Network response was not ok');
|
throw new Error('Network response was not ok');
|
||||||
}
|
}
|
||||||
return response.json();
|
|
||||||
})
|
const data = await response.json();
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
fetchNotifications();
|
// Update the UI to show the notification as read
|
||||||
|
const notifRow = document.querySelector(`tr[data-notif-id="${notifId}"]`);
|
||||||
|
if (notifRow) {
|
||||||
|
notifRow.classList.remove('table-warning');
|
||||||
|
const statusCell = notifRow.querySelector('td:nth-last-child(2)');
|
||||||
|
if (statusCell) {
|
||||||
|
statusCell.innerHTML = '<span class="badge bg-success">Read</span>';
|
||||||
|
}
|
||||||
|
const actionsCell = notifRow.querySelector('td:last-child');
|
||||||
|
if (actionsCell) {
|
||||||
|
const markReadBtn = actionsCell.querySelector('.mark-read');
|
||||||
|
if (markReadBtn) {
|
||||||
|
markReadBtn.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
} catch (error) {
|
||||||
.catch(error => {
|
|
||||||
console.error('Error marking notification as read:', error);
|
console.error('Error marking notification as read:', error);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to delete notification
|
// Function to delete notification
|
||||||
function deleteNotification(notifId) {
|
async function deleteNotification(notifId) {
|
||||||
if (!confirm('Are you sure you want to delete this notification?')) {
|
try {
|
||||||
return;
|
const response = await fetch(`/api/notifications/${notifId}`, {
|
||||||
}
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': csrfToken,
|
||||||
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
fetch(`/api/notifications/${notifId}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {
|
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Network response was not ok');
|
throw new Error('Network response was not ok');
|
||||||
}
|
}
|
||||||
return response.json();
|
|
||||||
})
|
const data = await response.json();
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
fetchNotifications();
|
// Remove the notification row from the table
|
||||||
|
const notifRow = document.querySelector(`tr[data-notif-id="${notifId}"]`);
|
||||||
|
if (notifRow) {
|
||||||
|
notifRow.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
} catch (error) {
|
||||||
.catch(error => {
|
|
||||||
console.error('Error deleting notification:', error);
|
console.error('Error deleting notification:', error);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to mark all notifications as read
|
// Function to mark all notifications as read
|
||||||
function markAllAsRead() {
|
async function markAllAsRead() {
|
||||||
fetch('/api/notifications/mark-all-read', {
|
try {
|
||||||
method: 'POST',
|
const response = await fetch('/api/notifications/mark-all-read', {
|
||||||
headers: {
|
method: 'POST',
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
headers: {
|
||||||
}
|
'Content-Type': 'application/json',
|
||||||
})
|
'X-CSRFToken': csrfToken,
|
||||||
.then(response => {
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Network response was not ok');
|
throw new Error('Network response was not ok');
|
||||||
}
|
}
|
||||||
return response.json();
|
|
||||||
})
|
const data = await response.json();
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
fetchNotifications();
|
// Update all notifications to show as read
|
||||||
|
document.querySelectorAll('tr[data-notif-id]').forEach(row => {
|
||||||
|
row.classList.remove('table-warning');
|
||||||
|
const statusCell = row.querySelector('td:nth-last-child(2)');
|
||||||
|
if (statusCell) {
|
||||||
|
statusCell.innerHTML = '<span class="badge bg-success">Read</span>';
|
||||||
|
}
|
||||||
|
const actionsCell = row.querySelector('td:last-child');
|
||||||
|
if (actionsCell) {
|
||||||
|
const markReadBtn = actionsCell.querySelector('.mark-read');
|
||||||
|
if (markReadBtn) {
|
||||||
|
markReadBtn.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
} catch (error) {
|
||||||
.catch(error => {
|
|
||||||
console.error('Error marking all notifications as read:', error);
|
console.error('Error marking all notifications as read:', error);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to handle notification action clicks
|
||||||
|
function handleNotificationAction(notifId, actionType) {
|
||||||
|
// Mark the notification as read when an action is taken
|
||||||
|
markAsRead(notifId);
|
||||||
|
|
||||||
|
// Additional handling for specific action types can be added here
|
||||||
|
if (actionType === 'view_room' || actionType === 'view_conversation') {
|
||||||
|
// The link will handle the navigation automatically
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to attach event listeners
|
// Function to attach event listeners
|
||||||
@@ -231,6 +249,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
loadNotifDetails(notifId);
|
loadNotifDetails(notifId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Action buttons (View Room, View Conversation)
|
||||||
|
document.querySelectorAll('.btn-group a').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (e) => {
|
||||||
|
const notifId = e.target.closest('tr').dataset.notifId;
|
||||||
|
const actionType = btn.classList.contains('fa-door-open') ? 'view_room' : 'view_conversation';
|
||||||
|
handleNotificationAction(notifId, actionType);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add event listeners for filters with debounce
|
// Add event listeners for filters with debounce
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
<tbody id="notifsTableBody">
|
<tbody id="notifsTableBody">
|
||||||
{% if notifications %}
|
{% if notifications %}
|
||||||
{% for notif in notifications %}
|
{% for notif in notifications %}
|
||||||
<tr class="{% if not notif.read %}table-warning{% endif %}">
|
<tr class="{% if not notif.read %}table-warning{% endif %}" data-notif-id="{{ notif.id }}">
|
||||||
<td>{{ notif.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</td>
|
<td>{{ notif.timestamp.strftime('%Y-%m-%d %H:%M:%S') }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% if notif.notif_type == 'account_created' %}
|
{% if notif.notif_type == 'account_created' %}
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
<span class="badge bg-secondary">{{ notif.notif_type }}</span>
|
<span class="badge bg-secondary">{{ notif.notif_type }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ notif.sender.username if notif.sender else 'System' }}</td>
|
<td>{{ notif.sender.username + ' ' + notif.sender.last_name if notif.sender else 'System' }}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-sm btn-secondary"
|
<button class="btn btn-sm btn-secondary"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
@@ -100,14 +100,27 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if not notif.read %}
|
<div class="btn-group">
|
||||||
<button class="btn btn-sm btn-primary mark-read" data-notif-id="{{ notif.id }}">
|
{% if notif.notif_type in ['room_invite', 'room_invite_removed'] and notif.details and notif.details.room_id %}
|
||||||
<i class="fas fa-check"></i> Mark as Read
|
<a href="{{ url_for('rooms.room', room_id=notif.details.room_id) }}"
|
||||||
</button>
|
class="btn btn-sm btn-primary">
|
||||||
{% endif %}
|
<i class="fas fa-door-open"></i> View Room
|
||||||
<button class="btn btn-sm btn-danger delete-notif" data-notif-id="{{ notif.id }}">
|
</a>
|
||||||
<i class="fas fa-trash"></i>
|
{% elif notif.notif_type in ['conversation_invite', 'conversation_invite_removed', 'conversation_message'] and notif.details and notif.details.conversation_id %}
|
||||||
</button>
|
<a href="{{ url_for('conversations.conversation', conversation_id=notif.details.conversation_id) }}"
|
||||||
|
class="btn btn-sm btn-primary">
|
||||||
|
<i class="fas fa-comments"></i> View Conversation
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if not notif.read %}
|
||||||
|
<button class="btn btn-sm btn-success mark-read" data-notif-id="{{ notif.id }}">
|
||||||
|
<i class="fas fa-check"></i> Mark as Read
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
<button class="btn btn-sm btn-danger delete-notif" data-notif-id="{{ notif.id }}">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
Reference in New Issue
Block a user