Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 49 additions & 21 deletions Back-End/pongProject/pong_app/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,24 +805,52 @@ def create_tournament(sender, instance, created, **kwargs):
logger.error(f'❌ Failed to send tournament notification to creator (ID: {instance.creator.user_id}): {str(e)}')
print(f"❌ Error sending notification to creator: {str(e)}")

logger.info(f'🏆 Tournament {instance.id} has these players {list(instance.player.all().values_list("user_id", flat=True))}')
# Send notifications to all participants (using the correct relationship)
for player in instance.player.all():
if player.user_id == instance.creator.user_id:
continue

# Send notification to player
try:
notification = ImmediateNotification(
Sender='Pong',
message=notification_data,
user_id=player.user_id
)
SendNotificationSync(notification)
logger.info(f'✅ Tournament notification sent to player (ID: {player.user_id}) for tournament {instance.id}')
except Exception as e:
logger.error(f'❌ Failed to send tournament notification to player (ID: {player.user_id}): {str(e)}')
print(f"❌ Error sending notification to player {player.user_id}: {str(e)}")
logger.info(f'✅ Tournament {instance.id} created successfully')
else:
logger.debug(f'🔄 Tournament {instance.id} updated (not created)')
# logger.info(f'🏆 Tournament {instance.id} has these players {list(instance.player.all().values_list("user_id", flat=True))}')
# # Send notifications to all participants (using the correct relationship)
# for player in instance.player.all():
# if player.user_id == instance.creator.user_id:
# continue

# # Send notification to player
# try:
# notification = ImmediateNotification(
# Sender='Pong',
# message=notification_data,
# user_id=player.user_id
# )
# SendNotificationSync(notification)
# logger.info(f'✅ Tournament notification sent to player (ID: {player.user_id}) for tournament {instance.id}')
# except Exception as e:
# logger.error(f'❌ Failed to send tournament notification to player (ID: {player.user_id}): {str(e)}')
# print(f"❌ Error sending notification to player {player.user_id}: {str(e)}")
# logger.info(f'✅ Tournament {instance.id} created successfully')
# else:
# logger.debug(f'🔄 Tournament {instance.id} updated (not created)')

# from django.db.models.signals import m2m_changed
# from django.dispatch import receiver

# @receiver(m2m_changed, sender=UserProfile.tournaments.through)
# def tournament_participants_changed(sender, instance, action, reverse, model, pk_set, **kwargs):
# if action == "post_add":
# from .notification import SendNotificationSync, ImmediateNotification
# logger = logging.getLogger(__name__)
# notification_data = {
# 'type': 'tournament_created',
# 'tournament_id': instance.id,
# 'name': instance.name,
# 'max_players': instance.max_partecipants,
# 'creator_id': instance.creator.user_id,
# 'begin_date': instance.begin_date.isoformat() if instance.begin_date else None,
# }
# for player in instance.player.all():
# try:
# notification = ImmediateNotification(
# Sender='Pong',
# message=notification_data,
# user_id=player.user_id
# )
# SendNotificationSync(notification)
# logger.info(f'✅ Tournament notification sent to player (ID: {player.user_id}) for tournament {instance.id}')
# except Exception as e:
# logger.error(f'❌ Failed to send tournament notification to player (ID: {player.user_id}): {str(e)}')
23 changes: 21 additions & 2 deletions Back-End/pongProject/pong_app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,31 @@ def perform_create(self, serializer):

# Add initial partecipants if provided
if initial_partecipants:
from .notification import SendNotificationSync, ImmediateNotification
notification_data = {
'type': 'tournament_created',
'tournament_id': tournament.id,
'name': tournament.name,
'max_players': tournament.max_partecipants,
'creator_id': tournament.creator.user_id,
'begin_date': tournament.begin_date.isoformat() if tournament.begin_date else None,
}
for user_id in initial_partecipants:
try:
participant = UserProfile.objects.get(user_id=int(user_id))
# Avoid adding duplicates (in case creator is in the list)
# if not participant.tournaments.filter(id=tournament.id).exists():
participant.tournaments.add(tournament)
if not participant.tournaments.filter(id=tournament.id).exists():
participant.tournaments.add(tournament)
try:
notification = ImmediateNotification(
Sender='Pong',
message=notification_data,
user_id=participant.user_id
)
SendNotificationSync(notification)
logger.info(f'✅ Tournament notification sent to creator (ID: {tournament.creator.user_id}) for tournament {tournament.id}')
except Exception as e:
logger.error(f'❌ Failed to send tournament notification to creator (ID: {tournament.creator.user_id}): {str(e)}')
except UserProfile.DoesNotExist:
# Log the error but don't fail the entire creation
logging.warning(f"User with ID {user_id} not found, skipping from tournament {tournament.id}")
Expand Down
223 changes: 48 additions & 175 deletions Front-End/public/alert/alert.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,183 +4,56 @@ link.href = '/alert/alert.css';
document.head.appendChild(link);

export function renderAlert(message, type) {
return `
<div class="alert ${type}">
${message}
return `
<div class="alert ${type}">
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">&times;</button>
</div>
`;
</div>
`;
}

export function showAlertForXSeconds(message, type, seconds, options = {}) {
const { asToast = false } = options;
let container;

if (asToast) {
const position = getRandomToastPosition();
const containerId = `toast-container-${position}`;

container = document.getElementById(containerId);
if (!container) {
container = document.createElement('div');
container.id = containerId;
container.style.position = 'fixed';
container.style.zIndex = '9999';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.gap = '10px';
container.style.maxWidth = '350px';
container.style.minWidth = '300px';

// Set position based on random corner
setToastContainerPosition(container, position);

document.body.appendChild(container);
}
} else {
container = document.getElementById('alert-container');
if (!container) {
console.error("Alert container not found");
return;
}
}

const alertHTML = renderAlert(message, type);
const wrapper = document.createElement('div');
wrapper.innerHTML = alertHTML;

if (asToast) {
const alertElement = wrapper.firstElementChild;

// Add animation class for entrance
alertElement.style.opacity = '0';
alertElement.style.transform = 'translateY(20px)';
alertElement.style.transition = 'all 0.3s ease-in-out';

container.appendChild(alertElement);

// Trigger entrance animation
setTimeout(() => {
alertElement.style.opacity = '1';
alertElement.style.transform = 'translateY(0)';
}, 10);

// Remove with exit animation
setTimeout(() => {
if (alertElement.parentNode) {
alertElement.style.opacity = '0';
alertElement.style.transform = 'translateY(-20px)';
setTimeout(() => {
if (alertElement.parentNode) {
alertElement.parentNode.removeChild(alertElement);

// Clean up empty containers
if (container.children.length === 0) {
container.parentNode.removeChild(container);
}
}
}, 300);
}
}, seconds * 1000 - 300);
} else {
container.innerHTML = alertHTML;
setTimeout(() => {
container.innerHTML = '';
}, seconds * 1000);
}
}

function getRandomToastPosition() {
const positions = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
return positions[Math.floor(Math.random() * positions.length)];
}

function setToastContainerPosition(container, position) {
// Reset all position properties
container.style.top = '';
container.style.bottom = '';
container.style.left = '';
container.style.right = '';

const offset = '20px';

switch (position) {
case 'top-left':
container.style.top = offset;
container.style.left = offset;
break;
case 'top-right':
container.style.top = offset;
container.style.right = offset;
break;
case 'bottom-left':
container.style.bottom = offset;
container.style.left = offset;
break;
case 'bottom-right':
container.style.bottom = offset;
container.style.right = offset;
break;
default:
// Default to bottom-left
container.style.bottom = offset;
container.style.left = offset;
}
}

// Optional: Export function to show toast in specific position
export function showToastAtPosition(message, type, seconds, position) {
const containerId = `toast-container-${position}`;
let container = document.getElementById(containerId);

if (!container) {
container = document.createElement('div');
container.id = containerId;
container.style.position = 'fixed';
container.style.zIndex = '9999';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.gap = '10px';
container.style.maxWidth = '350px';
container.style.minWidth = '300px';

setToastContainerPosition(container, position);
document.body.appendChild(container);
}

const alertHTML = renderAlert(message, type);
const wrapper = document.createElement('div');
wrapper.innerHTML = alertHTML;
const alertElement = wrapper.firstElementChild;

// Add animation
alertElement.style.opacity = '0';
alertElement.style.transform = 'translateY(20px)';
alertElement.style.transition = 'all 0.3s ease-in-out';

container.appendChild(alertElement);

// Trigger entrance animation
setTimeout(() => {
alertElement.style.opacity = '1';
alertElement.style.transform = 'translateY(0)';
}, 10);

// Remove with exit animation
setTimeout(() => {
if (alertElement.parentNode) {
alertElement.style.opacity = '0';
alertElement.style.transform = 'translateY(-20px)';
setTimeout(() => {
if (alertElement.parentNode) {
alertElement.parentNode.removeChild(alertElement);

// Clean up empty containers
if (container.children.length === 0) {
container.parentNode.removeChild(container);
}
}
}, 300);
}
}, seconds * 1000 - 300);
const { asToast = false } = options;
let container;

if (asToast) {
container = document.getElementById('toast-container');
if (!container) {
container = document.createElement('div');
container.id = 'toast-container';
container.style.position = 'fixed';
container.style.bottom = '20px';
container.style.left = '20px';
container.style.zIndex = '9999';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.gap = '10px';
document.body.appendChild(container);
}
} else {
container = document.getElementById('alert-container');
if (!container) {
console.error("Alert container not found");
return;
}
}

const alertHTML = renderAlert(message, type);
const wrapper = document.createElement('div');
wrapper.innerHTML = alertHTML;

if (asToast) {
const alertElement = wrapper.firstElementChild;
container.appendChild(alertElement);
setTimeout(() => {
if (alertElement.parentNode) {
alertElement.parentNode.removeChild(alertElement);
}
}, seconds * 1000);
} else {
container.innerHTML = alertHTML;
setTimeout(() => {
container.innerHTML = '';
}, seconds * 1000);
}
}
4 changes: 4 additions & 0 deletions Front-End/public/chat/AddChat.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ function renderAddChat() {
const addChatContainer = document.createElement("div");
addChatContainer.className = "add-chat";
addChatContainer.innerHTML = `
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:1rem;">
<h2>Create Chat</h2>
<i class="bi bi-envelope-plus fs-1" style="color: var(--bs-secondary-color) !important; margin-right: 1rem;"></i>
</div>
<form id="addChatForm" class="d-flex flex-column">
<input type="text" id="roomName" placeholder="Nome del gruppo" class="form-control mb-2" />
<input type="text" id="roomDescription" placeholder="Descrizione" class="form-control mb-2" />
Expand Down
12 changes: 12 additions & 0 deletions Front-End/public/home/home.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@
overflow: hidden;
}

.task-container.gap {
margin-bottom: 10px;
}

.task-double-container {
flex: 1 1 45%;
max-width: 100%;
min-width: 168px;
gap: 10px;
}


.profile {
flex: 1 1 100%;
max-width: 100%;
Expand Down
Loading