| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Medication Leftovers Control System</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> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| primary: '#3b82f6', |
| secondary: '#10b981', |
| danger: '#ef4444', |
| } |
| } |
| } |
| } |
| </script> |
| <style> |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(10px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| .fade-in { |
| animation: fadeIn 0.3s ease-out forwards; |
| } |
| |
| .signature-pad { |
| border: 1px dashed #ccc; |
| border-radius: 4px; |
| background-color: #f9fafb; |
| } |
| |
| input:focus, select:focus, textarea:focus { |
| outline: 2px solid #3b82f6; |
| outline-offset: 2px; |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 min-h-screen"> |
| <div class="container mx-auto px-4 py-8 max-w-4xl"> |
| |
| <header class="mb-8 text-center"> |
| <h1 class="text-3xl font-bold text-primary mb-2"> |
| <i class="fas fa-pills mr-2"></i>Medication Leftovers Control |
| </h1> |
| <p class="text-gray-600">Track and manage medication leftovers with expiration dates and stability information</p> |
| </header> |
| |
| |
| <div class="bg-white rounded-lg shadow-md p-6 mb-8"> |
| <div class="flex flex-wrap gap-4 mb-6"> |
| <button id="scanBtn" class="flex items-center px-4 py-2 bg-primary text-white rounded-lg hover:bg-blue-700 transition"> |
| <i class="fas fa-barcode mr-2"></i> Scan Barcode |
| </button> |
| <button id="manualBtn" class="flex items-center px-4 py-2 bg-secondary text-white rounded-lg hover:bg-emerald-700 transition"> |
| <i class="fas fa-keyboard mr-2"></i> Manual Entry |
| </button> |
| <button id="clearBtn" class="flex items-center px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition"> |
| <i class="fas fa-trash-alt mr-2"></i> Clear Form |
| </button> |
| <button id="saveBtn" class="flex items-center px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition ml-auto"> |
| <i class="fas fa-save mr-2"></i> Save Record |
| </button> |
| </div> |
| |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> |
| |
| <div> |
| <div class="mb-4"> |
| <label for="medication" class="block text-sm font-medium text-gray-700 mb-1">Medication Name *</label> |
| <div class="relative"> |
| <input type="text" id="medication" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary" placeholder="Enter medication name"> |
| <div id="medSuggestions" class="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-lg shadow-lg hidden"></div> |
| </div> |
| </div> |
| |
| <div class="mb-4"> |
| <label for="batch" class="block text-sm font-medium text-gray-700 mb-1">Batch Number *</label> |
| <input type="text" id="batch" class="w-full px-4 py-2 border border-gray-300 rounded-lg" placeholder="Enter batch number"> |
| </div> |
| |
| <div class="mb-4"> |
| <label for="expiration" class="block text-sm font-medium text-gray-700 mb-1">Expiration Date *</label> |
| <input type="date" id="expiration" class="w-full px-4 py-2 border border-gray-300 rounded-lg"> |
| </div> |
| |
| <div class="mb-4"> |
| <label for="quantity" class="block text-sm font-medium text-gray-700 mb-1">Quantity Left *</label> |
| <div class="flex"> |
| <input type="number" id="quantity" class="w-3/4 px-4 py-2 border border-gray-300 rounded-l-lg" placeholder="Enter quantity"> |
| <select id="unit" class="w-1/4 px-2 py-2 border border-gray-300 rounded-r-lg bg-gray-50"> |
| <option value="tablets">tablets</option> |
| <option value="ml">ml</option> |
| <option value="mg">mg</option> |
| <option value="vials">vials</option> |
| <option value="ampoules">ampoules</option> |
| </select> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div> |
| <div class="mb-4"> |
| <label for="productionDate" class="block text-sm font-medium text-gray-700 mb-1">Production Date</label> |
| <input type="date" id="productionDate" class="w-full px-4 py-2 border border-gray-300 rounded-lg"> |
| </div> |
| |
| <div class="mb-4"> |
| <label for="stability" class="block text-sm font-medium text-gray-700 mb-1">Stability Information</label> |
| <select id="stability" class="w-full px-4 py-2 border border-gray-300 rounded-lg"> |
| <option value="">Select stability</option> |
| <option value="room_temp">Room temperature (15-25°C)</option> |
| <option value="refrigerated">Refrigerated (2-8°C)</option> |
| <option value="frozen">Frozen (-20°C or below)</option> |
| <option value="protected_light">Protected from light</option> |
| <option value="special">Special conditions (see notes)</option> |
| </select> |
| </div> |
| |
| <div class="mb-4"> |
| <label for="useExpiration" class="block text-sm font-medium text-gray-700 mb-1">Expiration Date After Opening</label> |
| <input type="date" id="useExpiration" class="w-full px-4 py-2 border border-gray-300 rounded-lg"> |
| </div> |
| |
| <div class="mb-4"> |
| <label for="laboratory" class="block text-sm font-medium text-gray-700 mb-1">Manufacturer/Laboratory</label> |
| <input type="text" id="laboratory" class="w-full px-4 py-2 border border-gray-300 rounded-lg" placeholder="Enter manufacturer name"> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="mb-6"> |
| <label for="notes" class="block text-sm font-medium text-gray-700 mb-1">Additional Notes</label> |
| <textarea id="notes" rows="3" class="w-full px-4 py-2 border border-gray-300 rounded-lg" placeholder="Any special instructions or notes about this medication"></textarea> |
| </div> |
| |
| |
| <div> |
| <label class="block text-sm font-medium text-gray-700 mb-1">Responsible Person Signature</label> |
| <div class="signature-pad p-4 mb-2"> |
| <canvas id="signatureCanvas" width="100%" height="100"></canvas> |
| </div> |
| <div class="flex justify-between"> |
| <button id="clearSignature" class="text-sm text-gray-500 hover:text-gray-700"> |
| <i class="fas fa-eraser mr-1"></i> Clear Signature |
| </button> |
| <div class="text-sm text-gray-500"> |
| <i class="fas fa-info-circle mr-1"></i> Sign above to confirm |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="bg-white rounded-lg shadow-md p-6"> |
| <h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center"> |
| <i class="fas fa-history mr-2"></i> Recent Records |
| </h2> |
| <div class="overflow-x-auto"> |
| <table class="min-w-full divide-y divide-gray-200"> |
| <thead class="bg-gray-50"> |
| <tr> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Medication</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Batch</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Expiration</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Quantity</th> |
| <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th> |
| </tr> |
| </thead> |
| <tbody id="recordsTable" class="bg-white divide-y divide-gray-200"> |
| |
| </tbody> |
| </table> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| const medicationDatabase = [ |
| { name: "Amoxicillin 500mg", commonBatch: "AMX2023", stability: "room_temp", lab: "PharmaCorp" }, |
| { name: "Ibuprofen 200mg", commonBatch: "IBU2305", stability: "room_temp", lab: "MediHealth" }, |
| { name: "Insulin Glargine", commonBatch: "INS456", stability: "refrigerated", lab: "BioSolutions" }, |
| { name: "Paracetamol 500mg", commonBatch: "PARA789", stability: "room_temp", lab: "GenericMeds" }, |
| { name: "Loratadine 10mg", commonBatch: "LORA123", stability: "room_temp", lab: "AllerFree" }, |
| { name: "Omeprazole 20mg", commonBatch: "OME456", stability: "room_temp", lab: "GastroPharm" } |
| ]; |
| |
| |
| const today = new Date(); |
| const todayFormatted = today.toISOString().split('T')[0]; |
| document.getElementById('productionDate').value = todayFormatted; |
| |
| |
| const canvas = document.getElementById('signatureCanvas'); |
| const ctx = canvas.getContext('2d'); |
| let isDrawing = false; |
| let lastX = 0; |
| let lastY = 0; |
| |
| |
| function resizeCanvas() { |
| const container = canvas.parentElement; |
| canvas.width = container.offsetWidth; |
| canvas.height = 100; |
| } |
| resizeCanvas(); |
| window.addEventListener('resize', resizeCanvas); |
| |
| |
| canvas.addEventListener('mousedown', (e) => { |
| isDrawing = true; |
| [lastX, lastY] = [e.offsetX, e.offsetY]; |
| }); |
| |
| canvas.addEventListener('mousemove', (e) => { |
| if (!isDrawing) return; |
| ctx.beginPath(); |
| ctx.moveTo(lastX, lastY); |
| ctx.lineTo(e.offsetX, e.offsetY); |
| ctx.strokeStyle = '#000'; |
| ctx.lineWidth = 2; |
| ctx.stroke(); |
| [lastX, lastY] = [e.offsetX, e.offsetY]; |
| }); |
| |
| canvas.addEventListener('mouseup', () => isDrawing = false); |
| canvas.addEventListener('mouseout', () => isDrawing = false); |
| |
| |
| canvas.addEventListener('touchstart', (e) => { |
| e.preventDefault(); |
| const touch = e.touches[0]; |
| const mouseEvent = new MouseEvent('mousedown', { |
| clientX: touch.clientX, |
| clientY: touch.clientY |
| }); |
| canvas.dispatchEvent(mouseEvent); |
| }); |
| |
| canvas.addEventListener('touchmove', (e) => { |
| e.preventDefault(); |
| const touch = e.touches[0]; |
| const mouseEvent = new MouseEvent('mousemove', { |
| clientX: touch.clientX, |
| clientY: touch.clientY |
| }); |
| canvas.dispatchEvent(mouseEvent); |
| }); |
| |
| canvas.addEventListener('touchend', () => { |
| const mouseEvent = new MouseEvent('mouseup'); |
| canvas.dispatchEvent(mouseEvent); |
| }); |
| |
| |
| document.getElementById('clearSignature').addEventListener('click', () => { |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| }); |
| |
| |
| const medicationInput = document.getElementById('medication'); |
| const suggestionsContainer = document.getElementById('medSuggestions'); |
| |
| medicationInput.addEventListener('input', function() { |
| const input = this.value.toLowerCase(); |
| if (input.length < 2) { |
| suggestionsContainer.classList.add('hidden'); |
| return; |
| } |
| |
| const matches = medicationDatabase.filter(med => |
| med.name.toLowerCase().includes(input) |
| ); |
| |
| if (matches.length > 0) { |
| suggestionsContainer.innerHTML = ''; |
| matches.forEach(med => { |
| const div = document.createElement('div'); |
| div.className = 'px-4 py-2 hover:bg-gray-100 cursor-pointer'; |
| div.textContent = med.name; |
| div.addEventListener('click', () => { |
| medicationInput.value = med.name; |
| document.getElementById('batch').value = med.commonBatch; |
| document.getElementById('stability').value = med.stability; |
| document.getElementById('laboratory').value = med.lab; |
| suggestionsContainer.classList.add('hidden'); |
| |
| |
| const futureDate = new Date(); |
| futureDate.setFullYear(futureDate.getFullYear() + 1); |
| document.getElementById('expiration').value = futureDate.toISOString().split('T')[0]; |
| |
| |
| const useDate = new Date(); |
| useDate.setDate(useDate.getDate() + 30); |
| document.getElementById('useExpiration').value = useDate.toISOString().split('T')[0]; |
| }); |
| suggestionsContainer.appendChild(div); |
| }); |
| suggestionsContainer.classList.remove('hidden'); |
| } else { |
| suggestionsContainer.classList.add('hidden'); |
| } |
| }); |
| |
| |
| document.addEventListener('click', (e) => { |
| if (e.target !== medicationInput) { |
| suggestionsContainer.classList.add('hidden'); |
| } |
| }); |
| |
| |
| let records = [ |
| { |
| date: '2023-06-15', |
| medication: 'Amoxicillin 500mg', |
| batch: 'AMX2023', |
| expiration: '2024-06-14', |
| quantity: '15 tablets', |
| stability: 'Room temperature', |
| useExpiration: '2023-07-15', |
| laboratory: 'PharmaCorp', |
| notes: 'Keep in dry place' |
| }, |
| { |
| date: '2023-06-10', |
| medication: 'Insulin Glargine', |
| batch: 'INS456', |
| expiration: '2023-12-10', |
| quantity: '2 vials', |
| stability: 'Refrigerated', |
| useExpiration: '2023-07-10', |
| laboratory: 'BioSolutions', |
| notes: 'Do not freeze' |
| } |
| ]; |
| |
| |
| function renderRecords() { |
| const tableBody = document.getElementById('recordsTable'); |
| tableBody.innerHTML = ''; |
| |
| records.forEach((record, index) => { |
| const row = document.createElement('tr'); |
| row.className = 'fade-in'; |
| row.innerHTML = ` |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${record.date}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${record.medication}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${record.batch}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${record.expiration}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${record.quantity}</td> |
| <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> |
| <button class="text-primary hover:text-blue-700 mr-3 view-btn" data-index="${index}"> |
| <i class="fas fa-eye mr-1"></i> View |
| </button> |
| <button class="text-danger hover:text-red-700 delete-btn" data-index="${index}"> |
| <i class="fas fa-trash-alt mr-1"></i> Delete |
| </button> |
| </td> |
| `; |
| tableBody.appendChild(row); |
| }); |
| |
| |
| document.querySelectorAll('.view-btn').forEach(btn => { |
| btn.addEventListener('click', function() { |
| const index = this.getAttribute('data-index'); |
| viewRecord(index); |
| }); |
| }); |
| |
| document.querySelectorAll('.delete-btn').forEach(btn => { |
| btn.addEventListener('click', function() { |
| const index = this.getAttribute('data-index'); |
| deleteRecord(index); |
| }); |
| }); |
| } |
| |
| |
| function viewRecord(index) { |
| const record = records[index]; |
| alert(`Medication: ${record.medication}\nBatch: ${record.batch}\nExpiration: ${record.expiration}\nQuantity: ${record.quantity}\nStability: ${record.stability}\nUse Until: ${record.useExpiration}\nLab: ${record.laboratory}\nNotes: ${record.notes}`); |
| } |
| |
| |
| function deleteRecord(index) { |
| if (confirm('Are you sure you want to delete this record?')) { |
| records.splice(index, 1); |
| renderRecords(); |
| } |
| } |
| |
| |
| document.getElementById('saveBtn').addEventListener('click', function() { |
| const medication = document.getElementById('medication').value; |
| const batch = document.getElementById('batch').value; |
| const expiration = document.getElementById('expiration').value; |
| const quantity = document.getElementById('quantity').value + ' ' + document.getElementById('unit').value; |
| const productionDate = document.getElementById('productionDate').value; |
| const stability = document.getElementById('stability').options[document.getElementById('stability').selectedIndex].text; |
| const useExpiration = document.getElementById('useExpiration').value; |
| const laboratory = document.getElementById('laboratory').value; |
| const notes = document.getElementById('notes').value; |
| |
| if (!medication || !batch || !expiration || !quantity) { |
| alert('Please fill in all required fields (marked with *)'); |
| return; |
| } |
| |
| |
| const signatureEmpty = canvas.toDataURL() === document.createElement('canvas').toDataURL(); |
| if (signatureEmpty) { |
| alert('Please provide a signature to confirm the record'); |
| return; |
| } |
| |
| const newRecord = { |
| date: todayFormatted, |
| medication, |
| batch, |
| expiration, |
| quantity, |
| productionDate, |
| stability, |
| useExpiration, |
| laboratory, |
| notes |
| }; |
| |
| records.unshift(newRecord); |
| renderRecords(); |
| |
| |
| document.getElementById('clearBtn').click(); |
| |
| |
| const successMsg = document.createElement('div'); |
| successMsg.className = 'fixed bottom-4 right-4 bg-green-500 text-white px-4 py-2 rounded-lg shadow-lg flex items-center'; |
| successMsg.innerHTML = '<i class="fas fa-check-circle mr-2"></i> Record saved successfully!'; |
| document.body.appendChild(successMsg); |
| |
| setTimeout(() => { |
| successMsg.classList.add('opacity-0', 'transition-opacity', 'duration-500'); |
| setTimeout(() => successMsg.remove(), 500); |
| }, 3000); |
| }); |
| |
| |
| document.getElementById('clearBtn').addEventListener('click', function() { |
| document.getElementById('medication').value = ''; |
| document.getElementById('batch').value = ''; |
| document.getElementById('expiration').value = ''; |
| document.getElementById('quantity').value = ''; |
| document.getElementById('unit').value = 'tablets'; |
| document.getElementById('productionDate').value = todayFormatted; |
| document.getElementById('stability').value = ''; |
| document.getElementById('useExpiration').value = ''; |
| document.getElementById('laboratory').value = ''; |
| document.getElementById('notes').value = ''; |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| }); |
| |
| |
| document.getElementById('scanBtn').addEventListener('click', function() { |
| |
| const randomMed = medicationDatabase[Math.floor(Math.random() * medicationDatabase.length)]; |
| document.getElementById('medication').value = randomMed.name; |
| document.getElementById('batch').value = randomMed.commonBatch; |
| document.getElementById('stability').value = randomMed.stability; |
| document.getElementById('laboratory').value = randomMed.lab; |
| |
| |
| const futureDate = new Date(); |
| futureDate.setFullYear(futureDate.getFullYear() + 1); |
| document.getElementById('expiration').value = futureDate.toISOString().split('T')[0]; |
| |
| const useDate = new Date(); |
| useDate.setDate(useDate.getDate() + 30); |
| document.getElementById('useExpiration').value = useDate.toISOString().split('T')[0]; |
| |
| |
| document.getElementById('quantity').value = Math.floor(Math.random() * 20) + 5; |
| |
| |
| const scanAnimation = document.createElement('div'); |
| scanAnimation.className = 'fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50'; |
| scanAnimation.innerHTML = ` |
| <div class="bg-white p-6 rounded-lg text-center max-w-sm"> |
| <div class="text-4xl mb-4"> |
| <i class="fas fa-barcode animate-pulse text-primary"></i> |
| </div> |
| <h3 class="text-xl font-semibold mb-2">Scanning Barcode</h3> |
| <p class="text-gray-600 mb-4">Detected: ${randomMed.name}</p> |
| <div class="h-1 w-full bg-gray-200 rounded-full overflow-hidden"> |
| <div class="h-full bg-primary rounded-full animate-progress"></div> |
| </div> |
| </div> |
| `; |
| document.body.appendChild(scanAnimation); |
| |
| setTimeout(() => { |
| scanAnimation.classList.add('opacity-0', 'transition-opacity', 'duration-300'); |
| setTimeout(() => scanAnimation.remove(), 300); |
| }, 1500); |
| }); |
| |
| |
| renderRecords(); |
| }); |
| </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=Lechugaia/cmp" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |