From ecffa34d71bbc1ad6ac0d158086231766a4dd08d Mon Sep 17 00:00:00 2001 From: Saurabh Kumar Bajpai Date: Thu, 30 Jul 2026 22:30:50 +0530 Subject: [PATCH] fix: improve error handling --- Safety and awareness/script.js | 1018 ++++++++++++++++---------------- Shipments/shipments.js | 1012 +++++++++++++++---------------- 2 files changed, 1015 insertions(+), 1015 deletions(-) diff --git a/Safety and awareness/script.js b/Safety and awareness/script.js index 706476d..b3f1f26 100644 --- a/Safety and awareness/script.js +++ b/Safety and awareness/script.js @@ -1,510 +1,510 @@ -// Enhanced Road Safety Website JavaScript - -// Welcome animation and initialization -window.onload = function() { - // Create welcome popup instead of alert - showWelcomePopup(); - - // Initialize animations - initializeAnimations(); - - // Initialize statistics counter - initializeStatsCounter(); - - // Initialize smooth scrolling - initializeSmoothScrolling(); -}; - -// Welcome popup function -function showWelcomePopup() { - const popupContent = ` -
- -

Welcome to Road Safety Awareness!

-

- Your safety is our top priority. Explore our comprehensive resources to learn how to stay safe on the road. -

- -
- `; - - setTimeout(() => { - showPopup(popupContent); - }, 1000); -} - -// Smooth scrolling function -function scrollToSection(sectionId) { - const element = document.getElementById(sectionId); - if (element) { - element.scrollIntoView({ - behavior: 'smooth', - block: 'start' - }); - } -} - -// Initialize smooth scrolling for navigation links -function initializeSmoothScrolling() { - const navLinks = document.querySelectorAll('nav a[href^="#"]'); - navLinks.forEach(link => { - link.addEventListener('click', function(e) { - e.preventDefault(); - const targetId = this.getAttribute('href').substring(1); - scrollToSection(targetId); - }); - }); -} - -// Statistics counter animation -function initializeStatsCounter() { - const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - animateCounters(); - observer.unobserve(entry.target); - } - }); - }); - - const statsSection = document.querySelector('.stats-section'); - if (statsSection) { - observer.observe(statsSection); - } -} - -function animateCounters() { - const counters = document.querySelectorAll('.stat-number'); - - counters.forEach(counter => { - const target = parseInt(counter.getAttribute('data-target')); - const increment = target / 100; - let current = 0; - - const updateCounter = () => { - if (current < target) { - current += increment; - if (current > target) current = target; - - // Format numbers for display - if (target >= 1000000) { - counter.textContent = (current / 1000000).toFixed(1) + 'M'; - } else if (target >= 1000) { - counter.textContent = (current / 1000).toFixed(0) + 'K'; - } else { - counter.textContent = Math.floor(current) + '%'; - } - - requestAnimationFrame(updateCounter); - } - }; - - updateCounter(); - }); -} - -// Initialize scroll animations -function initializeAnimations() { - const observerOptions = { - threshold: 0.1, - rootMargin: '0px 0px -50px 0px' - }; - - const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.style.opacity = '1'; - entry.target.style.transform = 'translateY(0)'; - } - }); - }, observerOptions); - - // Observe all cards and sections - const animatedElements = document.querySelectorAll('.importance-card, .tip-card, .contact-item'); - animatedElements.forEach(el => { - el.style.opacity = '0'; - el.style.transform = 'translateY(30px)'; - el.style.transition = 'all 0.6s ease-out'; - observer.observe(el); - }); -} - -// Tip details popup -function showTipDetails(tipType) { - const tipDetails = { - seatbelt: { - title: "Always Wear Your Seatbelt", - icon: "fas fa-user-shield", - content: ` -

Seatbelt Safety

-
-

Why it matters: Seatbelts reduce the risk of death by 45% and serious injury by 50% for front-seat passengers.

-
-

Proper usage:

- -
-
- - Remember: It only takes 3 seconds to buckle up, but it can save your life! -
-
- ` - }, - speed: { - title: "Follow Speed Limits", - icon: "fas fa-tachometer-alt", - content: ` -

Speed Management

-
-

The impact of speed: Higher speeds dramatically increase both the likelihood of crashes and their severity.

-
-

Key guidelines:

- -
-
- - Fact: A 10% increase in speed leads to a 40% increase in fatal crash risk! -
-
- ` - }, - phone: { - title: "No Phone While Driving", - icon: "fas fa-mobile-alt", - content: ` -

Distraction-Free Driving

-
-

The danger: Using a phone while driving makes you 4 times more likely to be in a crash.

-
-

Safe practices:

- -
-
- - Tip: Use "Do Not Disturb While Driving" mode on your smartphone! -
-
- ` - }, - alcohol: { - title: "Never Drink and Drive", - icon: "fas fa-ban", - content: ` -

Zero Tolerance Policy

-
-

The reality: Alcohol impairs judgment, reaction time, and motor control - even in small amounts.

-
-

Safe alternatives:

- -
-
- - Warning: One drink can be one too many. There's no safe amount when driving! -
-
- ` - }, - pedestrians: { - title: "Watch for Pedestrians", - icon: "fas fa-walking", - content: ` -

Protecting Vulnerable Road Users

-
-

Why it's crucial: Pedestrians and cyclists are the most vulnerable road users with the highest injury rates.

-
-

Best practices:

- -
-
- - Remember: Every pedestrian is someone's family member. Drive with care! -
-
- ` - }, - helmet: { - title: "Always Wear Helmets", - icon: "fas fa-hard-hat", - content: ` -

Head Protection Saves Lives

-
-

Life-saving protection: Helmets reduce the risk of head injury by 70% and death by 40%.

-
-

Helmet safety tips:

- -
-
- - Protection: Your brain is irreplaceable. Always wear a quality helmet! -
-
- ` - } - }; - - if (tipDetails[tipType]) { - showPopup(tipDetails[tipType].content); - } -} - -// Importance card popup -function showImportancePopup(type) { - const importanceDetails = { - lives: { - content: ` -

Saving Lives Through Road Safety

-
-

Road traffic crashes are a leading cause of death worldwide, claiming over 1.35 million lives annually. However, most of these deaths are preventable through proper safety measures.

-
-

Impact of Safety Measures:

- -
- ` - }, - families: { - content: ` -

Protecting Families and Communities

-
-

Road crashes don't just affect individuals - they devastate entire families and communities. The ripple effects include emotional trauma, financial hardship, and long-term care needs.

-
-

Community Impact:

- -
- ` - }, - economy: { - content: ` -

Economic Benefits of Road Safety

-
-

Road crashes cost countries 3-5% of their GDP annually. Investing in road safety generates significant economic returns through reduced healthcare costs, property damage, and productivity losses.

-
-

Cost Savings Include:

- -
- ` - } - }; - - if (importanceDetails[type]) { - showPopup(importanceDetails[type].content); - } -} - -// Add click listeners for importance cards -document.addEventListener('DOMContentLoaded', function() { - const importanceCards = document.querySelectorAll('.importance-card'); - importanceCards.forEach(card => { - const popupType = card.getAttribute('data-popup'); - if (popupType) { - card.addEventListener('click', () => showImportancePopup(popupType)); - } - }); -}); - -// Emergency information popup -function showEmergencyInfo() { - const emergencyContent = ` -
-

Emergency Contacts

-
-
- Police Emergency: 100 -
-
- Medical Emergency: 108 -
-
- Fire Emergency: 101 -
-
- Traffic Helpline: 1073 -
-
-

- Save these numbers in your phone for quick access during emergencies. -

-
- `; - - showPopup(emergencyContent); -} - -// Report form popup -function showReportForm() { - const reportContent = ` -

Report Unsafe Road Conditions

-
-
- - -
-
- - -
-
- - -
- -
- `; - - showPopup(reportContent); -} - -// Submit report function -function submitReport(event) { - event.preventDefault(); - - // Simulate form submission - const successContent = ` -
- -

Report Submitted Successfully!

-

- Thank you for helping make our roads safer. Your report has been forwarded to the relevant authorities. -

-

- Reference ID: RS-2025-${Math.floor(Math.random() * 10000)} -

-
- `; - - showPopup(successContent); -} - -// Generic popup functions -function showPopup(content) { - const overlay = document.getElementById('popup-overlay'); - const body = document.getElementById('popup-body'); - - body.innerHTML = content; - overlay.classList.add('active'); - - // Prevent body scroll - document.body.style.overflow = 'hidden'; -} - -function closePopup() { - const overlay = document.getElementById('popup-overlay'); - overlay.classList.remove('active'); - - // Restore body scroll - document.body.style.overflow = 'auto'; -} - -// Keyboard event for closing popup -document.addEventListener('keydown', function(event) { - if (event.key === 'Escape') { - closePopup(); - } -}); - -// Add loading animation for page transitions -function addLoadingEffect() { - const loader = document.createElement('div'); - loader.innerHTML = ` -
-
- -

Loading Road Safety Information...

-
-
- `; - - document.body.appendChild(loader); - - setTimeout(() => { - loader.style.opacity = '0'; - setTimeout(() => { - document.body.removeChild(loader); - }, 500); - }, 1500); +// Enhanced Road Safety Website JavaScript + +// Welcome animation and initialization +window.onload = function() { + // Create welcome popup instead of alert + showWelcomePopup(); + + // Initialize animations + initializeAnimations(); + + // Initialize statistics counter + initializeStatsCounter(); + + // Initialize smooth scrolling + initializeSmoothScrolling(); +}; + +// Welcome popup function +function showWelcomePopup() { + const popupContent = ` +
+ +

Welcome to Road Safety Awareness!

+

+ Your safety is our top priority. Explore our comprehensive resources to learn how to stay safe on the road. +

+ +
+ `; + + setTimeout(() => { + showPopup(popupContent); + }, 1000); +} + +// Smooth scrolling function +function scrollToSection(sectionId) { + const element = document.getElementById(sectionId); + if (element) { + element.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + } +} + +// Initialize smooth scrolling for navigation links +function initializeSmoothScrolling() { + const navLinks = document.querySelectorAll('nav a[href^="#"]'); + navLinks.forEach(link => { + link.addEventListener('click', function(e) { + e.preventDefault(); + const targetId = this.getAttribute('href').substring(1); + scrollToSection(targetId); + }); + }); +} + +// Statistics counter animation +function initializeStatsCounter() { + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + animateCounters(); + observer.unobserve(entry.target); + } + }); + }); + + const statsSection = document.querySelector('.stats-section'); + if (statsSection) { + observer.observe(statsSection); + } +} + +function animateCounters() { + const counters = document.querySelectorAll('.stat-number'); + + counters.forEach(counter => { + const target = parseInt(counter.getAttribute('data-target', 10)); + const increment = target / 100; + let current = 0; + + const updateCounter = () => { + if (current < target) { + current += increment; + if (current > target) current = target; + + // Format numbers for display + if (target >= 1000000) { + counter.textContent = (current / 1000000).toFixed(1) + 'M'; + } else if (target >= 1000) { + counter.textContent = (current / 1000).toFixed(0) + 'K'; + } else { + counter.textContent = Math.floor(current) + '%'; + } + + requestAnimationFrame(updateCounter); + } + }; + + updateCounter(); + }); +} + +// Initialize scroll animations +function initializeAnimations() { + const observerOptions = { + threshold: 0.1, + rootMargin: '0px 0px -50px 0px' + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + } + }); + }, observerOptions); + + // Observe all cards and sections + const animatedElements = document.querySelectorAll('.importance-card, .tip-card, .contact-item'); + animatedElements.forEach(el => { + el.style.opacity = '0'; + el.style.transform = 'translateY(30px)'; + el.style.transition = 'all 0.6s ease-out'; + observer.observe(el); + }); +} + +// Tip details popup +function showTipDetails(tipType) { + const tipDetails = { + seatbelt: { + title: "Always Wear Your Seatbelt", + icon: "fas fa-user-shield", + content: ` +

Seatbelt Safety

+
+

Why it matters: Seatbelts reduce the risk of death by 45% and serious injury by 50% for front-seat passengers.

+
+

Proper usage:

+ +
+
+ + Remember: It only takes 3 seconds to buckle up, but it can save your life! +
+
+ ` + }, + speed: { + title: "Follow Speed Limits", + icon: "fas fa-tachometer-alt", + content: ` +

Speed Management

+
+

The impact of speed: Higher speeds dramatically increase both the likelihood of crashes and their severity.

+
+

Key guidelines:

+ +
+
+ + Fact: A 10% increase in speed leads to a 40% increase in fatal crash risk! +
+
+ ` + }, + phone: { + title: "No Phone While Driving", + icon: "fas fa-mobile-alt", + content: ` +

Distraction-Free Driving

+
+

The danger: Using a phone while driving makes you 4 times more likely to be in a crash.

+
+

Safe practices:

+ +
+
+ + Tip: Use "Do Not Disturb While Driving" mode on your smartphone! +
+
+ ` + }, + alcohol: { + title: "Never Drink and Drive", + icon: "fas fa-ban", + content: ` +

Zero Tolerance Policy

+
+

The reality: Alcohol impairs judgment, reaction time, and motor control - even in small amounts.

+
+

Safe alternatives:

+ +
+
+ + Warning: One drink can be one too many. There's no safe amount when driving! +
+
+ ` + }, + pedestrians: { + title: "Watch for Pedestrians", + icon: "fas fa-walking", + content: ` +

Protecting Vulnerable Road Users

+
+

Why it's crucial: Pedestrians and cyclists are the most vulnerable road users with the highest injury rates.

+
+

Best practices:

+ +
+
+ + Remember: Every pedestrian is someone's family member. Drive with care! +
+
+ ` + }, + helmet: { + title: "Always Wear Helmets", + icon: "fas fa-hard-hat", + content: ` +

Head Protection Saves Lives

+
+

Life-saving protection: Helmets reduce the risk of head injury by 70% and death by 40%.

+
+

Helmet safety tips:

+ +
+
+ + Protection: Your brain is irreplaceable. Always wear a quality helmet! +
+
+ ` + } + }; + + if (tipDetails[tipType]) { + showPopup(tipDetails[tipType].content); + } +} + +// Importance card popup +function showImportancePopup(type) { + const importanceDetails = { + lives: { + content: ` +

Saving Lives Through Road Safety

+
+

Road traffic crashes are a leading cause of death worldwide, claiming over 1.35 million lives annually. However, most of these deaths are preventable through proper safety measures.

+
+

Impact of Safety Measures:

+ +
+ ` + }, + families: { + content: ` +

Protecting Families and Communities

+
+

Road crashes don't just affect individuals - they devastate entire families and communities. The ripple effects include emotional trauma, financial hardship, and long-term care needs.

+
+

Community Impact:

+ +
+ ` + }, + economy: { + content: ` +

Economic Benefits of Road Safety

+
+

Road crashes cost countries 3-5% of their GDP annually. Investing in road safety generates significant economic returns through reduced healthcare costs, property damage, and productivity losses.

+
+

Cost Savings Include:

+ +
+ ` + } + }; + + if (importanceDetails[type]) { + showPopup(importanceDetails[type].content); + } +} + +// Add click listeners for importance cards +document.addEventListener('DOMContentLoaded', function() { + const importanceCards = document.querySelectorAll('.importance-card'); + importanceCards.forEach(card => { + const popupType = card.getAttribute('data-popup'); + if (popupType) { + card.addEventListener('click', () => showImportancePopup(popupType)); + } + }); +}); + +// Emergency information popup +function showEmergencyInfo() { + const emergencyContent = ` +
+

Emergency Contacts

+
+
+ Police Emergency: 100 +
+
+ Medical Emergency: 108 +
+
+ Fire Emergency: 101 +
+
+ Traffic Helpline: 1073 +
+
+

+ Save these numbers in your phone for quick access during emergencies. +

+
+ `; + + showPopup(emergencyContent); +} + +// Report form popup +function showReportForm() { + const reportContent = ` +

Report Unsafe Road Conditions

+
+
+ + +
+
+ + +
+
+ + +
+ +
+ `; + + showPopup(reportContent); +} + +// Submit report function +function submitReport(event) { + event.preventDefault(); + + // Simulate form submission + const successContent = ` +
+ +

Report Submitted Successfully!

+

+ Thank you for helping make our roads safer. Your report has been forwarded to the relevant authorities. +

+

+ Reference ID: RS-2025-${Math.floor(Math.random() * 10000)} +

+
+ `; + + showPopup(successContent); +} + +// Generic popup functions +function showPopup(content) { + const overlay = document.getElementById('popup-overlay'); + const body = document.getElementById('popup-body'); + + body.innerHTML = content; + overlay.classList.add('active'); + + // Prevent body scroll + document.body.style.overflow = 'hidden'; +} + +function closePopup() { + const overlay = document.getElementById('popup-overlay'); + overlay.classList.remove('active'); + + // Restore body scroll + document.body.style.overflow = 'auto'; +} + +// Keyboard event for closing popup +document.addEventListener('keydown', function(event) { + if (event.key === 'Escape') { + closePopup(); + } +}); + +// Add loading animation for page transitions +function addLoadingEffect() { + const loader = document.createElement('div'); + loader.innerHTML = ` +
+
+ +

Loading Road Safety Information...

+
+
+ `; + + document.body.appendChild(loader); + + setTimeout(() => { + loader.style.opacity = '0'; + setTimeout(() => { + document.body.removeChild(loader); + }, 500); + }, 1500); } \ No newline at end of file diff --git a/Shipments/shipments.js b/Shipments/shipments.js index b39c084..fcf1aa4 100644 --- a/Shipments/shipments.js +++ b/Shipments/shipments.js @@ -1,507 +1,507 @@ -// Shipments Management System -class ShipmentManager { - constructor() { - this.modal = null; - this.tbody = null; - this.emptyState = null; - this.stats = { pending: 0, delayed: 0, cancelled: 0, completed: 0 }; - this.trendChart = null; - this.statusChart = null; - this.shipments = []; - - this.init(); - } - - init() { - // Wait for DOM to be fully loaded - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', () => this.setupElements()); - } else { - this.setupElements(); - } - } - - setupElements() { - // Initialize modal - this.modal = new bootstrap.Modal(document.getElementById('modal')); - this.tbody = document.querySelector('#shipmentsTable tbody'); - this.emptyState = document.getElementById('emptyState'); - - // Setup event listeners - this.setupEventListeners(); - - // Initialize charts - this.initializeCharts(); - - // Load initial data (demo data) - this.loadDemoData(); - - // Setup mobile navigation - this.setupMobileNav(); - } - - setupEventListeners() { - // Add button click - document.getElementById('addBtn').addEventListener('click', () => this.showAddModal()); - - // Modal form submission - document.querySelector('#modal form').addEventListener('submit', (e) => this.handleFormSubmit(e)); - - // Quantity controls - document.getElementById('incQty').addEventListener('click', () => this.adjustQuantity(1)); - document.getElementById('decQty').addEventListener('click', () => this.adjustQuantity(-1)); - - // Search functionality - const searchInput = document.getElementById('searchInput'); - if (searchInput) { - searchInput.addEventListener('input', (e) => this.handleSearch(e.target.value)); - } - - // Modal reset on close - document.getElementById('modal').addEventListener('hidden.bs.modal', () => this.resetForm()); - } - - setupMobileNav() { - const burger = document.querySelector('.burger'); - const navLinks = document.querySelector('.nav-links'); - - if (burger && navLinks) { - burger.addEventListener('click', () => { - navLinks.classList.toggle('active'); - burger.classList.toggle('active'); - }); - } - } - - showAddModal() { - document.querySelector('.modal-title').innerHTML = ' Add New Shipment'; - this.resetForm(); - this.modal.show(); - } - - resetForm() { - const form = document.querySelector('#modal form'); - form.reset(); - document.getElementById('shipQty').value = 1; - - // Set minimum date to today - const today = new Date().toISOString().split('T')[0]; - document.getElementById('shipEta').setAttribute('min', today); - } - - adjustQuantity(change) { - const qtyInput = document.getElementById('shipQty'); - const currentValue = parseInt(qtyInput.value) || 1; - const newValue = Math.max(1, currentValue + change); - qtyInput.value = newValue; - } - - handleFormSubmit(e) { - e.preventDefault(); - - const shipmentData = { - id: document.getElementById('shipId').value.trim(), - status: document.getElementById('shipStatus').value, - depot: document.getElementById('shipDepot').value.trim(), - eta: document.getElementById('shipEta').value, - quantity: parseInt(document.getElementById('shipQty').value), - createdAt: new Date().toISOString() - }; - - // Validate required fields - if (!shipmentData.id || !shipmentData.status || !shipmentData.depot || !shipmentData.eta) { - this.showAlert('Please fill in all required fields', 'danger'); - return; - } - - // Check for duplicate ID - if (this.shipments.some(ship => ship.id === shipmentData.id)) { - this.showAlert('Shipment ID already exists', 'danger'); - return; - } - - this.addShipment(shipmentData); - this.modal.hide(); - this.showAlert('Shipment added successfully', 'success'); - } - - addShipment(shipmentData) { - this.shipments.push(shipmentData); - this.renderShipment(shipmentData); - this.updateStats(); - this.updateCharts(); - } - - renderShipment(shipment) { - const badgeClass = this.getStatusBadgeClass(shipment.status); - const row = document.createElement('tr'); - row.setAttribute('data-shipment-id', shipment.id); - - row.innerHTML = ` - ${shipment.id} - ${shipment.status} - ${shipment.depot} - ${this.formatDate(shipment.eta)} - ${shipment.quantity} - - - - - `; - - this.tbody.appendChild(row); - - // Add event listeners to action buttons - row.querySelector('.completeBtn').addEventListener('click', () => this.completeShipment(shipment.id)); - row.querySelector('.editBtn').addEventListener('click', () => this.editShipment(shipment.id)); - row.querySelector('.delBtn').addEventListener('click', () => this.deleteShipment(shipment.id)); - } - - getStatusBadgeClass(status) { - const statusClasses = { - 'Pending': 'info', - 'Delayed': 'warning', - 'Cancelled': 'secondary', - 'Completed': 'success' - }; - return statusClasses[status] || 'primary'; - } - - formatDate(dateString) { - const date = new Date(dateString); - return date.toLocaleDateString('en-US', { - year: 'numeric', - month: 'short', - day: 'numeric' - }); - } - - completeShipment(shipmentId) { - const shipment = this.shipments.find(s => s.id === shipmentId); - if (shipment && shipment.status !== 'Completed') { - shipment.status = 'Completed'; - - const row = document.querySelector(`tr[data-shipment-id="${shipmentId}"]`); - const badge = row.querySelector('.statusBadge'); - badge.textContent = 'Completed'; - badge.className = 'badge text-bg-success statusBadge'; - - this.updateStats(); - this.updateCharts(); - this.showAlert('Shipment marked as completed', 'success'); - } - } - - editShipment(shipmentId) { - const shipment = this.shipments.find(s => s.id === shipmentId); - if (shipment) { - // Populate form with existing data - document.getElementById('shipId').value = shipment.id; - document.getElementById('shipStatus').value = shipment.status; - document.getElementById('shipDepot').value = shipment.depot; - document.getElementById('shipEta').value = shipment.eta; - document.getElementById('shipQty').value = shipment.quantity; - - // Change modal title and store edit mode - document.querySelector('.modal-title').innerHTML = ' Edit Shipment'; - document.getElementById('shipId').setAttribute('readonly', true); - - this.modal.show(); - } - } - - deleteShipment(shipmentId) { - if (confirm('Are you sure you want to delete this shipment?')) { - this.shipments = this.shipments.filter(s => s.id !== shipmentId); - - const row = document.querySelector(`tr[data-shipment-id="${shipmentId}"]`); - row.remove(); - - this.updateStats(); - this.updateCharts(); - this.showAlert('Shipment deleted successfully', 'info'); - } - } - - handleSearch(searchTerm) { - const rows = this.tbody.querySelectorAll('tr'); - const term = searchTerm.toLowerCase(); - - rows.forEach(row => { - const text = row.textContent.toLowerCase(); - row.style.display = text.includes(term) ? '' : 'none'; - }); - } - - updateStats() { - this.stats = { pending: 0, delayed: 0, cancelled: 0, completed: 0 }; - - this.shipments.forEach(shipment => { - const status = shipment.status.toLowerCase(); - if (this.stats.hasOwnProperty(status)) { - this.stats[status]++; - } - }); - - // Update stat cards - document.getElementById('statTotal').textContent = this.shipments.length; - document.getElementById('statPending').textContent = this.stats.pending; - document.getElementById('statDelayed').textContent = this.stats.delayed; - document.getElementById('statCancelled').textContent = this.stats.cancelled; - - // Show/hide empty state - this.emptyState.style.display = this.shipments.length ? 'none' : 'block'; - } - - initializeCharts() { - // Trend Chart - const trendCtx = document.getElementById('trendChart'); - if (trendCtx) { - this.trendChart = new Chart(trendCtx, { - type: 'line', - data: { - labels: [], - datasets: [{ - label: 'Shipments by ETA', - data: [], - borderColor: '#0d6efd', - backgroundColor: 'rgba(13, 110, 253, 0.1)', - borderWidth: 3, - fill: true, - tension: 0.4, - pointBackgroundColor: '#0d6efd', - pointBorderColor: '#ffffff', - pointBorderWidth: 2, - pointRadius: 6 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false - }, - tooltip: { - backgroundColor: 'rgba(0, 0, 0, 0.8)', - titleColor: '#ffffff', - bodyColor: '#ffffff', - borderColor: '#0d6efd', - borderWidth: 1 - } - }, - scales: { - y: { - beginAtZero: true, - ticks: { - stepSize: 1 - }, - grid: { - color: 'rgba(0, 0, 0, 0.05)' - } - }, - x: { - grid: { - display: false - } - } - }, - interaction: { - intersect: false, - mode: 'index' - } - } - }); - } - - // Status Chart - const statusCtx = document.getElementById('statusChart'); - if (statusCtx) { - this.statusChart = new Chart(statusCtx, { - type: 'doughnut', - data: { - labels: ['Pending', 'Delayed', 'Cancelled', 'Completed'], - datasets: [{ - data: [0, 0, 0, 0], - backgroundColor: [ - '#0dcaf0', - '#ffc107', - '#6c757d', - '#198754' - ], - borderWidth: 0, - hoverOffset: 10 - }] - }, - options: { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - position: 'bottom', - labels: { - padding: 20, - usePointStyle: true, - font: { - size: 12, - weight: 'bold' - } - } - }, - tooltip: { - backgroundColor: 'rgba(0, 0, 0, 0.8)', - titleColor: '#ffffff', - bodyColor: '#ffffff', - callbacks: { - label: function(context) { - const label = context.label || ''; - const value = context.parsed || 0; - const total = context.dataset.data.reduce((a, b) => a + b, 0); - const percentage = total > 0 ? Math.round((value / total) * 100) : 0; - return `${label}: ${value} (${percentage}%)`; - } - } - } - }, - cutout: '60%' - } - }); - } - } - - updateCharts() { - if (this.trendChart) { - // Generate last 7 days - const today = new Date(); - const days = []; - const counts = []; - - for (let i = 6; i >= 0; i--) { - const date = new Date(today); - date.setDate(date.getDate() - i); - const dateString = date.toISOString().split('T')[0]; - const label = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); - - days.push(label); - - // Count shipments with ETA on this date - const count = this.shipments.filter(shipment => shipment.eta === dateString).length; - counts.push(count); - } - - this.trendChart.data.labels = days; - this.trendChart.data.datasets[0].data = counts; - this.trendChart.update(); - } - - if (this.statusChart) { - this.statusChart.data.datasets[0].data = [ - this.stats.pending, - this.stats.delayed, - this.stats.cancelled, - this.stats.completed - ]; - this.statusChart.update(); - } - } - - showAlert(message, type = 'info') { - // Create alert element - const alert = document.createElement('div'); - alert.className = `alert alert-${type} alert-dismissible fade show position-fixed`; - alert.style.cssText = 'top: 90px; right: 20px; z-index: 9999; max-width: 300px;'; - alert.innerHTML = ` - ${message} - - `; - - document.body.appendChild(alert); - - // Auto remove after 3 seconds - setTimeout(() => { - if (alert.parentNode) { - alert.remove(); - } - }, 3000); - } - - loadDemoData() { - // Load some demo data for testing - const demoShipments = [ - { - id: 'SH001', - status: 'Pending', - depot: 'Chennai Central', - eta: '2025-09-12', - quantity: 25, - createdAt: new Date().toISOString() - }, - { - id: 'SH002', - status: 'Delayed', - depot: 'Mumbai Port', - eta: '2025-09-15', - quantity: 15, - createdAt: new Date().toISOString() - }, - { - id: 'SH003', - status: 'Completed', - depot: 'Delhi Hub', - eta: '2025-09-10', - quantity: 30, - createdAt: new Date().toISOString() - } - ]; - - // Add demo shipments - demoShipments.forEach(shipment => { - this.shipments.push(shipment); - this.renderShipment(shipment); - }); - - this.updateStats(); - this.updateCharts(); - } -} - -// Initialize the application -const shipmentManager = new ShipmentManager(); - -// Additional utility functions -document.addEventListener('DOMContentLoaded', function() { - // Animate stats cards on load - const statCards = document.querySelectorAll('.stat-card'); - statCards.forEach((card, index) => { - setTimeout(() => { - card.style.opacity = '0'; - card.style.transform = 'translateY(20px)'; - card.style.transition = 'all 0.5s ease'; - - setTimeout(() => { - card.style.opacity = '1'; - card.style.transform = 'translateY(0)'; - }, 100); - }, index * 100); - }); - - // Add smooth scrolling for any anchor links - document.querySelectorAll('a[href^="#"]').forEach(anchor => { - anchor.addEventListener('click', function (e) { - e.preventDefault(); - const target = document.querySelector(this.getAttribute('href')); - if (target) { - target.scrollIntoView({ - behavior: 'smooth', - block: 'start' - }); - } - }); - }); +// Shipments Management System +class ShipmentManager { + constructor() { + this.modal = null; + this.tbody = null; + this.emptyState = null; + this.stats = { pending: 0, delayed: 0, cancelled: 0, completed: 0 }; + this.trendChart = null; + this.statusChart = null; + this.shipments = []; + + this.init(); + } + + init() { + // Wait for DOM to be fully loaded + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => this.setupElements()); + } else { + this.setupElements(); + } + } + + setupElements() { + // Initialize modal + this.modal = new bootstrap.Modal(document.getElementById('modal')); + this.tbody = document.querySelector('#shipmentsTable tbody'); + this.emptyState = document.getElementById('emptyState'); + + // Setup event listeners + this.setupEventListeners(); + + // Initialize charts + this.initializeCharts(); + + // Load initial data (demo data) + this.loadDemoData(); + + // Setup mobile navigation + this.setupMobileNav(); + } + + setupEventListeners() { + // Add button click + document.getElementById('addBtn').addEventListener('click', () => this.showAddModal()); + + // Modal form submission + document.querySelector('#modal form').addEventListener('submit', (e) => this.handleFormSubmit(e)); + + // Quantity controls + document.getElementById('incQty').addEventListener('click', () => this.adjustQuantity(1)); + document.getElementById('decQty').addEventListener('click', () => this.adjustQuantity(-1)); + + // Search functionality + const searchInput = document.getElementById('searchInput'); + if (searchInput) { + searchInput.addEventListener('input', (e) => this.handleSearch(e.target.value)); + } + + // Modal reset on close + document.getElementById('modal').addEventListener('hidden.bs.modal', () => this.resetForm()); + } + + setupMobileNav() { + const burger = document.querySelector('.burger'); + const navLinks = document.querySelector('.nav-links'); + + if (burger && navLinks) { + burger.addEventListener('click', () => { + navLinks.classList.toggle('active'); + burger.classList.toggle('active'); + }); + } + } + + showAddModal() { + document.querySelector('.modal-title').innerHTML = ' Add New Shipment'; + this.resetForm(); + this.modal.show(); + } + + resetForm() { + const form = document.querySelector('#modal form'); + form.reset(); + document.getElementById('shipQty').value = 1; + + // Set minimum date to today + const today = new Date().toISOString().split('T')[0]; + document.getElementById('shipEta').setAttribute('min', today); + } + + adjustQuantity(change) { + const qtyInput = document.getElementById('shipQty'); + const currentValue = parseInt(qtyInput.value, 10) || 1; + const newValue = Math.max(1, currentValue + change); + qtyInput.value = newValue; + } + + handleFormSubmit(e) { + e.preventDefault(); + + const shipmentData = { + id: document.getElementById('shipId').value.trim(), + status: document.getElementById('shipStatus').value, + depot: document.getElementById('shipDepot').value.trim(), + eta: document.getElementById('shipEta').value, + quantity: parseInt(document.getElementById('shipQty', 10).value), + createdAt: new Date().toISOString() + }; + + // Validate required fields + if (!shipmentData.id || !shipmentData.status || !shipmentData.depot || !shipmentData.eta) { + this.showAlert('Please fill in all required fields', 'danger'); + return; + } + + // Check for duplicate ID + if (this.shipments.some(ship => ship.id === shipmentData.id)) { + this.showAlert('Shipment ID already exists', 'danger'); + return; + } + + this.addShipment(shipmentData); + this.modal.hide(); + this.showAlert('Shipment added successfully', 'success'); + } + + addShipment(shipmentData) { + this.shipments.push(shipmentData); + this.renderShipment(shipmentData); + this.updateStats(); + this.updateCharts(); + } + + renderShipment(shipment) { + const badgeClass = this.getStatusBadgeClass(shipment.status); + const row = document.createElement('tr'); + row.setAttribute('data-shipment-id', shipment.id); + + row.innerHTML = ` + ${shipment.id} + ${shipment.status} + ${shipment.depot} + ${this.formatDate(shipment.eta)} + ${shipment.quantity} + + + + + `; + + this.tbody.appendChild(row); + + // Add event listeners to action buttons + row.querySelector('.completeBtn').addEventListener('click', () => this.completeShipment(shipment.id)); + row.querySelector('.editBtn').addEventListener('click', () => this.editShipment(shipment.id)); + row.querySelector('.delBtn').addEventListener('click', () => this.deleteShipment(shipment.id)); + } + + getStatusBadgeClass(status) { + const statusClasses = { + 'Pending': 'info', + 'Delayed': 'warning', + 'Cancelled': 'secondary', + 'Completed': 'success' + }; + return statusClasses[status] || 'primary'; + } + + formatDate(dateString) { + const date = new Date(dateString); + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + }); + } + + completeShipment(shipmentId) { + const shipment = this.shipments.find(s => s.id === shipmentId); + if (shipment && shipment.status !== 'Completed') { + shipment.status = 'Completed'; + + const row = document.querySelector(`tr[data-shipment-id="${shipmentId}"]`); + const badge = row.querySelector('.statusBadge'); + badge.textContent = 'Completed'; + badge.className = 'badge text-bg-success statusBadge'; + + this.updateStats(); + this.updateCharts(); + this.showAlert('Shipment marked as completed', 'success'); + } + } + + editShipment(shipmentId) { + const shipment = this.shipments.find(s => s.id === shipmentId); + if (shipment) { + // Populate form with existing data + document.getElementById('shipId').value = shipment.id; + document.getElementById('shipStatus').value = shipment.status; + document.getElementById('shipDepot').value = shipment.depot; + document.getElementById('shipEta').value = shipment.eta; + document.getElementById('shipQty').value = shipment.quantity; + + // Change modal title and store edit mode + document.querySelector('.modal-title').innerHTML = ' Edit Shipment'; + document.getElementById('shipId').setAttribute('readonly', true); + + this.modal.show(); + } + } + + deleteShipment(shipmentId) { + if (confirm('Are you sure you want to delete this shipment?')) { + this.shipments = this.shipments.filter(s => s.id !== shipmentId); + + const row = document.querySelector(`tr[data-shipment-id="${shipmentId}"]`); + row.remove(); + + this.updateStats(); + this.updateCharts(); + this.showAlert('Shipment deleted successfully', 'info'); + } + } + + handleSearch(searchTerm) { + const rows = this.tbody.querySelectorAll('tr'); + const term = searchTerm.toLowerCase(); + + rows.forEach(row => { + const text = row.textContent.toLowerCase(); + row.style.display = text.includes(term) ? '' : 'none'; + }); + } + + updateStats() { + this.stats = { pending: 0, delayed: 0, cancelled: 0, completed: 0 }; + + this.shipments.forEach(shipment => { + const status = shipment.status.toLowerCase(); + if (this.stats.hasOwnProperty(status)) { + this.stats[status]++; + } + }); + + // Update stat cards + document.getElementById('statTotal').textContent = this.shipments.length; + document.getElementById('statPending').textContent = this.stats.pending; + document.getElementById('statDelayed').textContent = this.stats.delayed; + document.getElementById('statCancelled').textContent = this.stats.cancelled; + + // Show/hide empty state + this.emptyState.style.display = this.shipments.length ? 'none' : 'block'; + } + + initializeCharts() { + // Trend Chart + const trendCtx = document.getElementById('trendChart'); + if (trendCtx) { + this.trendChart = new Chart(trendCtx, { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'Shipments by ETA', + data: [], + borderColor: '#0d6efd', + backgroundColor: 'rgba(13, 110, 253, 0.1)', + borderWidth: 3, + fill: true, + tension: 0.4, + pointBackgroundColor: '#0d6efd', + pointBorderColor: '#ffffff', + pointBorderWidth: 2, + pointRadius: 6 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false + }, + tooltip: { + backgroundColor: 'rgba(0, 0, 0, 0.8)', + titleColor: '#ffffff', + bodyColor: '#ffffff', + borderColor: '#0d6efd', + borderWidth: 1 + } + }, + scales: { + y: { + beginAtZero: true, + ticks: { + stepSize: 1 + }, + grid: { + color: 'rgba(0, 0, 0, 0.05)' + } + }, + x: { + grid: { + display: false + } + } + }, + interaction: { + intersect: false, + mode: 'index' + } + } + }); + } + + // Status Chart + const statusCtx = document.getElementById('statusChart'); + if (statusCtx) { + this.statusChart = new Chart(statusCtx, { + type: 'doughnut', + data: { + labels: ['Pending', 'Delayed', 'Cancelled', 'Completed'], + datasets: [{ + data: [0, 0, 0, 0], + backgroundColor: [ + '#0dcaf0', + '#ffc107', + '#6c757d', + '#198754' + ], + borderWidth: 0, + hoverOffset: 10 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'bottom', + labels: { + padding: 20, + usePointStyle: true, + font: { + size: 12, + weight: 'bold' + } + } + }, + tooltip: { + backgroundColor: 'rgba(0, 0, 0, 0.8)', + titleColor: '#ffffff', + bodyColor: '#ffffff', + callbacks: { + label: function(context) { + const label = context.label || ''; + const value = context.parsed || 0; + const total = context.dataset.data.reduce((a, b) => a + b, 0); + const percentage = total > 0 ? Math.round((value / total) * 100) : 0; + return `${label}: ${value} (${percentage}%)`; + } + } + } + }, + cutout: '60%' + } + }); + } + } + + updateCharts() { + if (this.trendChart) { + // Generate last 7 days + const today = new Date(); + const days = []; + const counts = []; + + for (let i = 6; i >= 0; i--) { + const date = new Date(today); + date.setDate(date.getDate() - i); + const dateString = date.toISOString().split('T')[0]; + const label = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); + + days.push(label); + + // Count shipments with ETA on this date + const count = this.shipments.filter(shipment => shipment.eta === dateString).length; + counts.push(count); + } + + this.trendChart.data.labels = days; + this.trendChart.data.datasets[0].data = counts; + this.trendChart.update(); + } + + if (this.statusChart) { + this.statusChart.data.datasets[0].data = [ + this.stats.pending, + this.stats.delayed, + this.stats.cancelled, + this.stats.completed + ]; + this.statusChart.update(); + } + } + + showAlert(message, type = 'info') { + // Create alert element + const alert = document.createElement('div'); + alert.className = `alert alert-${type} alert-dismissible fade show position-fixed`; + alert.style.cssText = 'top: 90px; right: 20px; z-index: 9999; max-width: 300px;'; + alert.innerHTML = ` + ${message} + + `; + + document.body.appendChild(alert); + + // Auto remove after 3 seconds + setTimeout(() => { + if (alert.parentNode) { + alert.remove(); + } + }, 3000); + } + + loadDemoData() { + // Load some demo data for testing + const demoShipments = [ + { + id: 'SH001', + status: 'Pending', + depot: 'Chennai Central', + eta: '2025-09-12', + quantity: 25, + createdAt: new Date().toISOString() + }, + { + id: 'SH002', + status: 'Delayed', + depot: 'Mumbai Port', + eta: '2025-09-15', + quantity: 15, + createdAt: new Date().toISOString() + }, + { + id: 'SH003', + status: 'Completed', + depot: 'Delhi Hub', + eta: '2025-09-10', + quantity: 30, + createdAt: new Date().toISOString() + } + ]; + + // Add demo shipments + demoShipments.forEach(shipment => { + this.shipments.push(shipment); + this.renderShipment(shipment); + }); + + this.updateStats(); + this.updateCharts(); + } +} + +// Initialize the application +const shipmentManager = new ShipmentManager(); + +// Additional utility functions +document.addEventListener('DOMContentLoaded', function() { + // Animate stats cards on load + const statCards = document.querySelectorAll('.stat-card'); + statCards.forEach((card, index) => { + setTimeout(() => { + card.style.opacity = '0'; + card.style.transform = 'translateY(20px)'; + card.style.transition = 'all 0.5s ease'; + + setTimeout(() => { + card.style.opacity = '1'; + card.style.transform = 'translateY(0)'; + }, 100); + }, index * 100); + }); + + // Add smooth scrolling for any anchor links + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute('href')); + if (target) { + target.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + } + }); + }); }); \ No newline at end of file