hardware / index.html
tlynn's picture
undefined - Initial Deployment
d861fe8 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sens Gruppen Hardware Inventory</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.28/jspdf.plugin.autotable.min.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
sens: {
blue: '#005b96',
light: '#e1f0ff',
dark: '#003d66'
}
}
}
}
}
</script>
<style>
.form-view {
transition: all 0.3s ease;
}
.table-view {
transition: all 0.3s ease;
}
.hidden-view {
display: none;
opacity: 0;
}
.visible-view {
display: block;
opacity: 1;
}
.toast {
animation: fadeInOut 3s ease-in-out;
}
@keyframes fadeInOut {
0% { opacity: 0; transform: translateY(20px); }
15% { opacity: 1; transform: translateY(0); }
85% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(-20px); }
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="mb-8">
<div class="flex justify-between items-center">
<div>
<h1 class="text-3xl font-bold text-sens-blue">
<i class="fas fa-laptop-house mr-2"></i> Sens Hardware Inventory
</h1>
<p class="text-gray-600">Manage all hardware assets across the Sens Gruppen companies</p>
</div>
<img src="https://via.placeholder.com/150x50?text=Sens+Logo" alt="Sens Gruppen Logo" class="h-12">
</div>
</header>
<!-- Main Content Area -->
<main>
<!-- Controls Bar -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6 sticky top-0 z-10">
<div class="flex flex-wrap gap-3 items-center justify-between">
<div class="flex flex-wrap gap-2">
<button id="newItemBtn" class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-plus-circle mr-2"></i> New Item
</button>
<button id="searchBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-search mr-2"></i> Search
</button>
<button id="queryBtn" class="bg-purple-500 hover:bg-purple-600 text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-filter mr-2"></i> Query
</button>
</div>
<div class="flex flex-wrap gap-2">
<button id="exportBtn" class="bg-yellow-500 hover:bg-yellow-600 text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-file-export mr-2"></i> Export
</button>
<button id="printBtn" class="bg-indigo-500 hover:bg-indigo-600 text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-print mr-2"></i> Print
</button>
<button id="toggleViewBtn" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-table mr-2"></i> Spreadsheet View
</button>
</div>
</div>
</div>
<!-- Search/Query Panel (Initially hidden) -->
<div id="searchQueryPanel" class="hidden bg-white rounded-lg shadow-md p-4 mb-6">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label class="block text-gray-700 mb-1">Hardware Type</label>
<select id="searchType" class="w-full p-2 border rounded-lg">
<option value="">All Types</option>
<option value="Laptop">Laptop</option>
<option value="Docking stations">Docking Station</option>
<option value="Monitors">Monitor</option>
<option value="Keyboards and mice">Keyboard/Mouse</option>
<option value="Headphones">Headphones</option>
<option value="Mobile telephones">Mobile Phone</option>
<option value="TV">TV</option>
<option value="Printers">Printer</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-1">Company</label>
<select id="searchCompany" class="w-full p-2 border rounded-lg">
<option value="">All Companies</option>
<option value="Sens Utvikling AS">Sens Utvikling AS</option>
<option value="Sens Arbeidsinkludering AS">Sens Arbeidsinkludering AS</option>
<option value="Sens Gruppen As">Sens Gruppen As</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-1">Department</label>
<select id="searchDepartment" class="w-full p-2 border rounded-lg">
<option value="">All Departments</option>
<option value="Moss">Moss</option>
<option value="Sarpsborg">Sarpsborg</option>
<option value="Halden">Halden</option>
<option value="Askim">Askim</option>
<option value="JMO">JMO</option>
<option value="SJF">SJF</option>
<option value="OMS">OMS</option>
<option value="AFT">AFT</option>
<option value="VTA">VTA</option>
<option value="Jobb+">Jobb+</option>
<option value="Miljoårena">Miljoårena</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-1">Make</label>
<select id="searchMake" class="w-full p-2 border rounded-lg">
<option value="">All Makes</option>
<option value="Lenovo">Lenovo</option>
<option value="Benq">Benq</option>
<option value="Philips">Philips</option>
<option value="HP">HP</option>
<option value="Asus">Asus</option>
<option value="Samsung">Samsung</option>
<option value="Sony">Sony</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-1">Employee Name</label>
<input id="searchEmployee" type="text" class="w-full p-2 border rounded-lg" placeholder="Search by employee...">
</div>
<div class="flex items-end">
<button id="applySearchBtn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-check mr-2"></i> Apply Filters
</button>
<button id="clearSearchBtn" class="ml-2 bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-times mr-2"></i> Clear
</button>
</div>
</div>
</div>
<!-- Form View -->
<div id="formView" class="form-view visible-view bg-white rounded-lg shadow-md p-6 mb-6">
<h2 class="text-xl font-semibold mb-4 text-sens-blue border-b pb-2">
<i class="fas fa-edit mr-2"></i> Hardware Details
</h2>
<form id="hardwareForm">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Column 1 -->
<div class="space-y-4">
<div>
<label class="block text-gray-700 mb-1">Hardware Type *</label>
<select id="type" name="type" required class="w-full p-2 border rounded-lg">
<option value="">Select Type</option>
<option value="Laptop">Laptop</option>
<option value="Docking stations">Docking Station</option>
<option value="Monitors">Monitor</option>
<option value="Keyboards and mice">Keyboard/Mouse</option>
<option value="Headphones">Headphones</option>
<option value="Mobile telephones">Mobile Phone</option>
<option value="TV">TV</option>
<option value="Printers">Printer</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-1">Company *</label>
<select id="company" name="company" required class="w-full p-2 border rounded-lg">
<option value="">Select Company</option>
<option value="Sens Utvikling AS">Sens Utvikling AS</option>
<option value="Sens Arbeidsinkludering AS">Sens Arbeidsinkludering AS</option>
<option value="Sens Gruppen As">Sens Gruppen As</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-1">Department *</label>
<select id="department" name="department" required class="w-full p-2 border rounded-lg">
<option value="">Select Department</option>
<option value="Moss">Moss</option>
<option value="Sarpsborg">Sarpsborg</option>
<option value="Halden">Halden</option>
<option value="Askim">Askim</option>
<option value="JMO">JMO</option>
<option value="SJF">SJF</option>
<option value="OMS">OMS</option>
<option value="AFT">AFT</option>
<option value="VTA">VTA</option>
<option value="Jobb+">Jobb+</option>
<option value="Miljoårena">Miljoårena</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-1">Department Manager</label>
<input id="departmentManager" name="departmentManager" type="text" class="w-full p-2 border rounded-lg" value="Gunnar" readonly>
</div>
</div>
<!-- Column 2 -->
<div class="space-y-4">
<div>
<label class="block text-gray-700 mb-1">Employee Name</label>
<input id="employeeName" name="employeeName" type="text" class="w-full p-2 border rounded-lg" placeholder="e.g. Sandra Johnson">
</div>
<div>
<label class="block text-gray-700 mb-1">Item Description</label>
<input id="itemDescription" name="itemDescription" type="text" class="w-full p-2 border rounded-lg" placeholder="e.g. Screen">
</div>
<div>
<label class="block text-gray-700 mb-1">Make *</label>
<select id="make" name="make" required class="w-full p-2 border rounded-lg">
<option value="">Select Make</option>
<option value="Lenovo">Lenovo</option>
<option value="Benq">Benq</option>
<option value="Philips">Philips</option>
<option value="HP">HP</option>
<option value="Asus">Asus</option>
<option value="Samsung">Samsung</option>
<option value="Sony">Sony</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-1">Model *</label>
<input id="model" name="model" type="text" required class="w-full p-2 border rounded-lg" placeholder="e.g. Lenovo L14">
</div>
</div>
<!-- Column 3 -->
<div class="space-y-4">
<div>
<label class="block text-gray-700 mb-1">Serial Number *</label>
<input id="serialNumber" name="serialNumber" type="text" required class="w-full p-2 border rounded-lg" placeholder="e.g. PHE20212">
</div>
<div>
<label class="block text-gray-700 mb-1">Full Specifications</label>
<textarea id="specifications" name="specifications" rows="3" class="w-full p-2 border rounded-lg" placeholder="e.g. Lenovo laptop, 14&quot; screen, 256GB Hard disk, 16GB Ram"></textarea>
</div>
<div>
<label class="block text-gray-700 mb-1">Date Purchased *</label>
<input id="datePurchased" name="datePurchased" type="date" required class="w-full p-2 border rounded-lg">
</div>
<div>
<label class="block text-gray-700 mb-1">Warranty Expiration *</label>
<input id="warrantyExpiration" name="warrantyExpiration" type="date" required class="w-full p-2 border rounded-lg">
</div>
<div>
<label class="block text-gray-700 mb-1">Price (Kr)</label>
<input id="price" name="price" type="number" class="w-full p-2 border rounded-lg" placeholder="e.g. 9909">
</div>
<input type="hidden" id="itemId" name="itemId">
<input type="hidden" id="uniqueIdentifier" name="uniqueIdentifier">
</div>
</div>
<div class="mt-6 flex justify-end gap-3">
<button type="button" id="cancelBtn" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg">
Cancel
</button>
<button type="submit" id="saveBtn" class="bg-sens-blue hover:bg-sens-dark text-white px-4 py-2 rounded-lg">
Save Item
</button>
</div>
</form>
</div>
<!-- Table View -->
<div id="tableView" class="table-view hidden-view bg-white rounded-lg shadow-md p-4 overflow-x-auto">
<h2 class="text-xl font-semibold mb-4 text-sens-blue border-b pb-2">
<i class="fas fa-table mr-2"></i> Inventory Overview
</h2>
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Company</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Department</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Employee</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Make/Model</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Warranty</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody id="inventoryTableBody" class="bg-white divide-y divide-gray-200">
<!-- Data will be populated here by JavaScript -->
</tbody>
</table>
</div>
<!-- Warranty Alerts -->
<div class="bg-white rounded-lg shadow-md p-4 mt-6">
<h2 class="text-xl font-semibold mb-4 text-sens-blue border-b pb-2">
<i class="fas fa-bell mr-2"></i> Warranty Expiration Alerts
</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-yellow-100 border-l-4 border-yellow-500 p-4">
<h3 class="font-bold text-yellow-800">Approaching Expiry (1 month or less)</h3>
<ul id="monthExpiryList" class="mt-2 space-y-1">
<!-- Items will be added here by JavaScript -->
</ul>
</div>
<div class="bg-red-100 border-l-4 border-red-500 p-4">
<h3 class="font-bold text-red-800">Imminent Expiry (2 weeks or less)</h3>
<ul id="weekExpiryList" class="mt-2 space-y-1">
<!-- Items will be added here by JavaScript -->
</ul>
</div>
</div>
<div class="mt-4">
<button id="sendAlertsBtn" class="bg-sens-blue hover:bg-sens-dark text-white px-4 py-2 rounded-lg flex items-center">
<i class="fas fa-envelope mr-2"></i> Email Alerts Now
</button>
</div>
</div>
</main>
</div>
<!-- Toast Notification -->
<div id="toastNotification" class="toast fixed bottom-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg hidden">
<div class="flex items-center">
<i class="fas fa-check-circle mr-2"></i>
<span id="toastMessage">Item saved successfully!</span>
</div>
</div>
<script>
// Sample data - in a real app, this would come from a database
let inventoryData = [
{
id: 1,
uniqueIdentifier: "L-0001",
type: "Laptop",
company: "Sens Utvikling AS",
department: "Moss",
departmentManager: "Gunnar",
employeeName: "Sandra Johnson",
itemDescription: "Work laptop",
make: "Lenovo",
model: "ThinkPad L14",
serialNumber: "LEN202101",
specifications: "Lenovo laptop, 14\" screen, 256GB SSD, 16GB RAM",
datePurchased: "2023-01-15",
warrantyExpiration: "2026-01-15",
price: 9909
},
{
id: 2,
uniqueIdentifier: "M-0001",
type: "Monitors",
company: "Sens Arbeidsinkludering AS",
department: "Sarpsborg",
departmentManager: "Gunnar",
employeeName: "John Doe",
itemDescription: "Main monitor",
make: "Benq",
model: "PD2700U",
serialNumber: "BEN202102",
specifications: "27-inch 4K UHD IPS monitor",
datePurchased: "2023-03-20",
warrantyExpiration: "2025-03-20",
price: 5499
},
{
id: 3,
uniqueIdentifier: "H-0001",
type: "Headphones",
company: "Sens Gruppen As",
department: "Halden",
departmentManager: "Gunnar",
employeeName: "Anna Smith",
itemDescription: "Noise-cancelling headphones",
make: "Sony",
model: "WH-1000XM4",
serialNumber: "SON202103",
specifications: "Wireless noise cancelling headphones",
datePurchased: "2023-05-10",
warrantyExpiration: "2024-05-10",
price: 2499
}
];
// DOM Elements
const formView = document.getElementById('formView');
const tableView = document.getElementById('tableView');
const toggleViewBtn = document.getElementById('toggleViewBtn');
const hardwareForm = document.getElementById('hardwareForm');
const inventoryTableBody = document.getElementById('inventoryTableBody');
const searchQueryPanel = document.getElementById('searchQueryPanel');
const searchBtn = document.getElementById('searchBtn');
const newItemBtn = document.getElementById('newItemBtn');
const cancelBtn = document.getElementById('cancelBtn');
const saveBtn = document.getElementById('saveBtn');
const applySearchBtn = document.getElementById('applySearchBtn');
const clearSearchBtn = document.getElementById('clearSearchBtn');
const exportBtn = document.getElementById('exportBtn');
const printBtn = document.getElementById('printBtn');
const sendAlertsBtn = document.getElementById('sendAlertsBtn');
const toastNotification = document.getElementById('toastNotification');
const toastMessage = document.getElementById('toastMessage');
const monthExpiryList = document.getElementById('monthExpiryList');
const weekExpiryList = document.getElementById('weekExpiryList');
// Current state
let currentView = 'form';
let currentItemId = null;
let isEditMode = false;
// Prefix map for unique identifiers
const prefixMap = {
'Laptop': 'L',
'Docking stations': 'D',
'Monitors': 'M',
'Keyboards and mice': 'K',
'Headphones': 'H',
'Mobile telephones': 'MT',
'TV': 'TV',
'Printers': 'P'
};
// Initialize the app
function init() {
// Set current date as default for date fields
const today = new Date().toISOString().split('T')[0];
document.getElementById('datePurchased').value = today;
// Calculate warranty expiration (3 years from today by default)
const warrantyDate = new Date();
warrantyDate.setFullYear(warrantyDate.getFullYear() + 3);
document.getElementById('warrantyExpiration').value = warrantyDate.toISOString().split('T')[0];
renderInventoryTable();
checkWarrantyAlerts();
// Event listeners
toggleViewBtn.addEventListener('click', toggleView);
newItemBtn.addEventListener('click', newItem);
cancelBtn.addEventListener('click', resetForm);
hardwareForm.addEventListener('submit', saveItem);
searchBtn.addEventListener('click', toggleSearchPanel);
applySearchBtn.addEventListener('click', applySearch);
clearSearchBtn.addEventListener('click', clearSearch);
exportBtn.addEventListener('click', exportToPDF);
printBtn.addEventListener('click', printInventory);
sendAlertsBtn.addEventListener('click', sendAlerts);
// Generate unique ID when type changes
document.getElementById('type').addEventListener('change', generateUniqueIdentifier);
}
// Toggle between form and table view
function toggleView() {
if (currentView === 'form') {
formView.classList.remove('visible-view');
formView.classList.add('hidden-view');
tableView.classList.remove('hidden-view');
tableView.classList.add('visible-view');
toggleViewBtn.innerHTML = '<i class="fas fa-edit mr-2"></i> Form View';
currentView = 'table';
} else {
formView.classList.remove('hidden-view');
formView.classList.add('visible-view');
tableView.classList.remove('visible-view');
tableView.classList.add('hidden-view');
toggleViewBtn.innerHTML = '<i class="fas fa-table mr-2"></i> Spreadsheet View';
currentView = 'form';
}
}
// Create new item
function newItem() {
resetForm();
isEditMode = false;
currentItemId = null;
generateUniqueIdentifier();
scrollToTop();
}
// Reset form
function resetForm() {
hardwareForm.reset();
document.getElementById('itemId').value = '';
document.getElementById('uniqueIdentifier').value = '';
document.getElementById('departmentManager').value = 'Gunnar';
// Set current date as default for date fields
const today = new Date().toISOString().split('T')[0];
document.getElementById('datePurchased').value = today;
// Calculate warranty expiration (3 years from today by default)
const warrantyDate = new Date();
warrantyDate.setFullYear(warrantyDate.getFullYear() + 3);
document.getElementById('warrantyExpiration').value = warrantyDate.toISOString().split('T')[0];
}
// Generate unique identifier based on type
function generateUniqueIdentifier() {
const type = document.getElementById('type').value;
if (!type) return;
const prefix = prefixMap[type] || 'X';
const itemsOfType = inventoryData.filter(item => item.type === type);
const nextNumber = (itemsOfType.length + 1).toString().padStart(4, '0');
const uniqueId = `${prefix}-${nextNumber}`;
document.getElementById('uniqueIdentifier').value = uniqueId;
}
// Save item (create or update)
function saveItem(e) {
e.preventDefault();
const formData = new FormData(hardwareForm);
const itemData = {};
formData.forEach((value, key) => {
itemData[key] = value;
});
if (isEditMode && currentItemId) {
// Update existing item
const index = inventoryData.findIndex(item => item.id === currentItemId);
if (index !== -1) {
inventoryData[index] = {
...inventoryData[index],
...itemData,
id: currentItemId
};
showToast('Item updated successfully!');
}
} else {
// Create new item
const newId = inventoryData.length > 0 ? Math.max(...inventoryData.map(item => item.id)) + 1 : 1;
itemData.id = newId;
inventoryData.push(itemData);
showToast('Item added successfully!');
}
renderInventoryTable();
checkWarrantyAlerts();
resetForm();
}
// Edit item
function editItem(id) {
const item = inventoryData.find(item => item.id === id);
if (!item) return;
// Populate form with item data
for (const key in item) {
if (document.getElementById(key)) {
document.getElementById(key).value = item[key];
}
}
currentItemId = id;
isEditMode = true;
scrollToTop();
}
// Delete item
function deleteItem(id) {
if (confirm('Are you sure you want to delete this item?')) {
inventoryData = inventoryData.filter(item => item.id !== id);
renderInventoryTable();
checkWarrantyAlerts();
showToast('Item deleted successfully!');
if (isEditMode && currentItemId === id) {
resetForm();
isEditMode = false;
currentItemId = null;
}
}
}
// Render inventory table
function renderInventoryTable(filteredData = null) {
const data = filteredData || inventoryData;
inventoryTableBody.innerHTML = '';
if (data.length === 0) {
const row = document.createElement('tr');
row.innerHTML = `
<td colspan="8" class="px-4 py-4 text-center text-gray-500">
No items found. Click "New Item" to add one.
</td>
`;
inventoryTableBody.appendChild(row);
return;
}
data.forEach(item => {
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50';
// Calculate warranty status
const warrantyDate = new Date(item.warrantyExpiration);
const today = new Date();
const daysLeft = Math.floor((warrantyDate - today) / (1000 * 60 * 60 * 24));
let warrantyStatus = '';
let statusClass = '';
if (daysLeft < 0) {
warrantyStatus = 'Expired';
statusClass = 'bg-red-100 text-red-800';
} else if (daysLeft <= 14) {
warrantyStatus = `${daysLeft} days`;
statusClass = 'bg-red-100 text-red-800';
} else if (daysLeft <= 30) {
warrantyStatus = `${daysLeft} days`;
statusClass = 'bg-yellow-100 text-yellow-800';
} else {
warrantyStatus = 'Valid';
statusClass = 'bg-green-100 text-green-800';
}
row.innerHTML = `
<td class="px-4 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${item.uniqueIdentifier}</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">${item.type}</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">${item.company}</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">${item.department}</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">${item.employeeName || '-'}</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">${item.make} ${item.model}</td>
<td class="px-4 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${statusClass}">
${warrantyStatus}
</span>
</td>
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
<button onclick="editItem(${item.id})" class="text-blue-600 hover:text-blue-900 mr-2">
<i class="fas fa-edit"></i>
</button>
<button onclick="deleteItem(${item.id})" class="text-red-600 hover:text-red-900">
<i class="fas fa-trash-alt"></i>
</button>
</td>
`;
inventoryTableBody.appendChild(row);
});
}
// Toggle search panel
function toggleSearchPanel() {
searchQueryPanel.classList.toggle('hidden');
if (!searchQueryPanel.classList.contains('hidden')) {
searchBtn.innerHTML = '<i class="fas fa-times mr-2"></i> Close Search';
} else {
searchBtn.innerHTML = '<i class="fas fa-search mr-2"></i> Search';
}
}
// Apply search filters
function applySearch() {
const type = document.getElementById('searchType').value;
const company = document.getElementById('searchCompany').value;
const department = document.getElementById('searchDepartment').value;
const make = document.getElementById('searchMake').value;
const employee = document.getElementById('searchEmployee').value.toLowerCase();
let filteredData = inventoryData;
if (type) {
filteredData = filteredData.filter(item => item.type === type);
}
if (company) {
filteredData = filteredData.filter(item => item.company === company);
}
if (department) {
filteredData = filteredData.filter(item => item.department === department);
}
if (make) {
filteredData = filteredData.filter(item => item.make === make);
}
if (employee) {
filteredData = filteredData.filter(item =>
item.employeeName && item.employeeName.toLowerCase().includes(employee)
);
}
renderInventoryTable(filteredData);
toggleSearchPanel();
}
// Clear search filters
function clearSearch() {
document.getElementById('searchType').value = '';
document.getElementById('searchCompany').value = '';
document.getElementById('searchDepartment').value = '';
document.getElementById('searchMake').value = '';
document.getElementById('searchEmployee').value = '';
renderInventoryTable();
}
// Check for warranty alerts
function checkWarrantyAlerts() {
const today = new Date();
const oneMonthFromNow = new Date();
oneMonthFromNow.setMonth(today.getMonth() + 1);
const twoWeeksFromNow = new Date();
twoWeeksFromNow.setDate(today.getDate() + 14);
// Clear previous alerts
monthExpiryList.innerHTML = '';
weekExpiryList.innerHTML = '';
inventoryData.forEach(item => {
const expiryDate = new Date(item.warrantyExpiration);
if (expiryDate <= twoWeeksFromNow && expiryDate >= today) {
// Within 2 weeks expiry
const li = document.createElement('li');
li.innerHTML = `
<div class="flex items-start">
<span class="flex-shrink-0 bg-red-500 w-2 h-2 mt-1.5 rounded-full"></span>
<span class="ml-2">${item.uniqueIdentifier} - ${item.type} (${item.company}/${item.department}) - Expires in ${Math.floor((expiryDate - today) / (1000 * 60 * 60 * 24))} days</span>
</div>
`;
weekExpiryList.appendChild(li);
} else if (expiryDate <= oneMonthFromNow && expiryDate > twoWeeksFromNow) {
// Within 1 month expiry
const li = document.createElement('li');
li.innerHTML = `
<div class="flex items-start">
<span class="flex-shrink-0 bg-yellow-500 w-2 h-2 mt-1.5 rounded-full"></span>
<span class="ml-2">${item.uniqueIdentifier} - ${item.type} (${item.company}/${item.department}) - Expires in ${Math.floor((expiryDate - today) / (1000 * 60 * 60 * 24))} days</span>
</div>
`;
monthExpiryList.appendChild(li);
}
});
// Show empty state if no alerts
if (monthExpiryList.children.length === 0) {
monthExpiryList.innerHTML = '<li class="text-gray-500 ml-4">No items expiring within the next month</li>';
}
if (weekExpiryList.children.length === 0) {
weekExpiryList.innerHTML = '<li class="text-gray-500 ml-4">No items expiring within the next 2 weeks</li>';
}
}
// Send alerts via email (simulated)
function sendAlerts() {
// In a real app, this would connect to an email service
// For demo purposes, we'll just show a toast notification
const alerts = [];
const monthItems = [...monthExpiryList.querySelectorAll('li')]
.filter(li => !li.textContent.includes('No items'));
const weekItems = [...weekExpiryList.querySelectorAll('li')]
.filter(li => !li.textContent.includes('No items'));
if (monthItems.length === 0 && weekItems.length === 0) {
showToast('No warranty alerts to send');
return;
}
let message = 'Warranty Alert Emails Sent:\n\n';
if (monthItems.length > 0) {
message += '1 Month Alerts:\n';
monthItems.forEach(li => {
message += `- ${li.textContent.trim()}\n`;
});
message += '\n';
}
if (weekItems.length > 0) {
message += '2 Week Alerts:\n';
weekItems.forEach(li => {
message += `- ${li.textContent.trim()}\n`;
});
}
// Show confirmation dialog with the message that would be sent
if (confirm(`${message}\nConfirm sending these alerts?`)) {
showToast('Warranty alerts sent successfully!');
}
}
// Export to PDF
function exportToPDF() {
// Using jsPDF with autoTable plugin
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
// Add title
doc.setFontSize(18);
doc.text('Sens Gruppen Hardware Inventory', 14, 22);
doc.setFontSize(12);
doc.text(`Generated on ${new Date().toLocaleDateString()}`, 14, 30);
// Prepare data for the table
const headers = [
'ID', 'Type', 'Company', 'Department', 'Employee', 'Make/Model',
'Serial No', 'Purchase Date', 'Warranty Expiry', 'Price (Kr)'
];
const data = inventoryData.map(item => [
item.uniqueIdentifier,
item.type,
item.company,
item.department,
item.employeeName || '-',
`${item.make} ${item.model}`,
item.serialNumber,
formatDate(item.datePurchased),
formatDate(item.warrantyExpiration),
item.price ? item.price.toString() : '-'
]);
// Add table to PDF
doc.autoTable({
head: [headers],
body: data,
startY: 40,
styles: {
fontSize: 8,
cellPadding: 2,
overflow: 'linebreak'
},
columnStyles: {
0: { cellWidth: 15 },
1: { cellWidth: 20 },
2: { cellWidth: 25 },
3: { cellWidth: 15 },
4: { cellWidth: 20 },
5: { cellWidth: 30 },
6: { cellWidth: 20 },
7: { cellWidth: 20 },
8: { cellWidth: 20 },
9: { cellWidth: 15 }
},
margin: { top: 40 }
});
// Save the PDF
doc.save('Sens_Hardware_Inventory.pdf');
showToast('Inventory exported to PDF');
}
// Print inventory
function printInventory() {
// Create a printable version of the inventory
const printWindow = window.open('', '_blank');
let html = `
<html>
<head>
<title>Sens Gruppen Hardware Inventory</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #005b96; margin-bottom: 5px; }
.date { color: #666; margin-bottom: 20px; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th { background-color: #005b96; color: white; text-align: left; padding: 8px; }
td { border: 1px solid #ddd; padding: 8px; }
tr:nth-child(even) { background-color: #f2f2f2; }
.expired { background-color: #ffdddd; }
.warning { background-color: #fff3cd; }
</style>
</head>
<body>
<h1>Sens Gruppen Hardware Inventory</h1>
<div class="date">Generated on ${new Date().toLocaleDateString()}</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>Type</th>
<th>Company</th>
<th>Department</th>
<th>Employee</th>
<th>Make/Model</th>
<th>Serial No</th>
<th>Purchase Date</th>
<th>Warranty Expiry</th>
<th>Price (Kr)</th>
</tr>
</thead>
<tbody>
`;
// Add table rows
inventoryData.forEach(item => {
const today = new Date();
const warrantyDate = new Date(item.warrantyExpiration);
const daysLeft = Math.floor((warrantyDate - today) / (1000 * 60 * 60 * 24));
let rowClass = '';
if (daysLeft < 0) {
rowClass = 'expired';
} else if (daysLeft <= 30) {
rowClass = 'warning';
}
html += `
<tr class="${rowClass}">
<td>${item.uniqueIdentifier}</td>
<td>${item.type}</td>
<td>${item.company}</td>
<td>${item.department}</td>
<td>${item.employeeName || '-'}</td>
<td>${item.make} ${item.model}</td>
<td>${item.serialNumber}</td>
<td>${formatDate(item.datePurchased)}</td>
<td>${formatDate(item.warrantyExpiration)}</td>
<td>${item.price || '-'}</td>
</tr>
`;
});
html += `
</tbody>
</table>
<script>
window.onload = function() {
window.print();
window.close();
};
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=tlynn/hardware" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>