document.addEventListener('alpine:init', () => { Alpine.data("validatePage", () => ({ // validate page specific unvalidatedList: [], isLoading: false, isValidating: false, // Date filter - missing in original code! filter: { date1: "", date2: "" }, filterTable: "", // Sorting & Pagination (shared with dashboard) sortCol: 'REQDATE', sortAsc: false, currentPage: 1, pageSize: 30, sort(col) { if (this.sortCol === col) { this.sortAsc = !this.sortAsc; } else { this.sortCol = col; this.sortAsc = true; } }, nextPage() { if (this.currentPage < this.unvalidatedTotalPages) this.currentPage++; }, prevPage() { if (this.currentPage > 1) this.currentPage--; }, get unvalidatedTotalPages() { return Math.ceil(this.unvalidatedFiltered.length / this.pageSize) || 1; }, get unvalidatedSorted() { return this.unvalidatedFiltered.slice().sort((a, b) => { let modifier = this.sortAsc ? 1 : -1; if (a[this.sortCol] < b[this.sortCol]) return -1 * modifier; if (a[this.sortCol] > b[this.sortCol]) return 1 * modifier; return 0; }); }, get unvalidatedPaginated() { const start = (this.currentPage - 1) * this.pageSize; const end = start + this.pageSize; return this.unvalidatedSorted.slice(start, end); }, get unvalidatedFiltered() { if (!this.filterTable) return this.unvalidatedList; const searchTerm = this.filterTable.toLowerCase(); return this.unvalidatedList.filter(item => Object.values(item).some(value => String(value).toLowerCase().includes(searchTerm) ) ); }, get unvalidatedCount() { return this.unvalidatedList.length; }, init() { this.today = new Date().toISOString().slice(0, 10); this.filter.date1 = this.today; this.filter.date2 = this.today; this.$watch('filterTable', () => { this.currentPage = 1; }); // Auto-fetch on page load this.fetchUnvalidated(); // Keyboard shortcuts for dialog document.addEventListener('keydown', (e) => { if (this.isDialogValOpen) { // N key - skip to next if (e.key === 'n' || e.key === 'N') { if (!e.target.closest('input, textarea, button')) { e.preventDefault(); this.skipToNext(); } } } }); }, fetchUnvalidated() { this.isLoading = true; this.unvalidatedList = []; let param = new URLSearchParams(this.filter).toString(); fetch(`${BASEURL}/api/validate/unvalidated?${param}`, { method: 'GET', headers: { 'Content-Type': 'application/json' }, }).then(res => res.json()).then(data => { this.unvalidatedList = data.data ?? []; }).finally(() => { this.isLoading = false; }); }, resetUnvalidated() { this.filter.date1 = this.today; this.filter.date2 = this.today; this.fetchUnvalidated(); }, /* * validate dialog methods */ valAccessnumber: null, valItem: null, currentIndex: 0, isDialogValOpen: false, isIframeLoaded: false, validationDelayRemaining: 0, validationTimer: null, toast: { show: false, message: '', type: 'success' }, showToast(message, type = 'success') { this.toast = { show: true, message, type }; setTimeout(() => { this.toast.show = false; }, 2000); }, onIframeLoad() { this.isIframeLoaded = true; this.validationDelayRemaining = 1; this.validationTimer = setInterval(() => { this.validationDelayRemaining--; if (this.validationDelayRemaining <= 0) { clearInterval(this.validationTimer); this.validationTimer = null; } }, 1000); }, openValDialogByIndex(index) { const filtered = this.unvalidatedFiltered; if (index < 0 || index >= filtered.length) { this.showToast('No more requests', 'error'); this.closeValDialog(); return; } // Reset iframe loading state this.isIframeLoaded = false; this.validationDelayRemaining = 0; if (this.validationTimer) { clearInterval(this.validationTimer); this.validationTimer = null; } const item = filtered[index]; this.currentIndex = index; this.valAccessnumber = item.SP_ACCESSNUMBER; this.valItem = item; this.isDialogValOpen = true; setTimeout(() => { const btn = document.getElementById('validate-btn'); if (btn) btn.focus(); }, 50); }, openValDialog(accessnumber) { // Find index by accessnumber const filtered = this.unvalidatedFiltered; const index = filtered.findIndex(item => item.SP_ACCESSNUMBER === accessnumber); if (index !== -1) { this.openValDialogByIndex(index); } else { this.openValDialogByIndex(0); } }, closeValDialog() { this.isDialogValOpen = false; this.valAccessnumber = null; this.valItem = null; if (this.validationTimer) { clearInterval(this.validationTimer); this.validationTimer = null; } this.validationDelayRemaining = 0; this.isIframeLoaded = false; }, skipToNext() { const nextIndex = (this.currentIndex + 1) % this.unvalidatedFiltered.length; this.closeValDialog(); // Use setTimeout for reliable focus after dialog re-renders setTimeout(() => this.openValDialogByIndex(nextIndex), 50); }, validate(accessnumber, userid) { if (!this.isIframeLoaded || this.validationDelayRemaining > 0) { this.showToast('Please wait for the report to load', 'error'); return; } if (!confirm(`Validate request ${accessnumber}?`)) return; this.isValidating = true; fetch(`${BASEURL}/api/requests/validate/${accessnumber}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userid: `${userid}` }) }).then(response => response.json()).then(data => { console.log(data); if (data.val === 2) { this.showToast(`Validated (val2): ${data.accessnumber} - PDF queued`); // Trigger PDF auto-generation after val2 fetch(`${BASEURL}/report/${data.accessnumber}/pdf`).then(res => res.json()).then(pdfData => { if (pdfData.success) { console.log('PDF generation queued:', pdfData.jobId); } else { console.error('PDF generation failed:', pdfData.error); } }).catch(err => { console.error('PDF generation request failed:', err); }); } else { this.showToast(`Validated: ${accessnumber}`); } this.unvalidatedList = this.unvalidatedList.filter( item => item.SP_ACCESSNUMBER !== accessnumber ); const filteredLength = this.unvalidatedFiltered.length; if (filteredLength > 0) { const nextIndex = Math.min(this.currentIndex, filteredLength - 1); this.closeValDialog(); setTimeout(() => this.openValDialogByIndex(nextIndex), 50); } else { this.closeValDialog(); this.showToast('All requests validated!'); } if (data.message && data.message.includes('already validate')) { alert(data.message); } }).catch(() => { this.showToast('Validation failed', 'error'); }).finally(() => { this.isValidating = false; }); }, getPreviewUrl() { return `report/${this.valAccessnumber}`; }, })); });