Harshit Ghosh
feat: implement robust security middleware, authentication system, and frontend UI components
e4fd6e0 | (function () { | |
| function initUserMenu() { | |
| var menu = document.querySelector('.user-menu'); | |
| var toggleButton = document.querySelector('[data-user-menu-toggle="true"]'); | |
| var dropdown = document.getElementById('userMenuDropdown'); | |
| if (!menu || !toggleButton || !dropdown) { | |
| return; | |
| } | |
| function closeMenu() { | |
| dropdown.classList.remove('active'); | |
| } | |
| function toggleMenu(event) { | |
| if (event) { | |
| event.preventDefault(); | |
| event.stopPropagation(); | |
| } | |
| dropdown.classList.toggle('active'); | |
| } | |
| toggleButton.addEventListener('click', toggleMenu); | |
| document.addEventListener('click', function (event) { | |
| if (!menu.contains(event.target)) { | |
| closeMenu(); | |
| } | |
| }); | |
| } | |
| function initPasswordModal() { | |
| var openButton = document.querySelector('.js-open-password-modal'); | |
| var closeButtons = document.querySelectorAll('.js-close-password-modal'); | |
| var modal = document.querySelector('.js-password-modal'); | |
| var form = document.getElementById('changePasswordForm'); | |
| var message = document.getElementById('passwordMessage'); | |
| if (!openButton || !closeButtons.length || !modal || !form || !message) { | |
| return; | |
| } | |
| function openModal() { | |
| modal.style.display = 'block'; | |
| form.reset(); | |
| message.innerHTML = ''; | |
| } | |
| function closeModal() { | |
| modal.style.display = 'none'; | |
| } | |
| openButton.addEventListener('click', openModal); | |
| closeButtons.forEach(function (button) { | |
| button.addEventListener('click', closeModal); | |
| }); | |
| document.addEventListener('click', function (event) { | |
| if (event.target === modal) { | |
| closeModal(); | |
| } | |
| }); | |
| form.addEventListener('submit', async function (event) { | |
| event.preventDefault(); | |
| var currentPassword = document.getElementById('currentPassword').value; | |
| var newPassword = document.getElementById('newPassword').value; | |
| var confirmPassword = document.getElementById('confirmPassword').value; | |
| var endpoint = form.dataset.changePasswordUrl; | |
| if (newPassword !== confirmPassword) { | |
| message.innerHTML = '<div class="alert alert-error">Passwords do not match</div>'; | |
| return; | |
| } | |
| try { | |
| var response = await fetch(endpoint, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| current_password: currentPassword, | |
| new_password: newPassword, | |
| confirm_password: confirmPassword | |
| }) | |
| }); | |
| var data = await response.json(); | |
| if (response.ok) { | |
| message.innerHTML = '<div class="alert alert-success">' + data.message + '</div>'; | |
| setTimeout(closeModal, 2000); | |
| } else { | |
| message.innerHTML = '<div class="alert alert-error">' + (data.error || 'Unable to update password') + '</div>'; | |
| } | |
| } catch (error) { | |
| message.innerHTML = '<div class="alert alert-error">An error occurred</div>'; | |
| } | |
| }); | |
| } | |
| function initUploadPage() { | |
| var tabs = document.querySelectorAll('.upload-tab'); | |
| var panels = document.querySelectorAll('.tab-panel'); | |
| if (!tabs.length || !panels.length) { | |
| return; | |
| } | |
| tabs.forEach(function (tab) { | |
| tab.addEventListener('click', function () { | |
| tabs.forEach(function (item) { | |
| item.classList.remove('active'); | |
| }); | |
| panels.forEach(function (panel) { | |
| panel.classList.remove('active'); | |
| }); | |
| tab.classList.add('active'); | |
| var target = document.getElementById('tab-' + tab.dataset.tab); | |
| if (target) { | |
| target.classList.add('active'); | |
| } | |
| }); | |
| }); | |
| function wireDropzone(options) { | |
| var zone = document.getElementById(options.zoneId); | |
| var input = document.getElementById(options.inputId); | |
| var info = document.getElementById(options.infoId); | |
| var label = document.getElementById(options.labelId); | |
| var clearButton = document.querySelector(options.clearSel); | |
| var submit = document.getElementById(options.submitId); | |
| var form = document.getElementById(options.formId); | |
| var overlay = document.getElementById(options.overlayId); | |
| if (!zone || !input || !info || !label || !submit) { | |
| return; | |
| } | |
| function showFiles(files) { | |
| var validFiles = []; | |
| for (var i = 0; i < files.length; i++) { | |
| var name = files[i].name.toLowerCase(); | |
| if (name.endsWith('.dcm') || name.endsWith('.zip')) { | |
| validFiles.push(files[i]); | |
| } | |
| } | |
| if (!validFiles.length) { | |
| return; | |
| } | |
| if (options.multi) { | |
| var totalSizeMB = 0; | |
| for (var j = 0; j < validFiles.length; j++) { | |
| totalSizeMB += validFiles[j].size / (1024 * 1024); | |
| } | |
| label.textContent = validFiles.length + ' file' + (validFiles.length > 1 ? 's' : '') + ' (' + totalSizeMB.toFixed(1) + ' MB)'; | |
| } else { | |
| label.textContent = validFiles[0].name; | |
| } | |
| info.style.display = 'flex'; | |
| zone.style.display = 'none'; | |
| submit.disabled = false; | |
| } | |
| function reset() { | |
| input.value = ''; | |
| info.style.display = 'none'; | |
| zone.style.display = 'flex'; | |
| submit.disabled = true; | |
| } | |
| zone.addEventListener('click', function () { | |
| input.click(); | |
| }); | |
| zone.addEventListener('dragover', function (event) { | |
| event.preventDefault(); | |
| zone.classList.add('dragover'); | |
| }); | |
| zone.addEventListener('dragleave', function () { | |
| zone.classList.remove('dragover'); | |
| }); | |
| zone.addEventListener('drop', function (event) { | |
| event.preventDefault(); | |
| zone.classList.remove('dragover'); | |
| if (event.dataTransfer.files.length) { | |
| input.files = event.dataTransfer.files; | |
| showFiles(event.dataTransfer.files); | |
| } | |
| }); | |
| input.addEventListener('change', function () { | |
| if (input.files.length) { | |
| showFiles(input.files); | |
| } | |
| }); | |
| if (clearButton) { | |
| clearButton.addEventListener('click', reset); | |
| } | |
| if (form && overlay) { | |
| form.addEventListener('submit', function () { | |
| overlay.style.display = 'flex'; | |
| submit.disabled = true; | |
| }); | |
| } | |
| } | |
| wireDropzone({ | |
| zoneId: 'dropzoneSingle', | |
| inputId: 'singleInput', | |
| infoId: 'singleInfo', | |
| labelId: 'singleFileName', | |
| clearSel: '.js-clear-single', | |
| submitId: 'singleSubmit', | |
| formId: 'singleForm', | |
| overlayId: 'singleOverlay', | |
| multi: false | |
| }); | |
| wireDropzone({ | |
| zoneId: 'dropzoneMulti', | |
| inputId: 'multiInput', | |
| infoId: 'multiInfo', | |
| labelId: 'multiFileName', | |
| clearSel: '.js-clear-multi', | |
| submitId: 'multiSubmit', | |
| formId: 'multiForm', | |
| overlayId: 'multiOverlay', | |
| multi: true | |
| }); | |
| var dirInput = document.getElementById('dirPath'); | |
| var dirSubmit = document.getElementById('dirSubmit'); | |
| if (dirInput && dirSubmit) { | |
| function checkDir() { | |
| dirSubmit.disabled = !dirInput.value.trim(); | |
| } | |
| dirInput.addEventListener('input', checkDir); | |
| checkDir(); | |
| } | |
| } | |
| function initBatchProgress() { | |
| var page = document.querySelector('.batch-page'); | |
| if (!page) { | |
| return; | |
| } | |
| var statusUrl = page.dataset.statusUrl; | |
| var reportsUrl = page.dataset.reportsUrl; | |
| var pollMs = 1000; | |
| var title = document.getElementById('batchTitle'); | |
| var subtitle = document.getElementById('batchSubtitle'); | |
| var fill = document.getElementById('progressFill'); | |
| var pctLabel = document.getElementById('progressPct'); | |
| var currentFile = document.getElementById('currentFile'); | |
| var statTotal = document.getElementById('statTotal'); | |
| var statProc = document.getElementById('statProcessed'); | |
| var statOK = document.getElementById('statSucceeded'); | |
| var statFail = document.getElementById('statFailed'); | |
| var feedPanel = document.getElementById('feedPanel'); | |
| var feedList = document.getElementById('batchFeed'); | |
| var donePanel = document.getElementById('donePanel'); | |
| var doneSummary = document.getElementById('doneSummary'); | |
| var failPanel = document.getElementById('failPanel'); | |
| var failList = document.getElementById('failList'); | |
| var prevIds = []; | |
| if (!statusUrl || !title || !subtitle || !fill || !pctLabel || !currentFile || !statTotal || !statProc || !statOK || !statFail || !feedPanel || !feedList || !donePanel || !doneSummary || !failPanel || !failList) { | |
| return; | |
| } | |
| function poll() { | |
| fetch(statusUrl) | |
| .then(function (response) { | |
| return response.json(); | |
| }) | |
| .then(function (data) { | |
| var pct = data.total > 0 ? Math.round(data.processed / data.total * 100) : 0; | |
| statTotal.textContent = data.total; | |
| statProc.textContent = data.processed; | |
| statOK.textContent = data.succeeded; | |
| statFail.textContent = data.failed_count; | |
| fill.style.width = pct + '%'; | |
| pctLabel.textContent = pct + '%'; | |
| if (data.current_file) { | |
| currentFile.textContent = 'Processing: ' + data.current_file; | |
| } else { | |
| currentFile.textContent = ''; | |
| } | |
| if (data.image_ids && data.image_ids.length) { | |
| feedPanel.style.display = 'block'; | |
| data.image_ids.forEach(function (imageId) { | |
| if (prevIds.indexOf(imageId) === -1) { | |
| prevIds.push(imageId); | |
| var li = document.createElement('li'); | |
| var link = document.createElement('a'); | |
| link.href = '/case/' + imageId; | |
| link.textContent = imageId; | |
| li.appendChild(link); | |
| feedList.insertBefore(li, feedList.firstChild); | |
| while (feedList.children.length > 20) { | |
| feedList.removeChild(feedList.lastChild); | |
| } | |
| } | |
| }); | |
| } | |
| if (data.status === 'completed' || data.status === 'failed') { | |
| title.textContent = 'Batch Complete'; | |
| subtitle.textContent = ''; | |
| donePanel.style.display = 'block'; | |
| doneSummary.textContent = data.succeeded + ' of ' + data.total + ' files processed successfully' + (data.failed_count > 0 ? ', ' + data.failed_count + ' failed' : '') + '.'; | |
| if (data.failed_ids && data.failed_ids.length) { | |
| failPanel.style.display = 'block'; | |
| data.failed_ids.forEach(function (failedId) { | |
| var li = document.createElement('li'); | |
| li.textContent = failedId; | |
| failList.appendChild(li); | |
| }); | |
| } | |
| if (reportsUrl) { | |
| return; | |
| } | |
| return; | |
| } | |
| setTimeout(poll, pollMs); | |
| }) | |
| .catch(function () { | |
| setTimeout(poll, pollMs * 3); | |
| }); | |
| } | |
| poll(); | |
| } | |
| function initPages() { | |
| initUserMenu(); | |
| initPasswordModal(); | |
| initUploadPage(); | |
| initBatchProgress(); | |
| } | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', initPages); | |
| } else { | |
| initPages(); | |
| } | |
| })(); | |