Repository: Neil-urk12/Surplus-Sales-Management-System
Pull request #64: OOP-63 Implement notification alerts functionality
Author: login: StanleighMorales
Created: 2025-05-09T19:46:14Z | Merged: 2025-05-10T03:17:55Z

https://oopfinalscs22agroup3.atlassian.net/browse/OOP-63

Implement notification alerts functionality
and monthly alert notification (will show when 5 days before end of the month


Diff:
diff --git a/Frontend/src/assets/styles/LoginPage.base.css b/Frontend/src/assets/styles/LoginPage.base.css
index 9487c8c..90552f2 100644
--- a/Frontend/src/assets/styles/LoginPage.base.css
+++ b/Frontend/src/assets/styles/LoginPage.base.css
@@ -13,7 +13,7 @@
   border: 1px solid;
   width: 100%;
   max-width: 500px;
-  transition: all 0.3s ease;
+  transition: var(--transition-property) var(--transition-duration) var(--transition-easing);
 }
 
 h1 {
@@ -30,7 +30,7 @@ input {
   border: 1px solid;
   border-radius: 8px;
   font-size: 1rem;
-  transition: all 0.3s ease;
+  transition: var(--transition-property) var(--transition-duration) var(--transition-easing);
 }
 
 .error-message {
@@ -75,7 +75,7 @@ input.error {
   cursor: pointer;
   font-size: 1.5rem;
   opacity: 0.8;
-  transition: opacity 0.3s ease;
+  transition: opacity var(--transition-duration) var(--transition-easing);
 }
 
 .close-icon:hover {
@@ -167,7 +167,7 @@ button {
   font-size: 1rem;
   font-weight: 500;
   cursor: pointer;
-  transition: all 0.3s ease;
+  transition: var(--transition-property) var(--transition-duration) var(--transition-easing);
   display: flex;
   align-items: center;
   justify-content: center;
@@ -228,7 +228,7 @@ button {
 
 .fade-enter-active,
 .fade-leave-active {
-  transition: opacity 0.5s ease;
+  transition: opacity var(--transition-duration) var(--transition-easing);
 }
 
 .fade-enter-from,
@@ -268,4 +268,4 @@ button {
     width: 100px;
     height: 100px;
   }
-} 
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/Frontend/src/components/Global/ProductModal.vue b/Frontend/src/components/Global/ProductModal.vue
index d8b9af5..323a4ed 100644
--- a/Frontend/src/components/Global/ProductModal.vue
+++ b/Frontend/src/components/Global/ProductModal.vue
@@ -69,13 +69,13 @@ const validatedImage = computed(() => {
 const getStatusColor = computed(() => {
   switch (props.status?.toLowerCase()) {
     case 'available':
-      return 'text-positive'
+      return 'text-green'
     case 'in stock':
-      return 'text-positive'
+      return 'text-green'
     case 'low stock':
-      return 'text-warning'
+      return 'text-orange'
     case 'out of stock':
-      return 'text-negative'
+      return 'text-red'
     default:
       return 'text-grey'
   }
diff --git a/Frontend/src/components/dashboard/DynamicAlertsPanel.vue b/Frontend/src/components/dashboard/DynamicAlertsPanel.vue
new file mode 100644
index 0000000..9f4f6a6
--- /dev/null
+++ b/Frontend/src/components/dashboard/DynamicAlertsPanel.vue
@@ -0,0 +1,135 @@
+<template>
+  <q-card bordered>
+    <q-card-section>
+      <div class="text-h6">Inventory Alerts</div>
+    </q-card-section>
+    <q-separator />
+    <q-card-section class="q-pa-none">
+      <q-list>
+        <q-item v-for="alert in alerts" :key="`${alert.category}-${alert.status}`" class="q-pa-md">
+          <q-item-section avatar>
+            <q-icon :name="getAlertIcon(alert.category)" :color="getAlertColor(alert.status)" />
+          </q-item-section>
+          <q-item-section>
+            <q-item-label>{{ alert.category }} - {{ alert.status }}</q-item-label>
+            <q-item-label caption>{{ alert.count }} {{ alert.category.toLowerCase() }} {{ alert.status.toLowerCase() }}</q-item-label>
+          </q-item-section>
+          <q-item-section side>
+            <q-btn
+              flat
+              round
+              dense
+              icon="visibility"
+              @click="handleAlertAction(alert)"
+              :color="getAlertColor(alert.status)"
+            />
+          </q-item-section>
+        </q-item>
+        <q-item v-if="alerts.length === 0" class="text-center q-pa-lg text-grey">
+          <q-item-section>No inventory alerts</q-item-section>
+        </q-item>
+      </q-list>
+    </q-card-section>
+  </q-card>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { useQuasar } from 'quasar';
+import { alertsService, type InventoryAlert } from 'src/services/alertsService';
+
+const $q = useQuasar();
+const router = useRouter();
+const alerts = ref<InventoryAlert[]>([]);
+const isLoading = ref(false);
+
+// Fetch alerts on component mount
+onMounted(async () => {
+  await fetchAlerts();
+});
+
+/**
+ * Fetches inventory alerts from the service
+ */
+async function fetchAlerts() {
+  try {
+    isLoading.value = true;
+    alerts.value = await alertsService.getInventoryAlerts();
+  } catch (error) {
+    console.error('Error fetching alerts:', error);
+    $q.notify({
+      type: 'negative',
+      message: 'Failed to load inventory alerts',
+      position: 'top',
+      timeout: 3000
+    });
+  } finally {
+    isLoading.value = false;
+  }
+}
+
+/**
+ * Map of category to icon mappings for better maintainability
+ */
+const alertIcons = new Map<string, string>([
+  ['Cabs', 'directions_car'],
+  ['Accessories', 'settings_input_component'],
+  ['Materials', 'category'],
+]);
+
+/**
+ * Gets the appropriate icon for each category using the Map
+ */
+function getAlertIcon(category: string): string {
+  return alertIcons.get(category) || 'warning';
+}
+
+/**
+ * Map of status to color mappings for better maintainability
+ */
+const alertColors = new Map<string, string>([
+  ['Low Stock', 'warning'],
+  ['Out of Stock', 'negative'],
+]);
+
+/**
+ * Gets the appropriate color for each alert status using the Map
+ */
+function getAlertColor(status: string): string {
+  return alertColors.get(status) || 'grey';
+}
+
+/**
+ * Handles the action when an alert is clicked
+ */
+async function handleAlertAction(alert: InventoryAlert) {
+  try {
+    // Navigate to the appropriate inventory page with filter
+    const category = alert.category.toLowerCase();
+    const status = alert.status.replace(' ', '-').toLowerCase();
+
+    // Use template literals for better readability
+    await router.push({
+      path: `/inventory/${category}`,
+      query: { status }
+    });
+
+    // Show notification using the same variables for consistency
+    $q.notify({
+      type: 'info',
+      message: `Viewing ${status.replace('-', ' ')} ${category}`,
+      position: 'top',
+      timeout: 2000
+    });
+  } catch (error) {
+    console.error('Error handling alert action:', error);
+    $q.notify({
+      type: 'negative',
+      message: 'Failed to process alert action',
+      position: 'top',
+      timeout: 3000
+    });
+  }
+}
+</script>
diff --git a/Frontend/src/components/dashboard/MetricsCard.vue b/Frontend/src/components/dashboard/MetricsCard.vue
index 13ca664..0012b12 100644
--- a/Frontend/src/components/dashboard/MetricsCard.vue
+++ b/Frontend/src/components/dashboard/MetricsCard.vue
@@ -110,7 +110,7 @@ const createChart = () => {
 
   const isPositive = (props.trendPercentage || 0) >= 0;
   const color = isPositive ? '#21BA45' : '#C10015';
-  
+
   // Create gradient
   const gradient = ctx.createLinearGradient(0, 0, 0, 60);
   gradient.addColorStop(0, `${color}15`);
@@ -175,7 +175,7 @@ watch([() => props.trendData, () => props.isLoading], () => {
 
 <style scoped>
 .metrics-card {
-  transition: all 0.3s;
+  transition: var(--transition-property) var(--transition-duration) var(--transition-easing);
   height: 100%;
   min-height: 160px;
 }
@@ -213,4 +213,4 @@ watch([() => props.trendData, () => props.isLoading], () => {
 .q-icon {
   font-size: 1.2em !important;
 }
-</style> 
+</style>
diff --git a/Frontend/src/constants/uiMessages.ts b/Frontend/src/constants/uiMessages.ts
new file mode 100644
index 0000000..9caed60
--- /dev/null
+++ b/Frontend/src/constants/uiMessages.ts
@@ -0,0 +1,38 @@
+/**
+ * UI text messages used throughout the application
+ * Centralizing these messages makes it easier to:
+ * - Maintain consistent wording
+ * - Update text in one place
+ * - Implement internationalization in the future
+ */
+
+/**
+ * Alert-related messages
+ */
+export const alertMessages = {
+  /**
+   * Messages for notification sections
+   */
+  sections: {
+    unreadNotifications: 'Unread Notifications',
+    readNotifications: 'Read Notifications',
+    systemAlerts: 'System Alerts',
+  },
+  /**
+   * Empty state messages
+   */
+  empty: {
+    noUnreadNotifications: 'No unread notifications',
+    noReadNotifications: 'No read notifications',
+    noActiveNotifications: 'No active notifications',
+  },
+  /**
+   * Action messages
+   */
+  actions: {
+    markAllAsRead: 'Mark all as read',
+    markAsRead: 'Mark as read',
+    viewAll: (count: number) => `View all ${count} read notifications`,
+    loadMore: (remaining: number) => `Load more (${remaining} remaining)`,
+  },
+};
diff --git a/Frontend/src/css/app.scss b/Frontend/src/css/app.scss
index 163ec15..2b7e337 100644
--- a/Frontend/src/css/app.scss
+++ b/Frontend/src/css/app.scss
@@ -1,12 +1,12 @@
 // app global css in SCSS form
 //TEXT CONFIGURATION
 .title-text {
-    font-size: 1.1rem; 
-  
+    font-size: 1.1rem;
+
     @media (min-width: 576px) {
       font-size: 1.1rem;
     }
-  
+
     @media (min-width: 992px) {
       font-size: 1.1rem;
     }
@@ -24,7 +24,7 @@
   .custom-style::placeholder {
     color: rgb(97, 97, 97);;
     opacity: 1 ;
-    
+
   }
   .custom-style::-webkit-input-placeholder { color: rgb(97, 97, 97) ; }
   .custom-style::-moz-placeholder { color: rgb(97, 97, 97) ; }
@@ -33,7 +33,7 @@
 //LAYOUT CONFIGURATION
   //*Navbar
   .custom-nav-height-width {
-    height: 62px; 
+    height: 62px;
     min-height: 62px;
   };
   .custom-rounded{
@@ -50,7 +50,7 @@
     height: 44px;
   }
   //BORDER
- 
+
 
 
   //DARK THEME
@@ -65,8 +65,13 @@
     --border-color: #e4e4e7;
     --hover-bg: #f4f4f5;
     --sticky-column-bg: #09090b; // Light theme sticky column background
+
+    // Transition variables for consistent animations
+    --transition-duration: 0.3s;
+    --transition-easing: ease;
+    --transition-property: all;
   }
-  
+
   body.body--dark {
     // Dark theme colors from shadcn UI
     --page-bg: #09090b;
@@ -78,49 +83,54 @@
     --border-color: #27272a;
     --hover-bg: #18181b;
     --sticky-column-bg: #ffffff; // Dark theme sticky column background
+
+    // Transition variables for consistent animations (same as light theme)
+    --transition-duration: 0.3s;
+    --transition-easing: ease;
+    --transition-property: all;
   }
-  
-  
+
+
   .q-page-container,
   .q-page {
     background-color: var(--page-bg) ;
   }
-  
+
   .q-header,
   .q-drawer {
     background-color: var(--header-nav-bg) ;
   }
-  
-  
+
+
   .q-icon,
   .text-soft-light,
   .text-primary,
   .text-secondary {
     color: var(--icon-text-color) ;
   }
-  
-  
+
+
   .q-toolbar,
   .q-toolbar-title,
   .q-btn {
     color: var(--header-content-color) ;
   }
-  
-  
+
+
   .custom-style {
     color: var(--header-content-color) ;
   }
-  
+
   .custom-style::placeholder {
     color: var(--input-placeholder-color) ;
     opacity: 1 ;
   }
-  
+
   .custom-style::-webkit-input-placeholder { color: var(--input-placeholder-color) ; }
   .custom-style::-moz-placeholder { color: var(--input-placeholder-color) ; }
   .custom-style:-ms-input-placeholder { color: var(--input-placeholder-color) ; }
   .custom-style:-moz-placeholder { color: var(--input-placeholder-color) ; }
-  
+
 //INVENTORY PAGE
 .inventory-header {
   height: 10rem;
@@ -146,7 +156,7 @@
 .q-btn {
   border-radius: var(--radius, 0.5rem);
   font-weight: 500;
-  transition: all 0.2s ease;
+  transition: var(--transition-property) var(--transition-duration) var(--transition-easing);
 
   &.q-btn--standard {
     background-color: var(--primary);
diff --git a/Frontend/src/css/quasar.variables.scss b/Frontend/src/css/quasar.variables.scss
index 5e4ad5b..f0dbf54 100644
--- a/Frontend/src/css/quasar.variables.scss
+++ b/Frontend/src/css/quasar.variables.scss
@@ -49,83 +49,93 @@ $grey-9    : #18181b; // Darkest gray
   // Base colors
   --background: #{$white};
   --foreground: #{$grey-9};
-  
+
   // Card colors
   --card: #{$white};
   --card-foreground: #{$grey-9};
-  
+
   // Popover colors
   --popover: #{$white};
   --popover-foreground: #{$grey-9};
-  
+
   // Primary colors
   --primary: #{$primary};
   --primary-foreground: #{$white};
-  
+
   // Secondary colors
   --secondary: #{$grey-2};
   --secondary-foreground: #{$grey-9};
-  
+
   // Muted colors
   --muted: #{$grey-2};
   --muted-foreground: #{$grey-5};
-  
+
   // Accent colors
   --accent: #{$grey-2};
   --accent-foreground: #{$grey-9};
-  
+
   // Destructive colors
   --destructive: #{$negative};
   --destructive-foreground: #{$white};
-  
+
   // Border and input colors
   --border: #{$grey-3};
   --input: #{$grey-3};
-  
+
   // Ring color for focus states
   --ring: #{$primary};
-  
+
   // Border radius
   --radius: 0.5rem;
+
+  // Transition variables for consistent animations
+  --transition-duration: 0.3s;
+  --transition-easing: ease;
+  --transition-property: all;
 }
 
 .dark, body.body--dark {
   // Base colors
   --background: #{$dark};
   --foreground: #{$grey-1};
-  
+
   // Card colors
   --card: #{$dark-card};
   --card-foreground: #{$grey-1};
-  
+
   // Popover colors
   --popover: #{$dark-card};
   --popover-foreground: #{$grey-1};
-  
+
   // Primary colors
   --primary: #{$primary};
   --primary-foreground: #{$white};
-  
+
   // Secondary colors
   --secondary: #{$grey-8};
   --secondary-foreground: #{$grey-1};
-  
+
   // Muted colors
   --muted: #{$grey-8};
   --muted-foreground: #{$grey-4};
-  
+
   // Accent colors
   --accent: #{$grey-8};
   --accent-foreground: #{$grey-1};
-  
+
   // Destructive colors
   --destructive: #{$negative};
   --destructive-foreground: #{$white};
-  
+
   // Border and input colors
   --border: #{$grey-7};
   --input: #{$grey-7};
-  
+
   // Ring color for focus states
   --ring: #{$primary};
+
+  // Transition variables for consistent animations (same as light theme)
+  --transition-duration: 0.3s;
+  --transition-easing: ease;
+  --transition-property: all;
 }
diff --git a/Frontend/src/layouts/MainLayout.vue b/Frontend/src/layouts/MainLayout.vue
index d171cf8..b249b8e 100644
--- a/Frontend/src/layouts/MainLayout.vue
+++ b/Frontend/src/layouts/MainLayout.vue
@@ -23,36 +23,151 @@
           </q-btn>
           <div class="notification-wrapper">
             <q-btn dense round flat icon="notifications" class="text-soft-light">
-              <q-badge color="red" floating>
-                {{ systemAlerts.length }}
-              </q-badge>
-              <q-menu anchor="bottom right" self="top right">
+            <q-badge
+              v-if="unreadAlertsCount > 0"
+              color="red"
+              floating
+            >
+              {{ unreadAlertsCount }}
+            </q-badge>
+              <q-menu anchor="bottom right" self="top right" @hide="resetReadAlertsLimit">
                 <q-list style="min-width: 350px">
                   <q-item-label header class="row items-center justify-between">
-                    <span>System Alerts</span>
+                    <span>{{ alertMessages.sections.systemAlerts }}</span>
                     <q-btn flat round dense icon="done_all" size="sm" @click="markAllAsRead" v-if="systemAlerts.length">
-                      <q-tooltip>Mark all as read</q-tooltip>
+                      <q-tooltip>{{ alertMessages.actions.markAllAsRead }}</q-tooltip>
                     </q-btn>
                   </q-item-label>
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Notifications Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          {{ alertMessages.sections.unreadNotifications }}
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="notification-item"
+                            :class="`notification-unread notification-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="notification-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="notification-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
+                                icon="done"
+                                size="sm"
+                                :color="getAlertColor(alert.severity)"
+                                @click.stop="markAlertAsRead(alert.id)"
+                              >
+                                <q-tooltip>{{ alertMessages.actions.markAsRead }}</q-tooltip>
+                              </q-btn>
+                            </q-item-section>
+                          </q-item>
+                        </template>
+
+                        <q-item v-else>
+                          <q-item-section class="text-center text-grey q-py-sm">
+                            <div>{{ alertMessages.empty.noUnreadNotifications }}</div>
+                          </q-item-section>
+                        </q-item>
+                      </div>
+
+                      <q-separator v-if="unreadAlerts.length > 0 && readAlerts.length > 0" class="q-my-sm" />
+
+                      <!-- Read Notifications Section -->
+                      <div>
+                        <q-item-label header class="text-grey q-px-md q-pt-md">
+                          {{ alertMessages.sections.readNotifications }}
+                        </q-item-label>
+
+                        <template v-if="readAlerts.length > 0">
+                          <q-item
+                            v-for="alert in readAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="notification-item notification-read"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="notification-icon-read"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="notification-title-read">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                          </q-item>
+                        </template>
+
+                        <q-item v-else>
+                          <q-item-section class="text-center text-grey q-py-sm">
+                            <div>{{ alertMessages.empty.noReadNotifications }}</div>
+                          </q-item-section>
+                        </q-item>
+
+                        <!-- Load More Button - Improves performance by loading read alerts incrementally -->
+                        <q-item
+                          v-if="hasMoreReadAlerts"
+                          clickable
+                          class="text-primary"
+                          @click="loadMoreReadAlerts"
+                        >
+                          <q-item-section class="text-center">
+                            <q-item-label>
+                              {{ alertMessages.actions.loadMore(remainingReadAlerts) }}
+                            </q-item-label>
+                          </q-item-section>
+                        </q-item>
+
+                        <!-- View All Button -->
+                        <q-item
+                          v-if="allReadAlerts.length > 0"
+                          clickable
+                          class="text-primary"
+                          @click="showAllReadAlerts"
+                        >
+                          <q-item-section class="text-center">
+                            <q-item-label>
+                              {{ alertMessages.actions.viewAll(allReadAlerts.length) }}
+                            </q-item-label>
+                          </q-item-section>
+                        </q-item>
+                      </div>
                     </q-scroll-area>
                   </template>
                   <q-item v-else>
-                    <q-item-section class="text-center text-grey q-py-md">
-                      <q-icon name="notifications_none" size="48px" color="grey-4" />
-                      <div class="q-mt-sm">No active alerts</div>
+                    <q-item-section class="text-center text-grey q-py-md flex content-center row">
+                      <q-icon class="col" name="notifications_none" size="48px" color="grey-4" />
+                      <div class="q-mt-sm col">{{ alertMessages.empty.noActiveNotifications }}</div>
                     </q-item-section>
                   </q-item>
                 </q-list>
@@ -142,6 +257,8 @@ import { useAuthStore } from '../stores/auth'
 const MenuItems = defineAsyncComponent(() => import('../components/SideMenuItems.vue'))
 import type { menuItemsProps } from '../types/menu-items'
 import type { AlertSeverity } from '../components/dashboard/AlertsPanel.vue';
+import { alertsService, type InventoryAlert } from '../services/alertsService';
+import { alertMessages } from '../constants/uiMessages';
 
 const menuItemsList: menuItemsProps[] = [
   {
@@ -227,29 +344,101 @@ const severityColorMap: Record<AlertSeverity, string> = {
   info: 'info'
 };
 
-// Update systemAlerts with timestamps
-const systemAlerts = ref<Alert[]>([
-  {
-    id: '1',
-    title: 'Low Stock Warning',
-    message: '5 items are running low on stock',
-    severity: 'warning' as AlertSeverity,
-    icon: 'warning',
-    actionIcon: 'visibility',
-    timestamp: new Date(),
-    read: false
-  },
-  {
-    id: '2',
-    title: 'Inventory Check Required',
-    message: 'Monthly inventory check due in 2 days',
-    severity: 'info' as AlertSeverity,
-    icon: 'inventory',
-    actionIcon: 'event',
-    timestamp: new Date(Date.now() - 3600000),
-    read: false
+// System alerts array
+const systemAlerts = ref<Alert[]>([]);
+
+// Inventory alerts from the service
+const inventoryAlerts = ref<InventoryAlert[]>([]);
+
+// Function to fetch inventory alerts and convert them to system alerts
+async function fetchInventoryAlerts() {
+  try {
+    inventoryAlerts.value = await alertsService.getInventoryAlerts();
+
+    // Convert inventory alerts to system alerts
+    const newSystemAlerts: Alert[] = inventoryAlerts.value.map((alert) => {
+      // Create a unique ID for each alert based on category and status
+      // This ensures consistent IDs across refreshes and prevents duplicate IDs
+      const alertId = `inventory-${alert.category.toLowerCase()}-${alert.status.toLowerCase()}`;
+
+      // Determine severity based on status
+      const severity: AlertSeverity = alert.status === 'Out of Stock' ? 'error' : 'warning';
+
+      // Determine icon based on category
+      let icon = 'warning';
+      if (alert.category === 'Cabs') icon = 'directions_car';
+      else if (alert.category === 'Accessories') icon = 'settings_input_component';
+      else if (alert.category === 'Materials') icon = 'category';
+
+      // Create the alert message
+      const title = `${alert.category} ${alert.status}`;
+      const message = `${alert.count} ${alert.category.toLowerCase()} ${alert.status.toLowerCase()}`;
+
+      return {
+        id: alertId,
+        title,
+        message,
+        severity,
+        icon,
+        actionIcon: 'visibility',
+        timestamp: new Date(),
+        read: false
+      };
+    });
+
+    // Add inventory check reminder if we're within 5 days of the end of the month
+    const daysUntilEndOfMonth = getDaysUntilEndOfMonth();
+    if (shouldShowInventoryCheckReminder()) {
+      // Get the name of the current month
+      const monthNames = ['January', 'February', 'March', 'April', 'May', 'June',
+                          'July', 'August', 'September', 'October', 'November', 'December'];
+      const currentMonth = monthNames[new Date().getMonth()];
+
+      // Determine severity based on how close we are to the end of the month
+      let severity: AlertSeverity = 'info';
+      let title = 'Monthly Inventory Check Reminder';
+      let message = `${currentMonth} inventory check due in ${daysUntilEndOfMonth} day${daysUntilEndOfMonth === 1 ? '' : 's'}`;
+
+      if (daysUntilEndOfMonth <= 2) {
+        severity = 'warning';
+        title = 'Urgent: Inventory Check Due Soon';
+      }
+
+      if (daysUntilEndOfMonth === 0) {
+        severity = 'error';
+        title = 'CRITICAL: Inventory Check Due Today';
+        message = `${currentMonth} inventory check must be completed TODAY!`;
+      }
+
+      newSystemAlerts.push({
+        id: 'inventory-check',
+        title: title,
+        message: message,
+        severity: severity,
+        icon: 'inventory',
+        actionIcon: 'event',
+        timestamp: new Date(),
+        read: false
+      });
+    }
+
+    // Update system alerts
+    systemAlerts.value = newSystemAlerts;
+  } catch (error) {
+    console.error('Error fetching inventory alerts:', error);
+    // Add a fallback alert if there's an error
+    systemAlerts.value = [{
+      id: 'error',
+      title: 'Error Loading Notifications',
+      message: 'Failed to load inventory notifications',
+      severity: 'error',
+      icon: 'error',
+      actionIcon: 'refresh',
+      timestamp: new Date(),
+      read: false
+    }];
   }
-]);
+}
 
 watch(() => route.path, (newPath) => {
   switch (newPath) {
@@ -278,14 +467,117 @@ watch(() => route.path, (newPath) => {
 
 const themeMode = computed(() => ($q.dark.isActive ? 'Dark Mode' : 'Light Mode'))
 
-onMounted(() => {
+// Computed property for unread alerts
+const unreadAlerts = computed(() => {
+  return systemAlerts.value.filter(alert => !alert.read)
+    .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); // Sort by timestamp, newest first
+})
+
+// Get all read alerts (for counting)
+const allReadAlerts = computed(() => {
+  return systemAlerts.value.filter(alert => alert.read)
+    .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); // Sort by timestamp, newest first
+})
+
+// State variable to track how many read alerts to display
+const readAlertsLimit = ref(5);
+
+/**
+ * Computed property for read alerts with dynamic limit
+ * This implements a pagination-like approach where we initially show a limited number
+ * of read alerts and allow the user to load more as needed, improving performance
+ * when there are many read alerts in the system.
+ */
+const readAlerts = computed(() => {
+  return allReadAlerts.value.slice(0, readAlertsLimit.value); // Limit to the current readAlertsLimit
+})
+
+// Check if there are more read alerts than we're showing
+const hasMoreReadAlerts = computed(() => {
+  return allReadAlerts.value.length > readAlertsLimit.value;
+})
+
+// Calculate the number of remaining read alerts
+const remainingReadAlerts = computed(() => {
+  return allReadAlerts.value.length - readAlertsLimit.value;
+})
+
+/**
+ * Function to load more read alerts
+ * Increases the limit by 5 each time, up to a maximum of 20 at a time
+ * to prevent performance issues with loading too many alerts at once
+ */
+function loadMoreReadAlerts() {
+  // Define a maximum number of alerts to show at once to prevent performance issues
+  const MAX_ALERTS_TO_SHOW = 20;
+
+  // Increase the limit by 5 each time, but don't exceed the maximum
+  readAlertsLimit.value = Math.min(readAlertsLimit.value + 5, MAX_ALERTS_TO_SHOW);
+
+  // If we've reached the maximum but there are still more alerts,
+  // show a notification to the user
+  if (readAlertsLimit.value === MAX_ALERTS_TO_SHOW && remainingReadAlerts.value > 0) {
+    $q.notify({
+      type: 'info',
+      message: `Showing ${MAX_ALERTS_TO_SHOW} most recent read alerts. Use "View All" to see all ${allReadAlerts.value.length} alerts.`,
+      position: 'top',
+      timeout: 3000
+    });
+  }
+}
+
+// Function to reset the read alerts limit when the menu is closed
+function resetReadAlertsLimit() {
+  // Reset to the initial limit
+  readAlertsLimit.value = 5;
+}
+
+// Computed property to count unread alerts
+const unreadAlertsCount = computed(() => {
+  return unreadAlerts.value.length;
+})
+
+onMounted(async () => {
+  // Set theme mode
   const savedMode = localStorage.getItem('quasar-theme')
   if (savedMode) {
     $q.dark.set(savedMode === 'dark')
   } else {
     $q.dark.set(window.matchMedia('(prefers-color-scheme: dark)').matches)
   }
-//for the time out cleanup
+
+  // Fetch inventory alerts
+  try {
+    await fetchInventoryAlerts();
+  } catch (error) {
+    console.error('Failed to fetch inventory alerts during initialization:', error);
+    $q.notify({
+      type: 'negative',
+      message: 'Failed to load inventory alerts',
+      caption: 'Please refresh the page or contact support if the issue persists',
+      position: 'top',
+      timeout: 5000
+    });
+  }
+
+  // Set up interval to refresh alerts every 5 minutes
+  const alertsInterval = setInterval(() => {
+    // Wrap in async IIFE with try-catch for proper error handling
+    void (async () => {
+      try {
+        await fetchInventoryAlerts();
+      } catch (error) {
+        console.error('Failed to fetch inventory alerts during refresh interval:', error);
+        // Don't show notification for background refresh errors to avoid spamming the user
+        // The error alert will already be shown by the fetchInventoryAlerts function
+      }
+    })();
+  }, 5 * 60 * 1000)
+
+  // Clean up interval on component unmount
+  return () => {
+    clearInterval(alertsInterval)
+  }
 })
 
 const toggleColorMode = () => {
@@ -330,6 +622,98 @@ function formatAlertTime(date: Date): string {
   return `${days} days ago`;
 }
 
+/**
+ * Calculates the number of days until the end of the current month
+ * @returns The number of days until the end of the month
+ */
+function getDaysUntilEndOfMonth(): number {
+  const today = new Date();
+  // Create a date for the first day of the next month
+  const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
+  // Subtract one day to get the last day of the current month
+  const lastDayOfMonth = new Date(nextMonth.getTime() - 86400000);
+  // Calculate the difference in days
+  const diffTime = lastDayOfMonth.getTime() - today.getTime();
+  const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
+  return diffDays;
+}
+
+/**
+ * Checks if we're within the reminder period for the monthly inventory check
+ * and if the user hasn't already scheduled the check for this month
+ * @param daysBeforeEndOfMonth Number of days before the end of the month to show the reminder
+ * @returns True if we should show the reminder, false otherwise
+ */
+function shouldShowInventoryCheckReminder(daysBeforeEndOfMonth: number = 5): boolean {
+  const daysRemaining = getDaysUntilEndOfMonth();
+
+  // Check if the user has already scheduled the inventory check for this month
+  const today = new Date();
+  const currentMonthYear = `${today.getMonth()}-${today.getFullYear()}`;
+  const scheduledCheck = localStorage.getItem('scheduledInventoryCheck');
+
+  // If the user has scheduled a check for the current month, don't show the reminder
+  if (scheduledCheck === currentMonthYear) {
+    return false;
+  }
+
+  // Show the reminder if we're within the specified number of days of the end of the month
+  return daysRemaining <= daysBeforeEndOfMonth;
+}
+
+/**
+ * Marks the inventory check as scheduled for the current month
+ */
+function markInventoryCheckScheduled(): void {
+  const today = new Date();
+  const currentMonthYear = `${today.getMonth()}-${today.getFullYear()}`;
+  localStorage.setItem('scheduledInventoryCheck', currentMonthYear);
+}
+
+/**
+ * Marks a specific notification as read
+ * @param alertId The ID of the notification to mark as read
+ */
+function markAlertAsRead(alertId: string) {
+  const alertIndex = systemAlerts.value.findIndex(a => a.id === alertId);
+  if (alertIndex !== -1) {
+    const alert = systemAlerts.value[alertIndex];
+    if (alert) {
+      alert.read = true;
+    }
+  }
+}
+
+/**
+ * Shows a dialog with all read notifications
+ */
+function showAllReadAlerts() {
+  // Create a list of all read notifications for the dialog
+  const alertsList = allReadAlerts.value.map(alert => {
+    return `<div class="q-mb-md">
+      <div class="text-weight-medium">${alert.title}</div>
+      <div class="text-caption">${alert.message}</div>
+      <div class="text-caption text-grey">${formatAlertTime(alert.timestamp)}</div>
+    </div>`;
+  }).join('');
+
+  // Show dialog with all read notifications
+  $q.dialog({
+    title: alertMessages.sections.readNotifications,
+    message: `<div class="q-pa-md">${alertsList}</div>`,
+    html: true,
+    style: 'width: 500px; max-width: 80vw',
+    ok: {
+      label: 'Close',
+      flat: true,
+      color: 'primary'
+    }
+  });
+}
+
+/**
+ * Marks all notifications as read
+ */
 function markAllAsRead() {
   systemAlerts.value = systemAlerts.value.map(alert => ({
     ...alert,
@@ -337,10 +721,10 @@ function markAllAsRead() {
   }));
 }
 
-// Update handleAlertAction to mark alerts as read with type safety
+// Update handleAlertAction to handle notification clicks
 async function handleAlertAction(alertId: string) {
   try {
-    // Mark the alert as read
+    // Mark the notification as read
     const alertIndex = systemAlerts.value.findIndex(a => a.id === alertId);
     if (alertIndex !== -1) {
       const alert = systemAlerts.value[alertIndex];
@@ -349,34 +733,103 @@ async function handleAlertAction(alertId: string) {
       }
     }
 
-    switch (alertId) {
-      case '1': {
+    // Handle inventory alerts
+    if (alertId.startsWith('inventory-')) {
+      const parts = alertId.split('-');
+      const category = parts[1];
+      const status = parts[2];
+
+      // Navigate to the appropriate inventory page with filter
+      if (category && ['cabs', 'accessories', 'materials'].includes(category)) {
         await router.push({
-          path: '/inventory/cabs',
-          query: { filter: 'low-stock' }
+          path: `/inventory/${category}`,
+          query: { status: status }
+        });
+
+        $q.notify({
+          type: 'info',
+          message: `Viewing ${status} ${category}`,
+          position: 'top',
+          timeout: 2000
         });
-        break;
       }
-      case '2': {
-        $q.dialog({
-          title: 'Schedule Inventory Check',
-          message: 'Would you like to schedule the monthly inventory check?',
-          ok: {
-            label: 'Schedule Now',
-            color: 'primary'
-          },
-          cancel: {
-            label: 'Remind Me Later',
-            color: 'grey'
-          },
-          persistent: true
-        }).onOk(() => {
-          $q.notify({
-            type: 'positive',
-            message: 'Inventory check scheduled successfully'
-          });
+    }
+    // Handle inventory check alert
+    else if (alertId === 'inventory-check') {
+      // Get the last day of the month for the inventory check date
+      const today = new Date();
+      const nextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1);
+      const lastDayOfMonth = new Date(nextMonth.getTime() - 86400000);
+      const formattedDate = lastDayOfMonth.toLocaleDateString('en-US', {
+        weekday: 'long',
+        year: 'numeric',
+        month: 'long',
+        day: 'numeric'
+      });
+
+      // Get the name of the current month
+      const monthNames = ['January', 'February', 'March', 'April', 'May', 'June',
+                          'July', 'August', 'September', 'October', 'November', 'December'];
+      const currentMonth = monthNames[today.getMonth()];
+
+      $q.dialog({
+        title: 'Schedule Inventory Check',
+        message: `The ${currentMonth} inventory check is due by ${formattedDate}. Would you like to schedule it now?`,
+        html: true,
+        ok: {
+          label: 'Schedule Now',
+          color: 'primary'
+        },
+        cancel: {
+          label: 'Remind Me Later',
+          color: 'grey'
+        },
+        persistent: true
+      }).onOk(() => {
+        // Mark the inventory check as scheduled for this month
+        markInventoryCheckScheduled();
+
+        // Remove the notification from the list
+        const alertIndex = systemAlerts.value.findIndex(a => a.id === alertId);
+        if (alertIndex !== -1) {
+          systemAlerts.value.splice(alertIndex, 1);
+        }
+
+        $q.notify({
+          type: 'positive',
+          message: `${currentMonth} inventory check scheduled for ${formattedDate}`,
+          timeout: 3000
+        });
+      });
+    }
+    // Handle error alert
+    else if (alertId === 'error') {
+      // Refresh notifications
+      try {
+        $q.notify({
+          type: 'info',
+          message: 'Refreshing notifications',
+          position: 'top',
+          timeout: 2000
+        });
+
+        await fetchInventoryAlerts();
+
+        $q.notify({
+          type: 'positive',
+          message: 'Notifications refreshed successfully',
+          position: 'top',
+          timeout: 2000
+        });
+      } catch (error) {
+        console.error('Failed to refresh notifications:', error);
+        $q.notify({
+          type: 'negative',
+          message: 'Failed to refresh notifications',
+          caption: 'Please try again or contact support if the issue persists',
+          position: 'top',
+          timeout: 3000
         });
-        break;
       }
     }
   } catch (error) {
@@ -409,4 +862,63 @@ async function handleAlertAction(alertId: string) {
 .notif-count {
   display: none !important;
 }
+
+/* Notification styling */
+.notification-item {
+  transition: var(--transition-property) var(--transition-duration) var(--transition-easing);
+}
+
+.notification-unread {
+  border-left: 4px solid;
+  background-color: rgba(var(--q-primary-rgb), 0.05);
+}
+
+.notification-unread-error {
+  border-left-color: var(--q-negative);
+}
+
+.notification-unread-warning {
+  border-left-color: var(--q-warning);
+}
+
+.notification-unread-info {
+  border-left-color: var(--q-info);
+}
+
+.notification-unread-success {
+  border-left-color: var(--q-positive);
+}
+
+.notification-read {
+  opacity: 0.7;
+  background-color: transparent;
+  border-left: 4px solid transparent;
+}
+
+.q-dark .notification-unread {
+  background-color: rgba(255, 255, 255, 0.1);
+}
+
+.q-dark .notification-read {
+  background-color: transparent;
+  opacity: 0.5;
+}
+
+.notification-title-unread {
+  font-weight: 600;
+}
+
+.notification-title-read {
+  font-weight: 400;
+}
+
+.notification-icon-unread {
+  transform: scale(1.1);
+  transition: transform var(--transition-duration) var(--transition-easing);
+}
+
+.notification-icon-read {
+  opacity: 0.7;
+  transition: opacity var(--transition-duration) var(--transition-easing);
+}
 </style>
diff --git a/Frontend/src/pages/AccessoriesPage.vue b/Frontend/src/pages/AccessoriesPage.vue
index 6eebb65..232c093 100644
--- a/Frontend/src/pages/AccessoriesPage.vue
+++ b/Frontend/src/pages/AccessoriesPage.vue
@@ -298,7 +298,7 @@ onMounted(async () => {
                 @click="openAddDialog"
                 :disable="pageLoading"
                 :class="[
-                  $q.dark.isActive ? 'text-black bg-white' : 'text-white bg-primary', 
+                  $q.dark.isActive ? 'text-black bg-white' : 'text-white bg-primary',
                   { 'full-width': $q.screen.lt.md }
                 ]"
               >
@@ -310,8 +310,8 @@ onMounted(async () => {
                 flat
                 :disable="pageLoading"
                 :class="[
-                  $q.dark.isActive ? 'bg-white text-black' : 'bg-primary text-white', 
-                  'q-pa-sm', 
+                  $q.dark.isActive ? 'bg-white text-black' : 'bg-primary text-white',
+                  'q-pa-sm',
                   { 'full-width': $q.screen.lt.md }
                 ]"
               >
@@ -377,7 +377,7 @@ onMounted(async () => {
       </div>
 
       <!--ACCESSORIES TABLE - Only show when not loading and no errors -->
-      <q-table v-if="!pageLoading && !hasApiError" class="my-sticky-column-table custom-table-text" flat bordered 
+      <q-table v-if="!pageLoading && !hasApiError" class="my-sticky-column-table custom-table-text" flat bordered
         :rows="store.filteredAccessoryRows" :columns="columns" row-key="id" :filter="store.search.searchValue"
         @row-click="onRowClick" :pagination="{ rowsPerPage: 10 }" :rows-per-page-options="[10]" :loading="store.isLoading">
         <template v-slot:loading>
@@ -388,7 +388,7 @@ onMounted(async () => {
         </template>
         <template v-slot:body-cell-status="props">
           <q-td :props="props">
-            <q-badge :color="props.row.status === 'In Stock' ? 'green' : (props.row.status === 'Out of Stock' || props.row.status === 'Low Stock' ? 'red' : 'grey')" :label="props.row.status" />
+            <q-badge :color="props.row.status === 'In Stock' ? 'green' : (props.row.status === 'Out of Stock' ? 'red' : 'orange')" :label="props.row.status" />
           </q-td>
         </template>
         <template v-slot:body-cell-actions="props">
diff --git a/Frontend/src/pages/CabsPage.vue b/Frontend/src/pages/CabsPage.vue
index e15549c..92b7e82 100644
--- a/Frontend/src/pages/CabsPage.vue
+++ b/Frontend/src/pages/CabsPage.vue
@@ -368,7 +368,7 @@ onMounted(async () => {
               <AdvancedSearch
                 v-model="store.search.searchInput"
                 placeholder="Search cabs"
-                @clear="handleResetFilters" 
+                @clear="handleResetFilters"
                 color="primary"
                 :disable="store.isLoading"
                 :style="$q.screen.lt.md ? { width: '100%' } : { width: '400px' }"
@@ -391,7 +391,7 @@ onMounted(async () => {
                 @click="openAddDialog"
                 :disable="store.isLoading"
                 :class="[
-                  $q.dark.isActive ? 'text-black bg-white' : 'text-white bg-primary', 
+                  $q.dark.isActive ? 'text-black bg-white' : 'text-white bg-primary',
                   { 'full-width': $q.screen.lt.md }
                 ]"
               >
@@ -404,8 +404,8 @@ onMounted(async () => {
                 @click="handleDownloadCsv"
                 :disable="store.isLoading"
                 :class="[
-                  $q.dark.isActive ? 'bg-white text-black' : 'bg-primary text-white', 
-                  'q-pa-sm', 
+                  $q.dark.isActive ? 'bg-white text-black' : 'bg-primary text-white',
+                  'q-pa-sm',
                   { 'full-width': $q.screen.lt.md }
                 ]"
               >
@@ -417,7 +417,7 @@ onMounted(async () => {
         </div>
       </div>
 
-      <CabsTable 
+      <CabsTable
         :rows="store.filteredCabRows || []"
         :columns="columns"
         :is-loading="store.isLoading"
@@ -508,7 +508,7 @@ onMounted(async () => {
     font-weight: 500
 
     .q-badge
-      font-size: 1em 
-      font-weight: 600 
+      font-size: 1em
+      font-weight: 600
 
 </style>
diff --git a/Frontend/src/pages/DashboardPage.vue b/Frontend/src/pages/DashboardPage.vue
index 7e7d7e3..a340af3 100644
--- a/Frontend/src/pages/DashboardPage.vue
+++ b/Frontend/src/pages/DashboardPage.vue
@@ -4,6 +4,7 @@ import { useQuasar } from 'quasar';
 const MetricsCard = defineAsyncComponent(() => import('../components/dashboard/MetricsCard.vue'));
 const ChartSection = defineAsyncComponent(() => import('../components/dashboard/ChartSection.vue'));
 const RecentActivitiesSection = defineAsyncComponent(() => import('../components/dashboard/RecentActivitiesSection.vue'));
+const DynamicAlertsPanel = defineAsyncComponent(() => import('../components/dashboard/DynamicAlertsPanel.vue'));
 
 const $q = useQuasar();
 
@@ -215,7 +216,7 @@ async function refreshData() {
   try {
     // TODO: Implement actual data fetching
     await new Promise(resolve => setTimeout(resolve, 1000));
-    
+
     // Mock data update
     totalInventoryValue.value = 2748560;
     totalSales.value = 845675;
@@ -269,12 +270,12 @@ const fetchTimeData = () => {
             }
           ]
         };
-        
+
         if (weeklyData.datasets[0] && weeklyData.datasets[1]) {
           const carsData = [...weeklyData.datasets[0].data];
           const accessoriesData = [...weeklyData.datasets[1].data];
           const totalData = carsData.map((val, idx) => val + (accessoriesData[idx] || 0));
-          
+
           weeklyData.datasets.push({
             label: 'Total',
             data: totalData,
@@ -283,11 +284,11 @@ const fetchTimeData = () => {
             fill: false
           });
         }
-        
+
         weeklySalesTrendData.value = weeklyData;
         break;
       }
-      
+
       case 'monthly': {
         const monthlyData: ChartData = {
           labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
@@ -308,12 +309,12 @@ const fetchTimeData = () => {
             }
           ]
         };
-        
+
         if (monthlyData.datasets[0] && monthlyData.datasets[1]) {
           const carsData = [...monthlyData.datasets[0].data];
           const accessoriesData = [...monthlyData.datasets[1].data];
           const totalData = carsData.map((val, idx) => val + (accessoriesData[idx] || 0));
-          
+
           monthlyData.datasets.push({
             label: 'Total',
             data: totalData,
@@ -322,11 +323,11 @@ const fetchTimeData = () => {
             fill: false
           });
         }
-        
+
         salesTrendData.value = monthlyData;
         break;
       }
-      
+
       case 'yearly': {
         const currentYear = new Date().getFullYear();
         const yearlyData: ChartData = {
@@ -353,12 +354,12 @@ const fetchTimeData = () => {
             }
           ]
         };
-        
+
         if (yearlyData.datasets[0] && yearlyData.datasets[1]) {
           const carsData = [...yearlyData.datasets[0].data];
           const accessoriesData = [...yearlyData.datasets[1].data];
           const totalData = carsData.map((val, idx) => val + (accessoriesData[idx] || 0));
-          
+
           yearlyData.datasets.push({
             label: 'Total',
             data: totalData,
@@ -367,7 +368,7 @@ const fetchTimeData = () => {
             fill: false
           });
         }
-        
+
         yearlySalesTrendData.value = yearlyData;
         break;
       }
@@ -414,11 +415,11 @@ watch(
           <p class="text-subtitle1 q-mt-xs">System Overview</p>
         </div>
         <div class="col-auto">
-          <q-btn 
+          <q-btn
             :class="[
               $q.dark.isActive ? 'bg-white text-black' : 'bg-primary text-white'
-            ]" 
-            @click="refreshData" 
+            ]"
+            @click="refreshData"
             :loading="isLoading"
           >
             <q-icon :name="'refresh'" :color="$q.dark.isActive ? 'black' : 'white'" />
@@ -496,11 +497,14 @@ watch(
         :is-loading="isLoading"
       />
 
-      <!-- Activity Feed Section -->
+      <!-- Activity and Alerts Section -->
       <div class="row q-col-gutter-md q-mb-xl">
-        <div class="col-12">
+        <div class="col-12 col-md-8">
           <recent-activities-section :activities="recentActivities" :is-loading="isLoading" />
         </div>
+        <div class="col-12 col-md-4">
+          <dynamic-alerts-panel />
+        </div>
       </div>
     </div>
   </q-page>
diff --git a/Frontend/src/pages/MaterialsPage.vue b/Frontend/src/pages/MaterialsPage.vue
index f86ff60..9e22615 100644
--- a/Frontend/src/pages/MaterialsPage.vue
+++ b/Frontend/src/pages/MaterialsPage.vue
@@ -351,19 +351,7 @@ const fileInput = ref<HTMLInputElement | null>(null);
 const imageUrlValid = ref(true);
 const validatingImage = ref(false);
 
-// Add a computed property for status color mapping
-const getStatusColor = (status: string) => {
-  switch (status) {
-    case 'In Stock':
-      return 'positive';
-    case 'Low Stock':
-      return 'warning';
-    case 'Out of Stock':
-      return 'negative';
-    default:
-      return 'grey';
-  }
-};
+// Status colors are now directly applied in the template
 
 // Update onMounted hook
 onMounted(async () => {
@@ -487,7 +475,7 @@ function addMaterialToCart() {
       <div class="q-mt-sm">
         <!--MATERIALS TABLE-->
         <q-table
-          class="my-sticky-column-table"
+          class="my-sticky-column-table custom-table-text"
           flat
           bordered
           title="Materials"
@@ -539,9 +527,7 @@ function addMaterialToCart() {
               <q-td key="supplier" :props="props">{{ props.row.supplier }}</q-td>
               <q-td key="quantity" :props="props">{{ props.row.quantity }}</q-td>
               <q-td key="status" :props="props">
-                <q-chip dense :color="getStatusColor(props.row.status)" text-color="white" class="q-px-sm">
-                  {{ props.row.status }}
-                </q-chip>
+                <q-badge :color="props.row.status === 'In Stock' ? 'green' : (props.row.status === 'Out of Stock' ? 'red' : 'orange')" :label="props.row.status" />
               </q-td>
               <q-td key="actions" :props="props" @click.stop>
                 <q-btn flat round dense color="grey" icon="more_vert" class="action-button">
diff --git a/Frontend/src/services/alertsService.ts b/Frontend/src/services/alertsService.ts
new file mode 100644
index 0000000..2027727
--- /dev/null
+++ b/Frontend/src/services/alertsService.ts
@@ -0,0 +1,126 @@
+import { accessoriesApi } from './accessoriesApi';
+import { api } from 'boot/axios';
+import type { MaterialRow } from 'src/types/materials';
+import { useAuthStore } from 'src/stores/auth';
+import { useCabsStore } from 'src/stores/cabs';
+
+/**
+ * Category types for inventory items
+ */
+export type AlertCategory = 'Cabs' | 'Accessories' | 'Materials';
+
+/**
+ * Status types for inventory alerts
+ */
+export type AlertStatus = 'Low Stock' | 'Out of Stock';
+
+/**
+ * Interface for inventory alert data
+ */
+export interface InventoryAlert {
+  category: AlertCategory;
+  status: AlertStatus;
+  count: number;
+}
+
+/**
+ * Service for handling system alerts related to inventory
+ */
+export const alertsService = {
+  /**
+   * Fetches all inventory items with low stock or out of stock status
+   * @returns Promise with array of inventory alerts
+   */
+  getInventoryAlerts: async (): Promise<InventoryAlert[]> => {
+    try {
+      const authStore = useAuthStore();
+      const cabsStore = useCabsStore();
+
+      // Initialize cabs if needed
+      if (cabsStore.cabRows.length === 0) {
+        await cabsStore.initializeCabs();
+      }
+
+      // Fetch data from all three inventory types
+      const [accessories, materials] = await Promise.all([
+        accessoriesApi.getAllAccessories(),
+        api.get<MaterialRow[]>('/api/materials', {
+          headers: {
+            Authorization: `Bearer ${authStore.token}`
+          }
+        }).then(response => response.data)
+      ]);
+
+      // Use the cabs from the store
+      const cabs = cabsStore.cabRows;
+
+      // Process alerts for each category
+      const alerts: InventoryAlert[] = [];
+
+      // Process cabs alerts
+      const lowStockCabs = cabs.filter(cab => cab.status === 'Low Stock');
+      const outOfStockCabs = cabs.filter(cab => cab.status === 'Out of Stock');
+
+      if (lowStockCabs.length > 0) {
+        alerts.push({
+          category: 'Cabs',
+          status: 'Low Stock',
+          count: lowStockCabs.length
+        });
+      }
+
+      if (outOfStockCabs.length > 0) {
+        alerts.push({
+          category: 'Cabs',
+          status: 'Out of Stock',
+          count: outOfStockCabs.length
+        });
+      }
+
+      // Process accessories alerts
+      const lowStockAccessories = accessories.filter(accessory => accessory.status === 'Low Stock');
+      const outOfStockAccessories = accessories.filter(accessory => accessory.status === 'Out of Stock');
+
+      if (lowStockAccessories.length > 0) {
+        alerts.push({
+          category: 'Accessories',
+          status: 'Low Stock',
+          count: lowStockAccessories.length
+        });
+      }
+
+      if (outOfStockAccessories.length > 0) {
+        alerts.push({
+          category: 'Accessories',
+          status: 'Out of Stock',
+          count: outOfStockAccessories.length
+        });
+      }
+
+      // Process materials alerts
+      const lowStockMaterials = materials.filter(material => material.status === 'Low Stock');
+      const outOfStockMaterials = materials.filter(material => material.status === 'Out of Stock');
+
+      if (lowStockMaterials.length > 0) {
+        alerts.push({
+          category: 'Materials',
+          status: 'Low Stock',
+          count: lowStockMaterials.length
+        });
+      }
+
+      if (outOfStockMaterials.length > 0) {
+        alerts.push({
+          category: 'Materials',
+          status: 'Out of Stock',
+          count: outOfStockMaterials.length
+        });
+      }
+
+      return alerts;
+    } catch (error) {
+      console.error('Error fetching inventory alerts:', error);
+      return [];
+    }
+  }
+};


Reviews:
user: login: github-actions[bot]
body: Gemini AI Code Reviewer Comments
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-09T19:47:45Z
user: login: StanleighMorales
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-09T20:07:01Z
user: login: StanleighMorales
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-09T21:13:34Z

Review comments:
diff_hunk: @@ -0,0 +1,131 @@
+<template>
+  <q-card bordered>
+    <q-card-section>
+      <div class="text-h6">Inventory Alerts</div>
+    </q-card-section>
+    <q-separator />
+    <q-card-section class="q-pa-none">
+      <q-list>
+        <q-item v-for="alert in alerts" :key="`${alert.category}-${alert.status}`" class="q-pa-md">
+          <q-item-section avatar>
+            <q-icon :name="getAlertIcon(alert.category)" :color="getAlertColor(alert.status)" />
+          </q-item-section>
+          <q-item-section>
+            <q-item-label>{{ alert.category }} - {{ alert.status }}</q-item-label>
+            <q-item-label caption>{{ alert.count }} {{ alert.category.toLowerCase() }} {{ alert.status.toLowerCase() }}</q-item-label>
+          </q-item-section>
+          <q-item-section side>
+            <q-btn 
+              flat 
+              round 
+              dense 
+              icon="visibility" 
+              @click="handleAlertAction(alert)"
+              :color="getAlertColor(alert.status)"
+            />
+          </q-item-section>
+        </q-item>
+        <q-item v-if="alerts.length === 0" class="text-center q-pa-lg text-grey">
+          <q-item-section>No inventory alerts</q-item-section>
+        </q-item>
+      </q-list>
+    </q-card-section>
+  </q-card>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { useQuasar } from 'quasar';
+import { alertsService, type InventoryAlert } from 'src/services/alertsService';
+
+const $q = useQuasar();
+const router = useRouter();
+const alerts = ref<InventoryAlert[]>([]);
+const isLoading = ref(false);
+
+// Fetch alerts on component mount
+onMounted(async () => {
+  await fetchAlerts();
path: Frontend/src/components/dashboard/DynamicAlertsPanel.vue
user: login: github-actions[bot]
body: Consider using a more descriptive name than `$q`. While it's conventional for Quasar, it reduces readability.  A name like `quasar` or `quasarUtils` would be more explicit.
created_at: 2025-05-09T19:47:42Z
author_association: NONE
diff_hunk: @@ -0,0 +1,131 @@
+<template>
+  <q-card bordered>
+    <q-card-section>
+      <div class="text-h6">Inventory Alerts</div>
+    </q-card-section>
+    <q-separator />
+    <q-card-section class="q-pa-none">
+      <q-list>
+        <q-item v-for="alert in alerts" :key="`${alert.category}-${alert.status}`" class="q-pa-md">
+          <q-item-section avatar>
+            <q-icon :name="getAlertIcon(alert.category)" :color="getAlertColor(alert.status)" />
+          </q-item-section>
+          <q-item-section>
+            <q-item-label>{{ alert.category }} - {{ alert.status }}</q-item-label>
+            <q-item-label caption>{{ alert.count }} {{ alert.category.toLowerCase() }} {{ alert.status.toLowerCase() }}</q-item-label>
+          </q-item-section>
+          <q-item-section side>
+            <q-btn 
+              flat 
+              round 
+              dense 
+              icon="visibility" 
+              @click="handleAlertAction(alert)"
+              :color="getAlertColor(alert.status)"
+            />
+          </q-item-section>
+        </q-item>
+        <q-item v-if="alerts.length === 0" class="text-center q-pa-lg text-grey">
+          <q-item-section>No inventory alerts</q-item-section>
+        </q-item>
+      </q-list>
+    </q-card-section>
+  </q-card>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { useQuasar } from 'quasar';
+import { alertsService, type InventoryAlert } from 'src/services/alertsService';
+
+const $q = useQuasar();
+const router = useRouter();
+const alerts = ref<InventoryAlert[]>([]);
+const isLoading = ref(false);
+
+// Fetch alerts on component mount
+onMounted(async () => {
+  await fetchAlerts();
+});
+
+/**
+ * Fetches inventory alerts from the service
+ */
+async function fetchAlerts() {
+  try {
+    isLoading.value = true;
+    alerts.value = await alertsService.getInventoryAlerts();
+  } catch (error) {
+    console.error('Error fetching alerts:', error);
+    $q.notify({
+      type: 'negative',
+      message: 'Failed to load inventory alerts',
+      position: 'top',
+      timeout: 3000
+    });
+  } finally {
+    isLoading.value = false;
+  }
+}
+
+/**
+ * Gets the appropriate icon for each category
+ */
+function getAlertIcon(category: string): string {
+  switch (category) {
+    case 'Cabs':
+      return 'directions_car';
+    case 'Accessories':
+      return 'settings_input_component';
+    case 'Materials':
+      return 'category';
+    default:
+      return 'warning';
+  }
+}
+
+/**
+ * Gets the appropriate color for each alert status
+ */
+function getAlertColor(status: string): string {
+  switch (status) {
+    case 'Low Stock':
+      return 'warning';
+    case 'Out of Stock':
+      return 'negative';
+    default:
+      return 'grey';
path: Frontend/src/components/dashboard/DynamicAlertsPanel.vue
user: login: github-actions[bot]
body: The `getAlertIcon` function can be improved by using a more robust data structure like a `Map` to store category-icon mappings. This would make the code more readable and maintainable, especially if the number of categories grows.

```typescript
const alertIcons = new Map<string, string>([
  ['Cabs', 'directions_car'],
  ['Accessories', 'settings_input_component'],
  ['Materials', 'category'],
]);

function getAlertIcon(category: string): string {
  return alertIcons.get(category) || 'warning';
}
```
created_at: 2025-05-09T19:47:42Z
author_association: NONE
diff_hunk: @@ -0,0 +1,131 @@
+<template>
+  <q-card bordered>
+    <q-card-section>
+      <div class="text-h6">Inventory Alerts</div>
+    </q-card-section>
+    <q-separator />
+    <q-card-section class="q-pa-none">
+      <q-list>
+        <q-item v-for="alert in alerts" :key="`${alert.category}-${alert.status}`" class="q-pa-md">
+          <q-item-section avatar>
+            <q-icon :name="getAlertIcon(alert.category)" :color="getAlertColor(alert.status)" />
+          </q-item-section>
+          <q-item-section>
+            <q-item-label>{{ alert.category }} - {{ alert.status }}</q-item-label>
+            <q-item-label caption>{{ alert.count }} {{ alert.category.toLowerCase() }} {{ alert.status.toLowerCase() }}</q-item-label>
+          </q-item-section>
+          <q-item-section side>
+            <q-btn 
+              flat 
+              round 
+              dense 
+              icon="visibility" 
+              @click="handleAlertAction(alert)"
+              :color="getAlertColor(alert.status)"
+            />
+          </q-item-section>
+        </q-item>
+        <q-item v-if="alerts.length === 0" class="text-center q-pa-lg text-grey">
+          <q-item-section>No inventory alerts</q-item-section>
+        </q-item>
+      </q-list>
+    </q-card-section>
+  </q-card>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { useQuasar } from 'quasar';
+import { alertsService, type InventoryAlert } from 'src/services/alertsService';
+
+const $q = useQuasar();
+const router = useRouter();
+const alerts = ref<InventoryAlert[]>([]);
+const isLoading = ref(false);
+
+// Fetch alerts on component mount
+onMounted(async () => {
+  await fetchAlerts();
+});
+
+/**
+ * Fetches inventory alerts from the service
+ */
+async function fetchAlerts() {
+  try {
+    isLoading.value = true;
+    alerts.value = await alertsService.getInventoryAlerts();
+  } catch (error) {
+    console.error('Error fetching alerts:', error);
+    $q.notify({
+      type: 'negative',
+      message: 'Failed to load inventory alerts',
+      position: 'top',
+      timeout: 3000
+    });
+  } finally {
+    isLoading.value = false;
+  }
+}
+
+/**
+ * Gets the appropriate icon for each category
+ */
+function getAlertIcon(category: string): string {
+  switch (category) {
+    case 'Cabs':
+      return 'directions_car';
+    case 'Accessories':
+      return 'settings_input_component';
+    case 'Materials':
+      return 'category';
+    default:
+      return 'warning';
+  }
+}
+
+/**
+ * Gets the appropriate color for each alert status
+ */
+function getAlertColor(status: string): string {
+  switch (status) {
+    case 'Low Stock':
+      return 'warning';
+    case 'Out of Stock':
+      return 'negative';
+    default:
+      return 'grey';
+  }
+}
+
+/**
+ * Handles the action when an alert is clicked
+ */
+async function handleAlertAction(alert: InventoryAlert) {
+  try {
+    // Navigate to the appropriate inventory page with filter
+    const path = `/inventory/${alert.category.toLowerCase()}`;
+    await router.push({
+      path,
+      query: { status: alert.status.replace(' ', '-').toLowerCase() }
+    });
+    
+    // Show notification
+    $q.notify({
+      type: 'info',
path: Frontend/src/components/dashboard/DynamicAlertsPanel.vue
user: login: github-actions[bot]
body: Similar to `getAlertIcon`, the `getAlertColor` function could benefit from using a `Map` for status-color mappings to improve readability and maintainability.

```typescript
const alertColors = new Map<string, string>([
  ['Low Stock', 'warning'],
  ['Out of Stock', 'negative'],
]);

function getAlertColor(status: string): string {
  return alertColors.get(status) || 'grey';
}
```
created_at: 2025-05-09T19:47:42Z
author_association: NONE
diff_hunk: @@ -0,0 +1,131 @@
+<template>
+  <q-card bordered>
+    <q-card-section>
+      <div class="text-h6">Inventory Alerts</div>
+    </q-card-section>
+    <q-separator />
+    <q-card-section class="q-pa-none">
+      <q-list>
+        <q-item v-for="alert in alerts" :key="`${alert.category}-${alert.status}`" class="q-pa-md">
+          <q-item-section avatar>
+            <q-icon :name="getAlertIcon(alert.category)" :color="getAlertColor(alert.status)" />
+          </q-item-section>
+          <q-item-section>
+            <q-item-label>{{ alert.category }} - {{ alert.status }}</q-item-label>
+            <q-item-label caption>{{ alert.count }} {{ alert.category.toLowerCase() }} {{ alert.status.toLowerCase() }}</q-item-label>
+          </q-item-section>
+          <q-item-section side>
+            <q-btn 
+              flat 
+              round 
+              dense 
+              icon="visibility" 
+              @click="handleAlertAction(alert)"
+              :color="getAlertColor(alert.status)"
+            />
+          </q-item-section>
+        </q-item>
+        <q-item v-if="alerts.length === 0" class="text-center q-pa-lg text-grey">
+          <q-item-section>No inventory alerts</q-item-section>
+        </q-item>
+      </q-list>
+    </q-card-section>
+  </q-card>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { useQuasar } from 'quasar';
+import { alertsService, type InventoryAlert } from 'src/services/alertsService';
+
+const $q = useQuasar();
+const router = useRouter();
+const alerts = ref<InventoryAlert[]>([]);
+const isLoading = ref(false);
+
+// Fetch alerts on component mount
+onMounted(async () => {
+  await fetchAlerts();
+});
+
+/**
+ * Fetches inventory alerts from the service
+ */
+async function fetchAlerts() {
+  try {
+    isLoading.value = true;
+    alerts.value = await alertsService.getInventoryAlerts();
+  } catch (error) {
+    console.error('Error fetching alerts:', error);
+    $q.notify({
+      type: 'negative',
+      message: 'Failed to load inventory alerts',
+      position: 'top',
+      timeout: 3000
+    });
+  } finally {
+    isLoading.value = false;
+  }
+}
+
+/**
+ * Gets the appropriate icon for each category
+ */
+function getAlertIcon(category: string): string {
+  switch (category) {
+    case 'Cabs':
+      return 'directions_car';
+    case 'Accessories':
+      return 'settings_input_component';
+    case 'Materials':
+      return 'category';
+    default:
+      return 'warning';
+  }
+}
+
+/**
+ * Gets the appropriate color for each alert status
+ */
+function getAlertColor(status: string): string {
+  switch (status) {
+    case 'Low Stock':
+      return 'warning';
+    case 'Out of Stock':
+      return 'negative';
+    default:
+      return 'grey';
+  }
+}
+
+/**
+ * Handles the action when an alert is clicked
+ */
+async function handleAlertAction(alert: InventoryAlert) {
+  try {
+    // Navigate to the appropriate inventory page with filter
+    const path = `/inventory/${alert.category.toLowerCase()}`;
+    await router.push({
+      path,
+      query: { status: alert.status.replace(' ', '-').toLowerCase() }
+    });
+    
+    // Show notification
+    $q.notify({
+      type: 'info',
+      message: `Viewing ${alert.status.toLowerCase()} ${alert.category.toLowerCase()}`,
+      position: 'top',
+      timeout: 2000
+    });
+  } catch (error) {
+    console.error('Error handling alert action:', error);
+    $q.notify({
+      type: 'negative',
+      message: 'Failed to process alert action',
+      position: 'top',
+      timeout: 3000
+    });
+  }
+}
path: Frontend/src/components/dashboard/DynamicAlertsPanel.vue
user: login: github-actions[bot]
body: Consider using template literals for constructing the path to improve readability.

```typescript
const path = `/inventory/${alert.category.toLowerCase()}`;
```
created_at: 2025-05-09T19:47:42Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider using a more descriptive class name than `alert-item`.  Something like `notification-item` might be more appropriate, especially if 'alerts' are used for other things in the system.
created_at: 2025-05-09T19:47:42Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: It might be better UX to have a single 'Mark as Read' button for all alerts, or a 'Clear All' button, instead of one for each alert, depending on the expected usage and number of alerts.
created_at: 2025-05-09T19:47:42Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider extracting the `No unread alerts` and `No read alerts` messages into a shared component or variable to avoid duplication and improve maintainability. This also allows for easier internationalization.
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
+                                icon="done"
+                                size="sm"
+                                :color="getAlertColor(alert.severity)"
+                                @click.stop="markAlertAsRead(alert.id)"
+                              >
+                                <q-tooltip>Mark as read</q-tooltip>
+                              </q-btn>
+                            </q-item-section>
+                          </q-item>
+                        </template>
+
+                        <q-item v-else>
+                          <q-item-section class="text-center text-grey q-py-sm">
+                            <div>No unread alerts</div>
+                          </q-item-section>
+                        </q-item>
+                      </div>
+
+                      <q-separator v-if="unreadAlerts.length > 0 && readAlerts.length > 0" class="q-my-sm" />
+
+                      <!-- Read Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-grey q-px-md q-pt-md">
+                          Read Alerts
+                        </q-item-label>
+
+                        <template v-if="readAlerts.length > 0">
+                          <q-item
+                            v-for="alert in readAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item alert-read"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider adding a limit to the number of read alerts displayed initially, with a 'Load More' button, to improve performance if there are a large number of read alerts.  The current implementation loads all read alerts within the scroll area, which might be slow if there are thousands of them.
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: The `index` in `inventory-${alert.category.toLowerCase()}-${alert.status.toLowerCase()}-${index}` might lead to duplicate IDs if the same alert is fetched multiple times, potentially causing issues with the UI. Consider a more robust method for generating unique IDs, such as combining the index with a timestamp or using a hash of the alert content.
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider extracting the logic for determining the icon based on the category into a separate function for better readability and maintainability.
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
+                                icon="done"
+                                size="sm"
+                                :color="getAlertColor(alert.severity)"
+                                @click.stop="markAlertAsRead(alert.id)"
+                              >
+                                <q-tooltip>Mark as read</q-tooltip>
+                              </q-btn>
+                            </q-item-section>
+                          </q-item>
+                        </template>
+
+                        <q-item v-else>
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: The `shouldShowInventoryCheckReminder` function is not defined in the provided code. Ensure this function is defined elsewhere and correctly determines whether to show the inventory check reminder. Also, the `getDaysUntilEndOfMonth` function is not defined in the provided code. Ensure this function is defined elsewhere and correctly calculates the number of days until the end of the month.
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
+                                icon="done"
+                                size="sm"
+                                :color="getAlertColor(alert.severity)"
+                                @click.stop="markAlertAsRead(alert.id)"
+                              >
+                                <q-tooltip>Mark as read</q-tooltip>
+                              </q-btn>
+                            </q-item-section>
+                          </q-item>
+                        </template>
+
+                        <q-item v-else>
+                          <q-item-section class="text-center text-grey q-py-sm">
+                            <div>No unread alerts</div>
+                          </q-item-section>
+                        </q-item>
+                      </div>
+
+                      <q-separator v-if="unreadAlerts.length > 0 && readAlerts.length > 0" class="q-my-sm" />
+
+                      <!-- Read Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-grey q-px-md q-pt-md">
+                          Read Alerts
+                        </q-item-label>
+
+                        <template v-if="readAlerts.length > 0">
+                          <q-item
+                            v-for="alert in readAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item alert-read"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-read"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-read">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: The error handling only adds a generic error alert. Consider providing more specific error messages based on the type of error encountered to aid in debugging.
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -23,9 +23,13 @@
           </q-btn>
           <div class="notification-wrapper">
             <q-btn dense round flat icon="notifications" class="text-soft-light">
-              <q-badge color="red" floating>
-                {{ systemAlerts.length }}
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: It's great that you're sorting the alerts by timestamp. However, consider adding a comment explaining why you're using `getTime()` for the comparison.  This makes it clearer that you're comparing the numerical values of the dates.

```javascript
// Sort by timestamp, newest first (getTime() for numerical comparison)
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
```
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -23,9 +23,13 @@
           </q-btn>
           <div class="notification-wrapper">
             <q-btn dense round flat icon="notifications" class="text-soft-light">
-              <q-badge color="red" floating>
-                {{ systemAlerts.length }}
-              </q-badge>
+            <q-badge
+              v-if="unreadAlertsCount > 0"
+              color="red"
+              floating
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Duplicated sorting logic from the unread alerts calculation. Consider creating a reusable function for sorting alerts by timestamp to avoid code duplication and improve maintainability.
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider adding error handling to `fetchInventoryAlerts`. If the fetch fails, it could leave the component in an inconsistent state.  Wrap the `fetchInventoryAlerts` function call in a `try...catch` block and log any errors.

```javascript
try {
  await fetchInventoryAlerts();
} catch (error) {
  console.error('Failed to fetch inventory alerts:', error);
  // Optionally, display an error message to the user
}
```
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: It might be beneficial to check if `alertsInterval` is already set before calling `setInterval`. Although unlikely in this specific scenario, it adds a layer of safety, especially if the component's lifecycle changes in the future.
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Good practice to clear the interval on unmount. However, it's crucial to ensure `alertsInterval` is defined before attempting to clear it. If `setInterval` fails to assign a value to `alertsInterval` (though unlikely), `clearInterval(undefined)` could cause issues. Add a check before clearing.

```javascript
return () => {
  if (alertsInterval) {
    clearInterval(alertsInterval);
  }
};
```
created_at: 2025-05-09T19:47:43Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider using `Date.UTC()` to avoid potential issues with time zones when constructing the `nextMonth` date. This ensures consistency across different environments.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Storing the month as `getMonth()` can be problematic as it is zero-indexed. This can lead to issues when comparing months.  Consider storing `getMonth() + 1` or using a more robust date formatting library (like `date-fns`) to ensure consistent formatting and comparison of dates.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
+                                icon="done"
+                                size="sm"
+                                :color="getAlertColor(alert.severity)"
+                                @click.stop="markAlertAsRead(alert.id)"
+                              >
+                                <q-tooltip>Mark as read</q-tooltip>
+                              </q-btn>
+                            </q-item-section>
+                          </q-item>
+                        </template>
+
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Currently, if `alert` is undefined within the `if (alert)` block, an error might occur if you try to access properties of it. To prevent this, add a null check. This is unlikely, but good to do defensively.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
+                                icon="done"
+                                size="sm"
+                                :color="getAlertColor(alert.severity)"
+                                @click.stop="markAlertAsRead(alert.id)"
+                              >
+                                <q-tooltip>Mark as read</q-tooltip>
+                              </q-btn>
+                            </q-item-section>
+                          </q-item>
+                        </template>
+
+                        <q-item v-else>
+                          <q-item-section class="text-center text-grey q-py-sm">
+                            <div>No unread alerts</div>
+                          </q-item-section>
+                        </q-item>
+                      </div>
+
+                      <q-separator v-if="unreadAlerts.length > 0 && readAlerts.length > 0" class="q-my-sm" />
+
+                      <!-- Read Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-grey q-px-md q-pt-md">
+                          Read Alerts
+                        </q-item-label>
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Concatenating strings in a loop, especially for larger numbers of alerts, can be inefficient.  Consider using an array to build the HTML fragments and then joining them at the end.

```javascript
const alertsHtml = allReadAlerts.value.map(alert => {
  return `<div class="q-mb-md">
      <div class="text-weight-medium">${alert.title}</div>
      <div class="text-caption">${alert.message}</div>
      <div class="text-caption text-grey">${formatAlertTime(alert.timestamp)}</div>
    </div>`;
});

const alertsList = alertsHtml.join('');
```
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -23,9 +23,13 @@
           </q-btn>
           <div class="notification-wrapper">
             <q-btn dense round flat icon="notifications" class="text-soft-light">
-              <q-badge color="red" floating>
-                {{ systemAlerts.length }}
-              </q-badge>
+            <q-badge
+              v-if="unreadAlertsCount > 0"
+              color="red"
+              floating
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider using a more robust and scalable method for identifying and handling different alert types instead of relying on string prefixes. A dictionary or a dedicated alert handler class could improve maintainability and readability.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider moving the `$q.notify` call outside the `if` block to ensure it's always displayed when the route is pushed.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: The logic for calculating the last day of the month can be simplified.  Consider using `new Date(today.getFullYear(), today.getMonth() + 1, 0)` which directly gets the last day of the current month. This avoids the subtraction and might be clearer.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider using `Intl.DateTimeFormat` instead of `toLocaleDateString` for better internationalization support and more control over the date formatting.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
+                                icon="done"
+                                size="sm"
+                                :color="getAlertColor(alert.severity)"
+                                @click.stop="markAlertAsRead(alert.id)"
+                              >
+                                <q-tooltip>Mark as read</q-tooltip>
+                              </q-btn>
+                            </q-item-section>
+                          </q-item>
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider using a more efficient way to get the current month name, like `new Date().toLocaleString('default', { month: 'long' })`, instead of manually creating an array.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
+                                icon="done"
+                                size="sm"
+                                :color="getAlertColor(alert.severity)"
+                                @click.stop="markAlertAsRead(alert.id)"
+                              >
+                                <q-tooltip>Mark as read</q-tooltip>
+                              </q-btn>
+                            </q-item-section>
+                          </q-item>
+                        </template>
+
+                        <q-item v-else>
+                          <q-item-section class="text-center text-grey q-py-sm">
+                            <div>No unread alerts</div>
+                          </q-item-section>
+                        </q-item>
+                      </div>
+
+                      <q-separator v-if="unreadAlerts.length > 0 && readAlerts.length > 0" class="q-my-sm" />
+
+                      <!-- Read Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-grey q-px-md q-pt-md">
+                          Read Alerts
+                        </q-item-label>
+
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: The code comments `Mark the inventory check as scheduled for this month` and `Remove the alert from the list` are explaining what the code does, not why it's doing it. This type of comment is not useful and should be removed.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
+                                flat
+                                round
+                                dense
+                                icon="done"
+                                size="sm"
+                                :color="getAlertColor(alert.severity)"
+                                @click.stop="markAlertAsRead(alert.id)"
+                              >
+                                <q-tooltip>Mark as read</q-tooltip>
+                              </q-btn>
+                            </q-item-section>
+                          </q-item>
+                        </template>
+
+                        <q-item v-else>
+                          <q-item-section class="text-center text-grey q-py-sm">
+                            <div>No unread alerts</div>
+                          </q-item-section>
+                        </q-item>
+                      </div>
+
+                      <q-separator v-if="unreadAlerts.length > 0 && readAlerts.length > 0" class="q-my-sm" />
+
+                      <!-- Read Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-grey q-px-md q-pt-md">
+                          Read Alerts
+                        </q-item-label>
+
+                        <template v-if="readAlerts.length > 0">
+                          <q-item
+                            v-for="alert in readAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item alert-read"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-read"
+                              />
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: The code `await fetchInventoryAlerts();` doesn't really handle error alerts, it just refreshes the alerts. Consider whether the `error` alert id should exist, since the alerts will refresh regardless.
created_at: 2025-05-09T19:47:44Z
author_association: NONE
diff_hunk: @@ -23,9 +23,13 @@
           </q-btn>
           <div class="notification-wrapper">
             <q-btn dense round flat icon="notifications" class="text-soft-light">
-              <q-badge color="red" floating>
-                {{ systemAlerts.length }}
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider using CSS variables for the transition duration and easing function to ensure consistency across the application. For example: `--transition-duration: 0.3s;` and `--transition-easing: ease;`. Then use these variables in the transition property: `transition: all var(--transition-duration) var(--transition-easing);`
created_at: 2025-05-09T19:47:45Z
author_association: NONE
diff_hunk: @@ -23,9 +23,13 @@
           </q-btn>
           <div class="notification-wrapper">
             <q-btn dense round flat icon="notifications" class="text-soft-light">
-              <q-badge color="red" floating>
-                {{ systemAlerts.length }}
-              </q-badge>
+            <q-badge
+              v-if="unreadAlertsCount > 0"
+              color="red"
+              floating
+            >
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: It is good to use `rgba` to set the background color, this allows you to control the opacity of the background without affecting the text or other elements within the alert. But consider adding a CSS variable for the opacity value to allow easy modification and consistency across the app. For example: `--alert-unread-opacity: 0.05;`. Then use it like this: `background-color: rgba(var(--q-primary-rgb), var(--alert-unread-opacity));`
created_at: 2025-05-09T19:47:45Z
author_association: NONE
diff_hunk: @@ -37,22 +41,119 @@
                   <q-separator />
                   <template v-if="systemAlerts.length">
                     <q-scroll-area style="height: 300px;">
-                      <q-item v-for="alert in systemAlerts" :key="alert.id" clickable v-close-popup @click="handleAlertAction(alert.id)" :class="{ 'bg-grey-2': alert.read }">
-                        <q-item-section avatar>
-                          <q-icon :name="alert.icon" :color="getAlertColor(alert.severity)" />
-                        </q-item-section>
-                        <q-item-section>
-                          <q-item-label class="text-weight-medium">{{ alert.title }}</q-item-label>
-                          <q-item-label caption>{{ alert.message }}</q-item-label>
-                          <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
-                        </q-item-section>
-                      </q-item>
+                      <!-- Unread Alerts Section -->
+                      <div>
+                        <q-item-label header class="text-primary q-px-md">
+                          Unread Alerts
+                        </q-item-label>
+
+                        <template v-if="unreadAlerts.length > 0">
+                          <q-item
+                            v-for="alert in unreadAlerts"
+                            :key="alert.id"
+                            clickable
+                            v-close-popup
+                            @click="handleAlertAction(alert.id)"
+                            class="alert-item"
+                            :class="`alert-unread alert-unread-${getAlertColor(alert.severity)}`"
+                          >
+                            <q-item-section avatar>
+                              <q-icon
+                                :name="alert.icon"
+                                :color="getAlertColor(alert.severity)"
+                                class="alert-icon-unread"
+                              />
+                            </q-item-section>
+                            <q-item-section>
+                              <q-item-label class="alert-title-unread">
+                                {{ alert.title }}
+                              </q-item-label>
+                              <q-item-label caption>{{ alert.message }}</q-item-label>
+                              <q-item-label caption class="text-grey-7">{{ formatAlertTime(alert.timestamp) }}</q-item-label>
+                            </q-item-section>
+                            <q-item-section side>
+                              <q-btn
path: Frontend/src/layouts/MainLayout.vue
user: login: github-actions[bot]
body: Consider defining the font weights as CSS variables (e.g., `--font-weight-semibold: 600;` and `--font-weight-regular: 400;`) for better maintainability and consistency. Then you can use them like this: `font-weight: var(--font-weight-semibold);`
created_at: 2025-05-09T19:47:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,126 @@
+import { accessoriesApi } from './accessoriesApi';
+import { api } from 'boot/axios';
+import type { MaterialRow } from 'src/types/materials';
+import { useAuthStore } from 'src/stores/auth';
+import { useCabsStore } from 'src/stores/cabs';
+
+/**
+ * Category types for inventory items
+ */
+export type AlertCategory = 'Cabs' | 'Accessories' | 'Materials';
+
+/**
+ * Status types for inventory alerts
+ */
+export type AlertStatus = 'Low Stock' | 'Out of Stock';
+
+/**
+ * Interface for inventory alert data
+ */
+export interface InventoryAlert {
+  category: AlertCategory;
+  status: AlertStatus;
+  count: number;
+}
+
+/**
+ * Service for handling system alerts related to inventory
+ */
+export const alertsService = {
+  /**
+   * Fetches all inventory items with low stock or out of stock status
+   * @returns Promise with array of inventory alerts
+   */
+  getInventoryAlerts: async (): Promise<InventoryAlert[]> => {
+    try {
+      const authStore = useAuthStore();
+      const cabsStore = useCabsStore();
+
+      // Initialize cabs if needed
+      if (cabsStore.cabRows.length === 0) {
path: Frontend/src/services/alertsService.ts
user: login: github-actions[bot]
body: Consider adding a comment explaining why the cabs store needs to be initialized here, as it might not be immediately obvious to someone reading the code.
created_at: 2025-05-09T19:47:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,126 @@
+import { accessoriesApi } from './accessoriesApi';
+import { api } from 'boot/axios';
+import type { MaterialRow } from 'src/types/materials';
+import { useAuthStore } from 'src/stores/auth';
+import { useCabsStore } from 'src/stores/cabs';
+
+/**
+ * Category types for inventory items
+ */
+export type AlertCategory = 'Cabs' | 'Accessories' | 'Materials';
+
+/**
+ * Status types for inventory alerts
+ */
+export type AlertStatus = 'Low Stock' | 'Out of Stock';
+
+/**
+ * Interface for inventory alert data
+ */
+export interface InventoryAlert {
+  category: AlertCategory;
+  status: AlertStatus;
+  count: number;
+}
+
+/**
+ * Service for handling system alerts related to inventory
+ */
+export const alertsService = {
+  /**
+   * Fetches all inventory items with low stock or out of stock status
+   * @returns Promise with array of inventory alerts
+   */
+  getInventoryAlerts: async (): Promise<InventoryAlert[]> => {
+    try {
+      const authStore = useAuthStore();
+      const cabsStore = useCabsStore();
+
+      // Initialize cabs if needed
+      if (cabsStore.cabRows.length === 0) {
+        await cabsStore.initializeCabs();
+      }
+
+      // Fetch data from all three inventory types
+      const [accessories, materials] = await Promise.all([
+        accessoriesApi.getAllAccessories(),
+        api.get<MaterialRow[]>('/api/materials', {
+          headers: {
+            Authorization: `Bearer ${authStore.token}`
+          }
path: Frontend/src/services/alertsService.ts
user: login: github-actions[bot]
body: It might be beneficial to extract the logic for processing alerts for each category into a separate, reusable function. This would reduce code duplication and improve readability. For instance, a function `processAlerts(category: AlertCategory, items: any[], status: AlertStatus)` could be created and used for cabs, accessories, and materials.
created_at: 2025-05-09T19:47:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,126 @@
+import { accessoriesApi } from './accessoriesApi';
+import { api } from 'boot/axios';
+import type { MaterialRow } from 'src/types/materials';
+import { useAuthStore } from 'src/stores/auth';
+import { useCabsStore } from 'src/stores/cabs';
+
+/**
+ * Category types for inventory items
+ */
+export type AlertCategory = 'Cabs' | 'Accessories' | 'Materials';
+
+/**
+ * Status types for inventory alerts
+ */
+export type AlertStatus = 'Low Stock' | 'Out of Stock';
+
+/**
+ * Interface for inventory alert data
+ */
+export interface InventoryAlert {
+  category: AlertCategory;
+  status: AlertStatus;
+  count: number;
+}
+
+/**
+ * Service for handling system alerts related to inventory
+ */
+export const alertsService = {
+  /**
+   * Fetches all inventory items with low stock or out of stock status
+   * @returns Promise with array of inventory alerts
+   */
+  getInventoryAlerts: async (): Promise<InventoryAlert[]> => {
+    try {
+      const authStore = useAuthStore();
+      const cabsStore = useCabsStore();
+
+      // Initialize cabs if needed
+      if (cabsStore.cabRows.length === 0) {
+        await cabsStore.initializeCabs();
+      }
+
+      // Fetch data from all three inventory types
+      const [accessories, materials] = await Promise.all([
+        accessoriesApi.getAllAccessories(),
+        api.get<MaterialRow[]>('/api/materials', {
+          headers: {
+            Authorization: `Bearer ${authStore.token}`
+          }
path: Frontend/src/services/alertsService.ts
user: login: github-actions[bot]
body: The `any` type should be replaced with a more specific type. Without knowing the structure of the `items` array, it's difficult to propose a concrete type, but it should represent the structure of `cabs`, `accessories`, and `materials`.
created_at: 2025-05-09T19:47:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,126 @@
+import { accessoriesApi } from './accessoriesApi';
+import { api } from 'boot/axios';
+import type { MaterialRow } from 'src/types/materials';
+import { useAuthStore } from 'src/stores/auth';
+import { useCabsStore } from 'src/stores/cabs';
+
+/**
+ * Category types for inventory items
+ */
+export type AlertCategory = 'Cabs' | 'Accessories' | 'Materials';
+
+/**
+ * Status types for inventory alerts
+ */
+export type AlertStatus = 'Low Stock' | 'Out of Stock';
+
+/**
+ * Interface for inventory alert data
+ */
+export interface InventoryAlert {
+  category: AlertCategory;
+  status: AlertStatus;
+  count: number;
+}
+
+/**
+ * Service for handling system alerts related to inventory
+ */
+export const alertsService = {
+  /**
+   * Fetches all inventory items with low stock or out of stock status
+   * @returns Promise with array of inventory alerts
+   */
+  getInventoryAlerts: async (): Promise<InventoryAlert[]> => {
+    try {
+      const authStore = useAuthStore();
+      const cabsStore = useCabsStore();
+
+      // Initialize cabs if needed
+      if (cabsStore.cabRows.length === 0) {
+        await cabsStore.initializeCabs();
+      }
+
+      // Fetch data from all three inventory types
+      const [accessories, materials] = await Promise.all([
+        accessoriesApi.getAllAccessories(),
+        api.get<MaterialRow[]>('/api/materials', {
+          headers: {
+            Authorization: `Bearer ${authStore.token}`
+          }
path: Frontend/src/services/alertsService.ts
user: login: github-actions[bot]
body: Consider adding a unit test to check the scenario when the API call fails. 
created_at: 2025-05-09T19:47:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,126 @@
+import { accessoriesApi } from './accessoriesApi';
+import { api } from 'boot/axios';
+import type { MaterialRow } from 'src/types/materials';
+import { useAuthStore } from 'src/stores/auth';
+import { useCabsStore } from 'src/stores/cabs';
+
+/**
+ * Category types for inventory items
+ */
+export type AlertCategory = 'Cabs' | 'Accessories' | 'Materials';
+
+/**
+ * Status types for inventory alerts
+ */
+export type AlertStatus = 'Low Stock' | 'Out of Stock';
+
+/**
+ * Interface for inventory alert data
+ */
+export interface InventoryAlert {
+  category: AlertCategory;
+  status: AlertStatus;
+  count: number;
+}
+
+/**
+ * Service for handling system alerts related to inventory
+ */
+export const alertsService = {
+  /**
+   * Fetches all inventory items with low stock or out of stock status
+   * @returns Promise with array of inventory alerts
+   */
+  getInventoryAlerts: async (): Promise<InventoryAlert[]> => {
+    try {
+      const authStore = useAuthStore();
+      const cabsStore = useCabsStore();
+
+      // Initialize cabs if needed
+      if (cabsStore.cabRows.length === 0) {
+        await cabsStore.initializeCabs();
+      }
+
+      // Fetch data from all three inventory types
+      const [accessories, materials] = await Promise.all([
+        accessoriesApi.getAllAccessories(),
+        api.get<MaterialRow[]>('/api/materials', {
+          headers: {
+            Authorization: `Bearer ${authStore.token}`
+          }
+        }).then(response => response.data)
+      ]);
+
+      // Use the cabs from the store
+      const cabs = cabsStore.cabRows;
+
+      // Process alerts for each category
+      const alerts: InventoryAlert[] = [];
+
+      // Process cabs alerts
+      const lowStockCabs = cabs.filter(cab => cab.status === 'Low Stock');
+      const outOfStockCabs = cabs.filter(cab => cab.status === 'Out of Stock');
+
+      if (lowStockCabs.length > 0) {
+        alerts.push({
+          category: 'Cabs',
+          status: 'Low Stock',
+          count: lowStockCabs.length
+        });
+      }
+
+      if (outOfStockCabs.length > 0) {
+        alerts.push({
+          category: 'Cabs',
+          status: 'Out of Stock',
+          count: outOfStockCabs.length
+        });
+      }
+
+      // Process accessories alerts
+      const lowStockAccessories = accessories.filter(accessory => accessory.status === 'Low Stock');
+      const outOfStockAccessories = accessories.filter(accessory => accessory.status === 'Out of Stock');
+
+      if (lowStockAccessories.length > 0) {
path: Frontend/src/services/alertsService.ts
user: login: github-actions[bot]
body: The `status` property is being compared as a string literal. Consider using an enum for `cab.status`, `accessory.status` and `material.status` for type safety and preventing typos.
created_at: 2025-05-09T19:47:45Z
author_association: NONE
diff_hunk: @@ -0,0 +1,131 @@
+<template>
+  <q-card bordered>
+    <q-card-section>
+      <div class="text-h6">Inventory Alerts</div>
+    </q-card-section>
+    <q-separator />
+    <q-card-section class="q-pa-none">
+      <q-list>
+        <q-item v-for="alert in alerts" :key="`${alert.category}-${alert.status}`" class="q-pa-md">
+          <q-item-section avatar>
+            <q-icon :name="getAlertIcon(alert.category)" :color="getAlertColor(alert.status)" />
+          </q-item-section>
+          <q-item-section>
+            <q-item-label>{{ alert.category }} - {{ alert.status }}</q-item-label>
+            <q-item-label caption>{{ alert.count }} {{ alert.category.toLowerCase() }} {{ alert.status.toLowerCase() }}</q-item-label>
+          </q-item-section>
+          <q-item-section side>
+            <q-btn 
+              flat 
+              round 
+              dense 
+              icon="visibility" 
+              @click="handleAlertAction(alert)"
+              :color="getAlertColor(alert.status)"
+            />
+          </q-item-section>
+        </q-item>
+        <q-item v-if="alerts.length === 0" class="text-center q-pa-lg text-grey">
+          <q-item-section>No inventory alerts</q-item-section>
+        </q-item>
+      </q-list>
+    </q-card-section>
+  </q-card>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { useQuasar } from 'quasar';
+import { alertsService, type InventoryAlert } from 'src/services/alertsService';
+
+const $q = useQuasar();
+const router = useRouter();
+const alerts = ref<InventoryAlert[]>([]);
+const isLoading = ref(false);
+
+// Fetch alerts on component mount
+onMounted(async () => {
+  await fetchAlerts();
path: Frontend/src/components/dashboard/DynamicAlertsPanel.vue
user: login: StanleighMorales
body: $q is what we got used to
created_at: 2025-05-09T20:07:01Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,126 @@
+import { accessoriesApi } from './accessoriesApi';
+import { api } from 'boot/axios';
+import type { MaterialRow } from 'src/types/materials';
+import { useAuthStore } from 'src/stores/auth';
+import { useCabsStore } from 'src/stores/cabs';
+
+/**
+ * Category types for inventory items
+ */
+export type AlertCategory = 'Cabs' | 'Accessories' | 'Materials';
+
+/**
+ * Status types for inventory alerts
+ */
+export type AlertStatus = 'Low Stock' | 'Out of Stock';
+
+/**
+ * Interface for inventory alert data
+ */
+export interface InventoryAlert {
+  category: AlertCategory;
+  status: AlertStatus;
+  count: number;
+}
+
+/**
+ * Service for handling system alerts related to inventory
+ */
+export const alertsService = {
+  /**
+   * Fetches all inventory items with low stock or out of stock status
+   * @returns Promise with array of inventory alerts
+   */
+  getInventoryAlerts: async (): Promise<InventoryAlert[]> => {
+    try {
+      const authStore = useAuthStore();
+      const cabsStore = useCabsStore();
+
+      // Initialize cabs if needed
+      if (cabsStore.cabRows.length === 0) {
+        await cabsStore.initializeCabs();
+      }
+
+      // Fetch data from all three inventory types
+      const [accessories, materials] = await Promise.all([
+        accessoriesApi.getAllAccessories(),
+        api.get<MaterialRow[]>('/api/materials', {
+          headers: {
+            Authorization: `Bearer ${authStore.token}`
+          }
+        }).then(response => response.data)
+      ]);
+
+      // Use the cabs from the store
+      const cabs = cabsStore.cabRows;
+
+      // Process alerts for each category
+      const alerts: InventoryAlert[] = [];
+
+      // Process cabs alerts
+      const lowStockCabs = cabs.filter(cab => cab.status === 'Low Stock');
+      const outOfStockCabs = cabs.filter(cab => cab.status === 'Out of Stock');
+
+      if (lowStockCabs.length > 0) {
+        alerts.push({
+          category: 'Cabs',
+          status: 'Low Stock',
+          count: lowStockCabs.length
+        });
+      }
+
+      if (outOfStockCabs.length > 0) {
+        alerts.push({
+          category: 'Cabs',
+          status: 'Out of Stock',
+          count: outOfStockCabs.length
+        });
+      }
+
+      // Process accessories alerts
+      const lowStockAccessories = accessories.filter(accessory => accessory.status === 'Low Stock');
+      const outOfStockAccessories = accessories.filter(accessory => accessory.status === 'Out of Stock');
+
+      if (lowStockAccessories.length > 0) {
path: Frontend/src/services/alertsService.ts
user: login: StanleighMorales
body: bugs the whole system, temporary string literal
created_at: 2025-05-09T21:13:34Z
author_association: COLLABORATOR

PR comments:
user: login: netlify[bot]
created_at: 2025-05-09T19:46:17Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *cortes-surplus* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 075cee8b615a60a0d819eadaf08251e552a313f0 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/cortes-surplus/deploys/681eb975bbd5a70008217f86 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-64--cortes-surplus.netlify.app](https://deploy-preview-64--cortes-surplus.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTY0LS1jb3J0ZXMtc3VycGx1cy5uZXRsaWZ5LmFwcCJ9.7MLGM4Qg6EkMldwqUGqJb0kwmXGIR5lsuIfvG8ttCSs)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [cortes-surplus Preview](https://deploy-preview-64--cortes-surplus.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/cortes-surplus/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: StanleighMorales
created_at: 2025-05-09T19:46:50Z
body: /gemini-review
author_association: COLLABORATOR
user: login: StanleighMorales
created_at: 2025-05-09T22:35:40Z
body: improved error handling
added consistent status bar design for materials status
added limit of alerts
modified id generation for each alerts
added individual mark as read option
author_association: COLLABORATOR