diff --git a/public/js/pages/WarehouseShippingNote/WarehouseShippingNote.js b/public/js/pages/WarehouseShippingNote/WarehouseShippingNote.js index 93196dbcd..f53530c94 100644 --- a/public/js/pages/WarehouseShippingNote/WarehouseShippingNote.js +++ b/public/js/pages/WarehouseShippingNote/WarehouseShippingNote.js @@ -608,7 +608,7 @@ document.addEventListener('DOMContentLoaded', () => { top: '10px', left: '50%', transform: 'translateX(-50%)', - zIndex: '10000', + zIndex: '98', padding: '8px 16px', backgroundColor: '#f44336', color: 'white', @@ -630,7 +630,7 @@ document.addEventListener('DOMContentLoaded', () => { console.error('Logout request failed:', error); window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.'); } finally { - setTimeout(() => window.location.reload(), 1000); + setTimeout(() => window.location.reload(), 400); } }; diff --git a/public/plugins/vue/tt-components/tt-file-gallery.js b/public/plugins/vue/tt-components/tt-file-gallery.js index b5f5c82fb..627d0d111 100644 --- a/public/plugins/vue/tt-components/tt-file-gallery.js +++ b/public/plugins/vue/tt-components/tt-file-gallery.js @@ -1,20 +1,21 @@ Vue.component('tt-fullscreen-viewer', { props: { - item: {type: Object, required: true}, - url: {type: String, default: null}, - items: {type: Array, default: () => []}, - initialIndex: {type: Number, default: -1}, + item: { type: Object, required: true }, + url: { type: String, default: null }, + items: { type: Array, default: () => [] }, + initialIndex: { type: Number, default: -1 }, }, data: () => ({ currentItem: null, currentImageIndex: -1, zoom: 1, - pan: {x: 0, y: 0}, + pan: { x: 0, y: 0 }, isPanning: false, - panStart: {x: 0, y: 0}, + panStart: { x: 0, y: 0 }, lastPinchDist: 0, - // NEW: State to track if the content is loading isLoading: true, + // NEW: Flag to determine if running as a PWA/standalone app + isStandalone: false, }), computed: { contentSrc() { @@ -40,6 +41,16 @@ Vue.component('tt-fullscreen-viewer', { return this.currentItem?.fileId ? `/File/download?id=${this.currentItem.fileId}` : '#'; } }, + // NEW: Watcher to trigger PDF.js rendering when item changes in standalone mode + watch: { + currentItem(newItem) { + if (this.isPdf(newItem) && this.isStandalone) { + this.$nextTick(() => { + this.renderPdfWithJs(this.contentSrc); + }); + } + } + }, methods: { isImage: file => file?.mimetype?.startsWith('image/'), isPdf: file => file?.mimetype === 'application/pdf', @@ -52,7 +63,6 @@ Vue.component('tt-fullscreen-viewer', { navigateImage(direction) { const newIndex = this.currentImageIndex + direction; if (newIndex >= 0 && newIndex < this.items.length) { - // NEW: Reset loading state before loading the new image this.isLoading = true; this.currentImageIndex = newIndex; this.currentItem = this.items[newIndex]; @@ -63,20 +73,14 @@ Vue.component('tt-fullscreen-viewer', { e.stopPropagation(); if (!this.currentItem) return; switch (e.key) { - case 'Escape': - this.closeViewer(); - break; - case 'ArrowLeft': - this.isViewingImage && this.navigateImage(-1); - break; - case 'ArrowRight': - this.isViewingImage && this.navigateImage(1); - break; + case 'Escape': this.closeViewer(); break; + case 'ArrowLeft': this.isViewingImage && this.navigateImage(-1); break; + case 'ArrowRight': this.isViewingImage && this.navigateImage(1); break; } }, resetZoomAndPan() { this.zoom = 1; - this.pan = {x: 0, y: 0}; + this.pan = { x: 0, y: 0 }; this.isPanning = false; }, handleWheel(e) { @@ -103,14 +107,86 @@ Vue.component('tt-fullscreen-viewer', { onTouchStart(e) {}, onTouchMove(e) {}, onTouchEnd(e) {}, + + // NEW: Method to dynamically load the PDF.js library + loadPdfJsScript() { + return new Promise((resolve, reject) => { + if (document.getElementById('pdfjs-script')) { + // Script tag exists, check if library is loaded + if (window.pdfjsLib) { + resolve(); + } else { + // Tag exists but script not loaded yet, wait for it + document.getElementById('pdfjs-script').addEventListener('load', () => resolve()); + document.getElementById('pdfjs-script').addEventListener('error', (e) => reject(e)); + } + return; + } + + const script = document.createElement('script'); + script.id = 'pdfjs-script'; + script.src = 'https://mozilla.github.io/pdf.js/build/pdf.js'; // CDN URL + script.onload = () => resolve(); + script.onerror = (err) => { + console.error("Failed to load PDF.js script.", err); + reject(new Error("PDF.js script could not be loaded.")); + }; + document.head.appendChild(script); + }); + }, + + // NEW: Method to render PDF onto a canvas using PDF.js + async renderPdfWithJs(url) { + if (!url) return; + this.isLoading = true; + + try { + await this.loadPdfJsScript(); + + pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://mozilla.github.io/pdf.js/build/pdf.worker.js'; + + const pdf = await pdfjsLib.getDocument(url).promise; + // For simplicity, we render the first page. Add pagination controls as needed. + const page = await pdf.getPage(1); + + const canvas = this.$refs.pdfCanvas; + if (!canvas) return; // Exit if canvas is not available + + const context = canvas.getContext('2d'); + const viewport = page.getViewport({ scale: 1.5 }); + + canvas.height = viewport.height; + canvas.width = viewport.width; + + await page.render({ canvasContext: context, viewport: viewport }).promise; + + } catch (error) { + console.error('Error rendering PDF with PDF.js:', error); + // Optionally show an error message to the user + } finally { + this.onContentLoad(); // Use existing method to hide loader + } + }, }, created() { this.currentItem = this.item; this.currentImageIndex = this.initialIndex; + + // NEW: Check for standalone mode on component creation + if (typeof window !== 'undefined' && window.matchMedia) { + this.isStandalone = window.matchMedia('(display-mode: standalone)').matches; + } }, mounted() { document.body.style.overflow = 'hidden'; - this.$nextTick(() => this.$refs.viewer?.focus()); + this.$nextTick(() => { + this.$refs.viewer?.focus(); + + // NEW: Trigger initial PDF render on mount if in standalone mode + if (this.isPdf(this.currentItem) && this.isStandalone) { + this.renderPdfWithJs(this.contentSrc); + } + }); }, beforeDestroy() { document.body.style.overflow = ''; @@ -119,8 +195,7 @@ Vue.component('tt-fullscreen-viewer', { template: `