Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb89b434ed | |||
| ac4d125d73 | |||
| 3f6830e8c8 | |||
| 0ea5d2db26 | |||
| 41d9c0a848 | |||
| f6e3db9b4a | |||
| 7e5ee7b5ea | |||
| 2528e79895 |
+5
-2
@@ -3826,11 +3826,14 @@ def api_item_detail(item_id):
|
|||||||
{f'<p><strong>Ausgeliehen von:</strong> {html.escape(str(borrower_value))}</p>' if borrower_value and status_label == 'Ausgeliehen' else ''}
|
{f'<p><strong>Ausgeliehen von:</strong> {html.escape(str(borrower_value))}</p>' if borrower_value and status_label == 'Ausgeliehen' else ''}
|
||||||
{borrows_html}
|
{borrows_html}
|
||||||
"""
|
"""
|
||||||
|
ctx = get_tenant_context()
|
||||||
|
current_tenant_id = ctx.tenant_id if ctx else None
|
||||||
|
|
||||||
client.close()
|
client.close()
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'html': detail_html,
|
'html': detail_html,
|
||||||
'images': item.get('Images', item.get('Bilder', []))
|
'images': item.get('Images', item.get('Bilder', [])),
|
||||||
|
'tenant': str(current_tenant_id)
|
||||||
}), 200
|
}), 200
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
app.logger.error(f"Error fetching item detail: {e}")
|
app.logger.error(f"Error fetching item detail: {e}")
|
||||||
|
|||||||
@@ -450,6 +450,37 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.detail-gallery-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 15px;
|
||||||
|
margin: 15px 0 20px 0;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-image-wrapper {
|
||||||
|
background-color: #f8fafc;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 6px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-image {
|
||||||
|
max-width: 160px;
|
||||||
|
max-height: 200px;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="library-table-container" id="libraryTableContainer" data-can-edit="{{ 1 if current_permissions.actions.get('can_edit', False) else 0 }}">
|
<div class="library-table-container" id="libraryTableContainer" data-can-edit="{{ 1 if current_permissions.actions.get('can_edit', False) else 0 }}">
|
||||||
@@ -1328,43 +1359,71 @@
|
|||||||
const detailContent = document.getElementById('detailContent');
|
const detailContent = document.getElementById('detailContent');
|
||||||
const detailModal = document.getElementById('detailModal');
|
const detailModal = document.getElementById('detailModal');
|
||||||
|
|
||||||
|
// Lade-Status anzeigen und Modal öffnen
|
||||||
detailContent.innerHTML = '<p>Lade Details...</p>';
|
detailContent.innerHTML = '<p>Lade Details...</p>';
|
||||||
detailModal.style.display = 'flex';
|
detailModal.style.display = 'flex';
|
||||||
|
|
||||||
// Sicherer Zugriff auf das Item mit Fallback
|
// Daten vom Backend-API-Endpoint abrufen
|
||||||
const item = libraryItems.find(i => i._id === itemId);
|
|
||||||
let mediaHtml = '';
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
// Prüfe gängige Array-Namen aus dem Backend
|
|
||||||
const imageArray = item.Images || item.Bilder || item.images;
|
|
||||||
|
|
||||||
if (Array.isArray(imageArray) && imageArray.length > 0) {
|
|
||||||
const imagesHtml = imageArray.map(image => {
|
|
||||||
// Direkter, robuster Pfad zur Upload-Route
|
|
||||||
const imageSrc = image.startsWith('/uploads/') || image.startsWith('http')
|
|
||||||
? image
|
|
||||||
: `/uploads/${image}`;
|
|
||||||
|
|
||||||
return `<img src="${imageSrc}" alt="${escapeHtml(item.Name || 'Medium')}" class="item-image" style="width: 120px; height: 120px; object-fit: cover; border-radius: 8px; border: 1px solid #ddd; margin-right: 15px; margin-bottom: 15px;">`;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
mediaHtml = `<div class="detail-gallery-container" style="margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #eee; display: flex; flex-wrap: wrap;">${imagesHtml}</div>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zusätzliche Details vom Backend laden
|
|
||||||
fetch(`/api/item_detail/${itemId}`)
|
fetch(`/api/item_detail/${itemId}`)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
if (!response.ok) {
|
||||||
return response.text();
|
throw new Error('Fehler beim Laden der Artikeldetails');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
})
|
})
|
||||||
.then(html => {
|
.then(data => {
|
||||||
detailContent.innerHTML = mediaHtml + DOMPurify.sanitize(html);
|
detailContent.innerHTML = data.html;
|
||||||
|
|
||||||
|
const imageArray = data.images || [];
|
||||||
|
const thumbnailInfoMap = data.thumbnailInfo || [];
|
||||||
|
|
||||||
|
if (Array.isArray(imageArray) && imageArray.length > 0) {
|
||||||
|
const imagesHtml = imageArray.map((image, index) => {
|
||||||
|
const isVideo = typeof isVideoFile === 'function' ? isVideoFile(image) : /\.(mp4|webm|ogg|mov)$/i.test(image);
|
||||||
|
const thumbnailInfo = thumbnailInfoMap[index];
|
||||||
|
|
||||||
|
if (isVideo) {
|
||||||
|
const videoSrc = image.startsWith('/uploads/') || image.startsWith('http')
|
||||||
|
? image
|
||||||
|
: `/uploads/${image}`;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="item-image-wrapper" style="width: 100%; height: auto;">
|
||||||
|
<video src="${videoSrc}" class="item-image ${index === 0 ? 'active-image' : ''}" id="modal-image-${index}" controls preload="metadata" style="width: 100%; height: auto; max-height: 300px;"></video>
|
||||||
|
</div>`;
|
||||||
|
} else {
|
||||||
|
const imageSrc = thumbnailInfo && thumbnailInfo.has_preview
|
||||||
|
? thumbnailInfo.preview_url
|
||||||
|
: (image.startsWith('/uploads/') || image.startsWith('http')
|
||||||
|
? image
|
||||||
|
: `/uploads/${image}`);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="item-image-wrapper">
|
||||||
|
<img src="${imageSrc}"
|
||||||
|
alt="Buchcover / Bild"
|
||||||
|
class="item-image ${index === 0 ? 'active-image' : ''}"
|
||||||
|
id="modal-image-${index}"
|
||||||
|
loading="lazy"
|
||||||
|
onload="console.log('Bild geladen:', '${imageSrc}')"
|
||||||
|
onerror="console.error('FEHLER beim Laden des Bildes im DOM:', '${imageSrc}')">
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
const mediaHtml = `<div class="detail-gallery-container">${imagesHtml}</div>`;
|
||||||
|
|
||||||
|
const h2Tag = detailContent.querySelector('h2');
|
||||||
|
if (h2Tag) {
|
||||||
|
h2Tag.insertAdjacentHTML('afterend', mediaHtml);
|
||||||
|
} else {
|
||||||
|
detailContent.insertAdjacentHTML('afterbegin', mediaHtml);
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(error => {
|
||||||
console.error('Error loading detail:', err);
|
console.error('Error fetching item detail:', error);
|
||||||
detailContent.innerHTML = '<p style="color: red;">Entschuldigung, die Details konnten nicht geladen werden.</p>';
|
detailContent.innerHTML = '<p style="color: red;">Fehler beim Laden der Artikeldetails.</p>';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user