@@ -967,6 +1088,19 @@
let recognitionCarouselInterval = null;
let isRecognitionTransitioning = false;
+ // Current events carousel state - NOW FOR NON-INCIDENTS ONLY
+ // Incidents stay pinned, other events rotate
+ let nonIncidentEvents = [];
+ let currentNonIncidentIndex = 0;
+ let nonIncidentCarouselInterval = null;
+ let isNonIncidentTransitioning = false;
+
+ // Keep old variables for compatibility but they won't be used
+ let currentEventPairs = [];
+ let currentPairIndex = 0;
+ let currentCarouselInterval = null;
+ let isCurrentTransitioning = false;
+
// Get business unit from URL parameter
function getBusinessUnitFromURL() {
const urlParams = new URLSearchParams(window.location.search);
@@ -1054,8 +1188,8 @@
// Render events on the page (with smart delay during transitions)
function renderEvents(data) {
- // If carousel is mid-transition, delay the render
- if (isTransitioning) {
+ // If any carousel is mid-transition, delay the render
+ if (isTransitioning || isNonIncidentTransitioning || isRecognitionTransitioning) {
console.log('Carousel: Transition in progress, delaying render by 800ms');
pendingDataRender = data;
return;
@@ -1068,92 +1202,7 @@
const recognitions = data.current ? data.current.filter(e => e.typecolor === 'recognition') : [];
const otherCurrentEvents = data.current ? data.current.filter(e => e.typecolor !== 'recognition') : [];
- // Current Events - show all in grid layout (excluding Recognition) - FIRST priority
- if (otherCurrentEvents.length > 0) {
- // Split into active and resolved events
- const activeEvents = otherCurrentEvents.filter(e => !e.resolved);
- const resolvedEvents = otherCurrentEvents.filter(e => e.resolved);
-
- // Sort function: by type priority (Incident > Change > Awareness > TBD), then by date (earliest first)
- const typePriority = {
- 'Incident': 1,
- 'Change': 2,
- 'Awareness': 3,
- 'Recognition': 4,
- 'TBD': 5
- };
-
- const sortByTypeAndDate = (a, b) => {
- // First, sort by type priority
- const priorityA = typePriority[a.typename] || 999;
- const priorityB = typePriority[b.typename] || 999;
-
- if (priorityA !== priorityB) {
- return priorityA - priorityB;
- }
-
- // If same type, sort by start time (earliest first)
- const dateA = new Date(a.starttime);
- const dateB = new Date(b.starttime);
- return dateA - dateB;
- };
-
- // Sort both active and resolved events
- activeEvents.sort(sortByTypeAndDate);
- resolvedEvents.sort(sortByTypeAndDate);
-
- // Combine with active first, resolved last
- const sortedEvents = [...activeEvents, ...resolvedEvents];
-
- // Determine badge color based on highest severity
- const severity = getHighestSeverity(data.current);
-
- html += '
';
- html += `
CURRENT EVENTS
`;
-
- // Determine grid class based on number of events
- const gridClass = sortedEvents.length === 1 ? 'single' : 'multi';
- html += `
`;
-
- sortedEvents.forEach(event => {
- const borderColor = getColorFromType(event.typecolor);
- const ticketBadge = event.ticketnumber
- ? `
${escapeHtml(event.ticketnumber)}`
- : '';
- const resolvedIndicator = event.resolved ? '
RESOLVED
' : '';
-
- const endTimeText = event.resolved
- ? `
Resolved: ${formatDateTime(event.endtime)}`
- : event.endtime ? `
Ends: ${formatDateTime(event.endtime)}` : '
Status: Ongoing (no end time)';
-
- // Calculate progressive fade for resolved incidents (fade over 30 minutes)
- let opacity = 1.0;
- let resolvedClass = '';
- if (event.resolved && event.minutes_since_end !== null && event.minutes_since_end !== undefined) {
- // Fade from 1.0 to 0.5 over 30 minutes, then drop off
- opacity = Math.max(0.5, 1.0 - (event.minutes_since_end / 30) * 0.5);
- resolvedClass = ' resolved';
- }
-
- html += `
-
- ${resolvedIndicator}
-
${ticketBadge}
-
-
- Started: ${formatDateTime(event.starttime)}${endTimeText}
-
-
- `;
- });
-
- html += '
'; // Close grid
- html += '
'; // Close section
- }
-
- // Recognition Carousel Section - AFTER current events, BEFORE upcoming
+ // Recognition Carousel Section - FIRST (top of dashboard)
if (recognitions.length > 0) {
recognitionEvents = recognitions;
@@ -1197,22 +1246,148 @@
});
html += '
'; // Close carousel wrapper
-
- // Add dots if multiple recognitions
- if (recognitions.length > 1) {
- html += '';
- recognitions.forEach((_, index) => {
- html += ``;
- });
- html += '
';
- }
-
html += ''; // Close carousel container
html += ''; // Close section
} else {
recognitionEvents = [];
}
+ // Current Events - NEW LOGIC: Incidents pinned, others rotate
+ // Separate incidents from other event types
+ const incidents = otherCurrentEvents.filter(e => e.typename === 'Incident');
+ const nonIncidents = otherCurrentEvents.filter(e => e.typename !== 'Incident');
+
+ // Sort function: by date (earliest first), then resolved status
+ const sortByDateAndResolved = (a, b) => {
+ // Active events before resolved
+ if (a.resolved !== b.resolved) {
+ return a.resolved ? 1 : -1;
+ }
+ // Then by start time (earliest first)
+ const dateA = new Date(a.starttime);
+ const dateB = new Date(b.starttime);
+ return dateA - dateB;
+ };
+
+ // Sort incidents and non-incidents
+ incidents.sort(sortByDateAndResolved);
+ nonIncidents.sort(sortByDateAndResolved);
+
+ // Helper function to render a single event card
+ const renderEventCard = (event, index, total, showCounter = true) => {
+ const borderColor = getColorFromType(event.typecolor);
+ const ticketBadge = event.ticketnumber
+ ? `
+ ${resolvedIndicator}
+ ${eventCounter}
+
${ticketBadge}
+
+
+ Started: ${formatDateTime(event.starttime)}${endTimeText}
+
+
+ `;
+ };
+
+ // Determine badge color based on highest severity
+ const severity = getHighestSeverity(data.current);
+
+ // Only show current events section if there are incidents OR non-incidents
+ if (incidents.length > 0 || nonIncidents.length > 0) {
+ html += '';
+ html += `
CURRENT EVENTS
`;
+
+ // ========================================
+ // INCIDENTS SECTION - Always visible (pinned)
+ // ========================================
+ if (incidents.length > 0) {
+ html += '
';
+
+ const gridClass = incidents.length === 1 ? 'single' : 'multi';
+ html += `
`;
+ incidents.forEach((event, index) => {
+ html += renderEventCard(event, index, incidents.length, false); // No counter for pinned
+ });
+ html += '
'; // Close incidents grid
+ html += '
'; // Close incidents section
+ }
+
+ // ========================================
+ // NON-INCIDENTS SECTION - Rotating carousel
+ // ========================================
+ if (nonIncidents.length > 0) {
+ nonIncidentEvents = nonIncidents;
+
+ // Preserve current index if valid
+ if (currentNonIncidentIndex >= nonIncidents.length) {
+ currentNonIncidentIndex = 0;
+ }
+
+ html += '
';
+
+ if (nonIncidents.length === 1) {
+ // Single non-incident - no carousel needed
+ html += '
';
+ html += renderEventCard(nonIncidents[0], 0, 1, false);
+ html += '
';
+ } else {
+ // Multiple non-incidents - use carousel
+ html += '
';
+ html += '
';
+
+ nonIncidents.forEach((event, index) => {
+ const activeClass = index === currentNonIncidentIndex ? 'active' : 'enter-down';
+ html += `
+
+
${index + 1} of ${nonIncidents.length}
+
+
+
+ Started: ${formatDateTime(event.starttime)}
+ ${event.resolved ? `
Resolved: ${formatDateTime(event.endtime)}` : event.endtime ? `
Ends: ${formatDateTime(event.endtime)}` : '
Status: Ongoing'}
+
+
+ `;
+ });
+
+ html += '
'; // Close carousel wrapper
+ html += '
'; // Close carousel container
+ }
+ html += '
'; // Close non-incidents section
+ } else {
+ nonIncidentEvents = [];
+ }
+
+ html += '
'; // Close events-section
+ } else {
+ nonIncidentEvents = [];
+ }
+
+ // Clear old carousel state (not used anymore)
+ currentEventPairs = [];
+
// Upcoming Events - carousel with slide-up transition
if (data.upcoming && data.upcoming.length > 0) {
// Sort upcoming events by type priority, then by date
@@ -1262,8 +1437,10 @@
? `