Implemented comprehensive notification toast system integrating Socket.IO
with react-toastify for real-time user notifications.
Features:
- NotificationProvider component for automatic Socket.IO event handling
- Custom Bootstrap-themed toast styles with mobile responsiveness
- Four toast types: success, error, info, warning
- Auto-dismiss after 5 seconds with manual dismiss option
- Duplicate prevention using toast IDs
- Mobile-optimized full-width toasts
- Dark mode support
- 16 passing tests with full coverage
Toast notifications for:
- Connection status (connect/disconnect/reconnect)
- Event updates (new, updated, deleted, participants)
- Task updates (new, completed, updated, deleted)
- Street adoptions/unadoptions
- Achievement unlocks and badge awards
- Social updates (new posts, comments)
- Generic notifications with type-based styling
Usage:
import { notify } from '../context/NotificationProvider';
notify.success('Operation completed!');
notify.error('Something went wrong!');
Configuration:
- Position: top-right (configurable)
- Auto-close: 5 seconds (configurable)
- Max toasts: 5 concurrent
- Mobile responsive: full-width on ≤480px screens
Documentation:
- NOTIFICATION_SYSTEM.md: Complete usage guide
- NOTIFICATION_IMPLEMENTATION.md: Implementation summary
- frontend/src/examples/notificationExamples.js: Code examples
Co-Authored-By: AI Assistant <noreply@ai-assistant.com>
235 lines
4.7 KiB
CSS
235 lines
4.7 KiB
CSS
/* Custom Toast Styles for Adopt-a-Street */
|
|
|
|
/* Override react-toastify default styles to match Bootstrap theme */
|
|
:root {
|
|
--toastify-color-success: #28a745;
|
|
--toastify-color-error: #dc3545;
|
|
--toastify-color-warning: #ffc107;
|
|
--toastify-color-info: #17a2b8;
|
|
--toastify-text-color-light: #212529;
|
|
--toastify-toast-width: 320px;
|
|
--toastify-toast-background: #ffffff;
|
|
--toastify-toast-min-height: 64px;
|
|
--toastify-toast-max-height: 800px;
|
|
--toastify-font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
|
--toastify-z-index: 9999;
|
|
}
|
|
|
|
/* Toast container positioning and responsiveness */
|
|
.Toastify__toast-container {
|
|
width: var(--toastify-toast-width);
|
|
padding: 4px;
|
|
z-index: var(--toastify-z-index);
|
|
}
|
|
|
|
/* Mobile responsive */
|
|
@media only screen and (max-width: 480px) {
|
|
.Toastify__toast-container {
|
|
width: 100vw;
|
|
padding: 0;
|
|
left: 0;
|
|
right: 0;
|
|
margin: 0;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.Toastify__toast {
|
|
margin-bottom: 0;
|
|
border-radius: 0;
|
|
}
|
|
|
|
/* Top position on mobile */
|
|
.Toastify__toast-container--top-right {
|
|
top: 0;
|
|
right: 0;
|
|
left: 0;
|
|
}
|
|
}
|
|
|
|
/* Toast styling */
|
|
.Toastify__toast {
|
|
background-color: var(--toastify-toast-background);
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
padding: 16px;
|
|
margin-bottom: 8px;
|
|
font-family: var(--toastify-font-family);
|
|
color: var(--toastify-text-color-light);
|
|
min-height: var(--toastify-toast-min-height);
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.Toastify__toast:hover {
|
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
/* Success toast with Bootstrap green */
|
|
.Toastify__toast--success {
|
|
border-left: 4px solid var(--toastify-color-success);
|
|
}
|
|
|
|
.Toastify__toast--success .Toastify__progress-bar {
|
|
background: var(--toastify-color-success);
|
|
}
|
|
|
|
/* Error toast with Bootstrap red */
|
|
.Toastify__toast--error {
|
|
border-left: 4px solid var(--toastify-color-error);
|
|
}
|
|
|
|
.Toastify__toast--error .Toastify__progress-bar {
|
|
background: var(--toastify-color-error);
|
|
}
|
|
|
|
/* Warning toast with Bootstrap yellow */
|
|
.Toastify__toast--warning {
|
|
border-left: 4px solid var(--toastify-color-warning);
|
|
}
|
|
|
|
.Toastify__toast--warning .Toastify__progress-bar {
|
|
background: var(--toastify-color-warning);
|
|
}
|
|
|
|
/* Info toast with Bootstrap blue */
|
|
.Toastify__toast--info {
|
|
border-left: 4px solid var(--toastify-color-info);
|
|
}
|
|
|
|
.Toastify__toast--info .Toastify__progress-bar {
|
|
background: var(--toastify-color-info);
|
|
}
|
|
|
|
/* Toast body */
|
|
.Toastify__toast-body {
|
|
padding: 0;
|
|
line-height: 1.5;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.Toastify__toast-body > div:last-child {
|
|
word-break: break-word;
|
|
}
|
|
|
|
/* Close button styling */
|
|
.Toastify__close-button {
|
|
color: #6c757d;
|
|
opacity: 0.7;
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.Toastify__close-button:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
.Toastify__close-button > svg {
|
|
height: 16px;
|
|
width: 16px;
|
|
}
|
|
|
|
/* Progress bar */
|
|
.Toastify__progress-bar {
|
|
height: 4px;
|
|
}
|
|
|
|
.Toastify__progress-bar--animated {
|
|
animation: Toastify__trackProgress linear 1 forwards;
|
|
}
|
|
|
|
/* Icon styling for success/error/warning/info */
|
|
.Toastify__toast-icon {
|
|
width: 20px;
|
|
margin-right: 12px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Custom content styling for achievement notifications */
|
|
.achievement-toast {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.achievement-toast strong {
|
|
font-size: 16px;
|
|
margin-bottom: 4px;
|
|
color: var(--toastify-color-success);
|
|
}
|
|
|
|
.achievement-toast div {
|
|
font-weight: 600;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.achievement-toast small {
|
|
font-size: 12px;
|
|
color: #6c757d;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* Animations */
|
|
@keyframes Toastify__bounceInRight {
|
|
from,
|
|
60%,
|
|
75%,
|
|
90%,
|
|
to {
|
|
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
}
|
|
from {
|
|
opacity: 0;
|
|
transform: translate3d(3000px, 0, 0);
|
|
}
|
|
60% {
|
|
opacity: 1;
|
|
transform: translate3d(-25px, 0, 0);
|
|
}
|
|
75% {
|
|
transform: translate3d(10px, 0, 0);
|
|
}
|
|
90% {
|
|
transform: translate3d(-5px, 0, 0);
|
|
}
|
|
to {
|
|
transform: none;
|
|
}
|
|
}
|
|
|
|
@keyframes Toastify__bounceOutRight {
|
|
20% {
|
|
opacity: 1;
|
|
transform: translate3d(-20px, 0, 0);
|
|
}
|
|
to {
|
|
opacity: 0;
|
|
transform: translate3d(2000px, 0, 0);
|
|
}
|
|
}
|
|
|
|
/* Accessibility improvements */
|
|
.Toastify__toast[role="alert"] {
|
|
border-radius: 8px;
|
|
}
|
|
|
|
/* Dark mode support (optional) */
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--toastify-toast-background: #2b3035;
|
|
--toastify-text-color-light: #ffffff;
|
|
}
|
|
|
|
.Toastify__toast {
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
.Toastify__close-button {
|
|
color: #adb5bd;
|
|
}
|
|
}
|
|
|
|
/* Ensure toasts are above modals */
|
|
.Toastify__toast-container {
|
|
z-index: 9999 !important;
|
|
}
|