Skip to main content

Overview

The SmartShelf alert system provides real-time notifications for critical inventory events, helping managers and administrators take immediate action to prevent stockouts, reduce waste, and maintain optimal inventory levels.

Alert Categories

The system monitors four distinct alert types:

Expired Items

Severity: CRITICALItems that have passed their expiry date. Immediate removal required for safety and compliance.

Expiring Soon

Severity: HIGH/MEDIUMItems expiring within 7 days. Priority sale or use recommended to prevent waste.

Low Stock

Severity: HIGH/MEDIUMItems below reorder threshold (default: 10 units). Reordering needed to prevent stockouts.

Out of Stock

Severity: HIGHItems with zero quantity. Immediate restocking required to fulfill orders.

Real-Time Notifications

Live Alert Feed

The Manager Dashboard displays a live alert feed (ManagerDashboard.tsx:485-508):
interface Alert {
  id: string;
  type: string;              // 'expired' | 'expiring_soon' | 'low_stock' | 'out_of_stock'
  severity: string;          // 'critical' | 'high' | 'medium'
  productName: string;
  category: string;
  message: string;           // Human-readable alert message
  timestamp: string;         // ISO 8601 timestamp
}
Display Implementation:
<div className="space-y-2 max-h-64 overflow-y-auto">
  {alerts.map(alert => (
    <div className="flex items-start gap-3 p-3 rounded-lg bg-slate-50 border">
      <span className="text-xl">{getSeverityIcon(alert.severity)}</span>
      <div className="flex-1">
        <p className={`text-sm font-medium ${getSeverityColor(alert.severity)}`}>
          {alert.message}
        </p>
        <p className="text-xs text-slate-500 mt-1">
          {alert.category} • Just now
        </p>
      </div>
    </div>
  ))}
</div>

Severity Indicators

Visual indicators for quick severity assessment:
const getSeverityIcon = (severity: string) => {
  switch (severity) {
    case 'critical': return '🔴';
    case 'high': return '🟠';
    case 'medium': return '🟡';
    default: return '🔵';
  }
};

const getSeverityColor = (severity: string) => {
  switch (severity) {
    case 'critical': return 'text-red-600 dark:text-red-400';
    case 'high': return 'text-orange-600 dark:text-orange-400';
    case 'medium': return 'text-yellow-600 dark:text-yellow-400';
    default: return 'text-blue-600 dark:text-blue-400';
  }
};

Alert Types in Detail

Expired Items

Items that have passed their expiry date (alertController.js:89-121):
exports.getExpiredAlerts = async (req, res) => {
  const today = new Date();
  today.setHours(0, 0, 0, 0);

  const expiredItems = await Inventory.find({
    expiryDate: { $lt: today }
  })
  .populate('createdBy', 'name email')
  .sort({ expiryDate: 1 });  // Oldest expiry first

  const alerts = expiredItems.map(item => {
    const daysExpired = Math.ceil(
      (today - new Date(item.expiryDate)) / (1000 * 60 * 60 * 24)
    );
    
    return {
      ...item.toObject(),
      alertType: 'expired',
      severity: 'CRITICAL',
      daysExpired,
      message: `Expired ${daysExpired} day${daysExpired !== 1 ? 's' : ''} ago`
    };
  });
};
Alert Message Format:
  • “Expired 1 day ago”
  • “Expired 5 days ago”

Expiring Soon Alerts

Items expiring within configurable timeframe (alertController.js:39-84):
exports.getExpiringSoonAlerts = async (req, res) => {
  const days = parseInt(req.query.days) || 7;  // Default: 7 days
  
  const today = new Date();
  const futureDate = new Date();
  futureDate.setDate(today.getDate() + days);

  const expiringSoonItems = await Inventory.find({
    expiryDate: { $gte: today, $lte: futureDate },
    quantity: { $gt: 0 }  // Only items in stock
  })
  .sort({ expiryDate: 1 });  // Closest expiry first

  const alerts = expiringSoonItems.map(item => {
    const daysUntilExpiry = Math.ceil(
      (new Date(item.expiryDate) - today) / (1000 * 60 * 60 * 24)
    );
    
    // Dynamic severity based on urgency
    let severity = 'LOW';
    if (daysUntilExpiry <= 1) severity = 'CRITICAL';
    else if (daysUntilExpiry <= 3) severity = 'HIGH';
    else if (daysUntilExpiry <= 5) severity = 'MEDIUM';

    return {
      ...item.toObject(),
      alertType: 'expiring_soon',
      severity,
      daysUntilExpiry,
      message: `Expires in ${daysUntilExpiry} day${daysUntilExpiry !== 1 ? 's' : ''}`
    };
  });
};
Severity Rules:
  • ≤ 1 day: CRITICAL
  • ≤ 3 days: HIGH
  • ≤ 5 days: MEDIUM
  • 6-7 days: LOW

Low Stock Alerts

Items below threshold quantity (alertController.js:7-34):
exports.getLowStockAlerts = async (req, res) => {
  const threshold = parseInt(req.query.threshold) 
    || parseInt(process.env.LOW_STOCK_THRESHOLD) 
    || 10;  // Default threshold

  const lowStockItems = await Inventory.find({
    quantity: { $gt: 0, $lt: threshold }
  })
  .sort({ quantity: 1 });  // Lowest quantity first

  const alerts = lowStockItems.map(item => ({
    ...item.toObject(),
    alertType: 'low_stock',
    severity: item.quantity <= threshold / 2 ? 'HIGH' : 'MEDIUM',
    message: `Only ${item.quantity} units left in stock`
  }));
};
Threshold Configuration:
  • Default: 10 units
  • Configurable via query parameter or environment variable
  • High severity if quantity ≤ 50% of threshold

Out of Stock Alerts

Items with zero quantity (alertController.js:124-150):
exports.getOutOfStockAlerts = async (req, res) => {
  const outOfStockItems = await Inventory.find({
    quantity: 0
  })
  .sort({ updatedAt: -1 });  // Most recently updated first

  const alerts = outOfStockItems.map(item => ({
    ...item.toObject(),
    alertType: 'out_of_stock',
    severity: 'HIGH',
    message: 'Out of stock - Immediate restocking required'
  }));
};

Alert API Endpoints

Get All Critical Alerts

Combined view of all high-priority alerts (alertController.js:155-252):
GET /api/alerts/critical
Response:
{
  "success": true,
  "message": "Critical alerts fetched successfully",
  "data": {
    "alerts": [
      {
        "productName": "Fresh Milk",
        "category": "Dairy",
        "alertType": "expired",
        "severity": "CRITICAL",
        "priority": 1,
        "message": "Expired 2 days ago"
      }
    ],
    "count": 15,
    "breakdown": {
      "expired": 2,
      "expiringSoon": 5,
      "outOfStock": 3,
      "criticalLowStock": 5
    }
  }
}
Priority Order:
  1. Expired items (CRITICAL)
  2. Critically expiring items (≤3 days)
  3. Out of stock items (HIGH)
  4. Critical low stock (below 5 units)

Get Alert Summary

Overall statistics (alertController.js:331-373):
GET /api/alerts/summary
Response:
{
  "summary": {
    "totalAlerts": 28,
    "breakdown": {
      "expired": 2,
      "expiringSoon": 12,
      "outOfStock": 3,
      "lowStock": 11
    },
    "criticalCount": 5
  }
}

Category-Specific Alerts

Alerts filtered by category (alertController.js:257-326):
GET /api/alerts/category/Dairy
Response includes multiple alert types per item:
{
  "category": "Dairy",
  "alerts": [
    {
      "productName": "Fresh Milk",
      "quantity": 3,
      "alerts": [
        {
          "type": "expiring_soon",
          "severity": "HIGH",
          "message": "Expires in 2 days"
        },
        {
          "type": "low_stock",
          "severity": "HIGH",
          "message": "Only 3 units left"
        }
      ]
    }
  ]
}

Individual Alert Endpoints

GET /api/alerts/expired
Returns all expired items

Notification Analytics Endpoint

Special endpoint for Manager Dashboard (analyticsController.js:171-272):
GET /api/analytics/notification-alerts?limit=10
Features:
  • Combines all alert types
  • Sorts by severity
  • Returns human-readable messages
  • Includes timestamps
Implementation:
const alerts = [];

// Expired items
expiredItems.forEach(item => {
  alerts.push({
    type: 'expired',
    severity: 'critical',
    message: `${item.productName} has expired ${daysExpired} days ago`
  });
});

// Expiring soon
expiringSoon.forEach(item => {
  alerts.push({
    type: 'expiring_soon',
    severity: daysUntilExpiry <= 3 ? 'high' : 'medium',
    message: `${item.productName} is expiring in ${daysUntilExpiry} days`
  });
});

// Out of stock
outOfStock.forEach(item => {
  alerts.push({
    type: 'out_of_stock',
    severity: 'high',
    message: `${item.productName} is out of stock`
  });
});

// Low stock
lowStock.forEach(item => {
  alerts.push({
    type: 'low_stock',
    severity: item.quantity < 5 ? 'high' : 'medium',
    message: `${item.productName} is running low (${item.quantity} units remaining)`
  });
});

// Sort by severity
const severityOrder = { critical: 1, high: 2, medium: 3, low: 4 };
alerts.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);

Alert Configuration

Environment Variables

# .env file
LOW_STOCK_THRESHOLD=10
EXPIRY_ALERT_DAYS=7

Runtime Configuration

Alerts can be customized via query parameters:
// Custom low stock threshold
GET /api/alerts/low-stock?threshold=15

// Custom expiry alert window
GET /api/alerts/expiring-soon?days=14

Integration with Other Features

Task Automation

Critical alerts can trigger automatic task creation:
if (alert.severity === 'CRITICAL') {
  await taskService.create({
    description: `URGENT: ${alert.message} - ${alert.productName}`,
    assignedTo: warehouseManagerId,
    priority: 'HIGH'
  });
}

Dashboard Integration

Alerts feed into dashboard KPIs:
// Admin Dashboard Alert Summary
const [expired, expiringSoon, outOfStock, lowStock] = await Promise.all([
  Inventory.countDocuments({ expiryDate: { $lt: today } }),
  Inventory.countDocuments({ 
    expiryDate: { $gte: today, $lte: sevenDaysLater },
    quantity: { $gt: 0 }
  }),
  Inventory.countDocuments({ quantity: 0 }),
  Inventory.countDocuments({ quantity: { $gt: 0, $lt: 10 } })
]);

Best Practices

  • Review critical alerts first thing each morning
  • Address expired items immediately
  • Plan actions for high-severity alerts
  • Monitor trends in alert frequency
  • Adjust thresholds based on consumption rates
  • Different thresholds for different categories
  • Consider supplier lead times
  • Account for demand variability
  1. Triage alerts by severity
  2. Create tasks for action items
  3. Document actions taken
  4. Update inventory accordingly
  5. Review and adjust thresholds
  • Use FEFO to prevent expiry
  • Implement automatic reordering
  • Regular inventory audits
  • Track alert trends

Performance Considerations

Alert queries are optimized with database indexes on:
  • expiryDate (ascending)
  • quantity (ascending)
  • updatedAt (descending)

Access Control

RoleView AlertsConfigure ThresholdsCreate Tasks from Alerts
Admin✅ All
Manager✅ All
Worker

FEFO Ordering

Prevent expiry alerts with FEFO

Demand Forecasting

Predict low stock alerts

Task Management

Create tasks from alerts

Analytics Dashboard

Visualize alert trends

Build docs developers (and LLMs) love