Files
Aiirondev_dev bc61e87ab0 Refactor UI styles to use CSS variables for consistent theming
- Updated background colors across various templates to use CSS variables for better theming support.
- Replaced hardcoded colors with variables like `var(--ui-surface)` and `var(--ui-surface-soft)` for backgrounds.
- Adjusted text colors to use `var(--ui-text)` for improved readability and consistency.
- Enhanced the theme toggle functionality in the base template to dynamically change theme colors based on user preference.
- Ensured that all relevant templates reflect the new theming approach for a cohesive user experience.
2026-04-20 20:20:33 +02:00

817 lines
23 KiB
HTML
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!--
Copyright 2025-2026 AIIrondev
Licensed under the Inventarsystem EULA (Endbenutzer-Lizenzvertrag).
See Legal/LICENSE for the full license text.
Unauthorized commercial use, SaaS hosting, or removal of branding is prohibited.
For commercial licensing inquiries: https://github.com/AIIrondev
-->
<!--
Reset Item Modal
================
This template contains a dedicated modal window for resetting item borrowing status.
It provides detailed information about what will be reset and requires confirmation.
-->
<!-- Reset Item Modal -->
<div id="reset-item-modal" class="reset-modal-overlay" style="display: none;">
<div class="reset-modal-container">
<div class="reset-modal-header">
<h2>
<i class="reset-icon">⚠️</i>
Item-Status zurücksetzen
</h2>
<button class="reset-modal-close" onclick="closeResetModal()">&times;</button>
</div>
<div class="reset-modal-content">
<div class="reset-item-info">
<h3 id="reset-item-name">Item wird geladen...</h3>
<div class="reset-item-details">
<p><strong>ID:</strong> <span id="reset-item-id">-</span></p>
<p><strong>Ort:</strong> <span id="reset-item-location">-</span></p>
<p><strong>Aktueller Status:</strong> <span id="reset-item-status">-</span></p>
<p><strong>Aktuelle Ausleiher:</strong> <span id="reset-item-borrower">-</span></p>
</div>
</div>
<div class="reset-warning-section">
<div class="reset-warning-box">
<h4>⚠️ Achtung: Diese Aktion kann nicht rückgängig gemacht werden!</h4>
<p>Das Zurücksetzen des Item-Status wird folgende Aktionen durchführen:</p>
</div>
<div class="reset-actions-list">
<div class="reset-action-item">
<input type="checkbox" id="reset-availability" checked disabled>
<label for="reset-availability">
<strong>Item als verfügbar markieren</strong>
<span class="reset-action-desc">Das Item wird als "Verfügbar" gesetzt</span>
</label>
</div>
<div class="reset-action-item">
<input type="checkbox" id="reset-borrower" checked disabled>
<label for="reset-borrower">
<strong>Ausleiher-Informationen entfernen</strong>
<span class="reset-action-desc">Alle Benutzer-Zuordnungen werden gelöscht</span>
</label>
</div>
<div class="reset-action-item" id="reset-exemplar-section" style="display: none;">
<input type="checkbox" id="reset-exemplars" checked disabled>
<label for="reset-exemplars">
<strong>Exemplar-Status zurücksetzen</strong>
<span class="reset-action-desc">Alle Exemplar-Ausleihungen werden beendet</span>
</label>
</div>
<div class="reset-action-item" id="reset-borrowings-section">
<input type="checkbox" id="reset-borrowings" checked disabled>
<label for="reset-borrowings">
<strong>Aktive Ausleihungen beenden</strong>
<span class="reset-action-desc">Alle aktiven/geplanten Ausleihungen werden als "abgeschlossen" markiert</span>
</label>
<div id="active-borrowings-list" class="active-borrowings"></div>
</div>
</div>
</div>
<div class="reset-options-section">
<h4>Reset-Optionen:</h4>
<div class="reset-option">
<input type="checkbox" id="reset-create-log" checked>
<label for="reset-create-log">Log-Eintrag für diese Aktion erstellen</label>
</div>
<div class="reset-option">
<input type="checkbox" id="reset-notify-users">
<label for="reset-notify-users">Betroffene Benutzer benachrichtigen (falls möglich)</label>
</div>
</div>
<div class="reset-confirmation-section">
<div class="reset-confirmation-box">
<input type="checkbox" id="reset-final-confirmation">
<label for="reset-final-confirmation">
<strong>Ich bestätige, dass ich dieses Item zurücksetzen möchte</strong>
</label>
</div>
<div class="reset-reason-section">
<label for="reset-reason">Grund für das Zurücksetzen (optional):</label>
<textarea id="reset-reason" placeholder="z.B. Inkonsistenter Status, Item fälschlicherweise als ausgeliehen markiert, etc."></textarea>
</div>
</div>
</div>
<div class="reset-modal-footer">
<button class="reset-cancel-btn" onclick="closeResetModal()">
Abbrechen
</button>
<button class="reset-confirm-btn" id="reset-execute-btn" onclick="executeItemReset()" disabled>
<i class="reset-execute-icon">🔄</i>
Item zurücksetzen
</button>
</div>
</div>
</div>
<style>
/* Reset Modal Styles */
.reset-modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(3px);
animation: resetModalFadeIn 0.3s ease;
}
@keyframes resetModalFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.reset-modal-container {
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 700px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
position: relative;
animation: resetModalSlideIn 0.4s ease;
}
@keyframes resetModalSlideIn {
from {
transform: translateY(-50px) scale(0.9);
opacity: 0;
}
to {
transform: translateY(0) scale(1);
opacity: 1;
}
}
.reset-modal-header {
background: linear-gradient(135deg, #dc3545, #c82333);
color: white;
padding: 20px;
border-radius: 12px 12px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.reset-modal-header h2 {
margin: 0;
font-size: 1.4rem;
display: flex;
align-items: center;
gap: 10px;
}
.reset-icon {
font-size: 1.6rem;
}
.reset-modal-close {
background: none;
border: none;
color: white;
font-size: 2rem;
cursor: pointer;
padding: 0;
width: 40px;
height: 40px;
border-radius: 50%;
transition: background-color 0.3s ease;
}
.reset-modal-close:hover {
background-color: rgba(255, 255, 255, 0.2);
}
.reset-modal-content {
padding: 25px;
}
.reset-item-info {
background: var(--ui-surface-soft);
border-radius: 8px;
padding: 20px;
margin-bottom: 25px;
border-left: 4px solid #007bff;
}
.reset-item-info h3 {
margin: 0 0 15px 0;
color: var(--ui-text);
font-size: 1.3rem;
}
.reset-item-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
}
.reset-item-details p {
margin: 5px 0;
color: #666;
}
.reset-item-details strong {
color: var(--ui-text);
}
.reset-warning-section {
margin-bottom: 25px;
}
.reset-warning-box {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
}
.reset-warning-box h4 {
margin: 0 0 10px 0;
color: #856404;
font-size: 1.1rem;
}
.reset-warning-box p {
margin: 0;
color: #856404;
}
.reset-actions-list {
space-y: 15px;
}
.reset-action-item {
background: white;
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 15px;
margin-bottom: 10px;
transition: border-color 0.3s ease;
}
.reset-action-item:hover {
border-color: #007bff;
}
.reset-action-item input[type="checkbox"] {
margin-right: 12px;
transform: scale(1.2);
}
.reset-action-item label {
display: flex;
flex-direction: column;
cursor: pointer;
font-weight: 500;
color: var(--ui-text);
}
.reset-action-desc {
font-weight: normal;
color: #666;
font-size: 0.9rem;
margin-top: 5px;
}
.active-borrowings {
margin-top: 10px;
padding: 10px;
background: var(--ui-surface-soft);
border-radius: 5px;
font-size: 0.9rem;
}
.borrowing-entry {
padding: 5px 0;
border-bottom: 1px solid #dee2e6;
}
.borrowing-entry:last-child {
border-bottom: none;
}
.reset-options-section {
background: #f1f3f4;
border-radius: 8px;
padding: 20px;
margin-bottom: 25px;
}
.reset-options-section h4 {
margin: 0 0 15px 0;
color: var(--ui-text);
}
.reset-option {
margin-bottom: 10px;
}
.reset-option input[type="checkbox"] {
margin-right: 10px;
transform: scale(1.1);
}
.reset-option label {
color: #555;
font-weight: 500;
cursor: pointer;
}
.reset-confirmation-section {
border: 2px solid #dc3545;
border-radius: 8px;
padding: 20px;
background: #fdf2f2;
}
.reset-confirmation-box {
margin-bottom: 15px;
}
.reset-confirmation-box input[type="checkbox"] {
margin-right: 10px;
transform: scale(1.2);
}
.reset-confirmation-box label {
font-weight: bold;
color: #721c24;
cursor: pointer;
}
.reset-reason-section label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--ui-text);
}
.reset-reason-section textarea {
width: 100%;
height: 80px;
padding: 10px;
border: 1px solid #ced4da;
border-radius: 5px;
font-family: inherit;
resize: vertical;
background: white;
}
.reset-reason-section textarea:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 5px rgba(0, 123, 255, 0.3);
}
.reset-modal-footer {
padding: 20px 25px;
border-top: 1px solid #dee2e6;
display: flex;
justify-content: flex-end;
gap: 15px;
background: var(--ui-surface-soft);
border-radius: 0 0 12px 12px;
}
.reset-cancel-btn {
background: #6c757d;
color: white;
border: none;
padding: 12px 25px;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
}
.reset-cancel-btn:hover {
background: #5a6268;
transform: translateY(-1px);
}
.reset-confirm-btn {
background: #dc3545;
color: white;
border: none;
padding: 12px 25px;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.reset-confirm-btn:hover:not(:disabled) {
background: #c82333;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(220, 53, 69, 0.3);
}
.reset-confirm-btn:disabled {
background: #cccccc;
cursor: not-allowed;
transform: none;
}
.reset-execute-icon {
font-size: 1rem;
}
/* Loading states */
.reset-loading .reset-execute-icon {
animation: resetSpin 1s linear infinite;
}
@keyframes resetSpin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/* Responsive adjustments */
@media (max-width: 768px) {
.reset-modal-container {
width: 95%;
margin: 10px;
max-height: 95vh;
}
.reset-modal-content {
padding: 20px;
}
.reset-item-details {
grid-template-columns: 1fr;
}
.reset-modal-footer {
flex-direction: column;
}
.reset-cancel-btn,
.reset-confirm-btn {
width: 100%;
justify-content: center;
}
}
/* Success animation */
.reset-success {
animation: resetSuccess 0.6s ease;
}
@keyframes resetSuccess {
0% { transform: scale(1); }
50% { transform: scale(1.05); background-color: #28a745; }
100% { transform: scale(1); }
}
</style>
<script>
// Global variable to store the current item being reset
let currentResetItem = null;
/**
* Opens the reset modal for a specific item
*/
function openResetModal(itemId) {
currentResetItem = itemId;
// Show the modal
document.getElementById('reset-item-modal').style.display = 'flex';
// Load item details
loadItemDetailsForReset(itemId);
// Reset form state
resetModalFormState();
// Add event listeners
setupResetModalEventListeners();
}
/**
* Closes the reset modal
*/
function closeResetModal() {
document.getElementById('reset-item-modal').style.display = 'none';
currentResetItem = null;
resetModalFormState();
}
/**
* Loads item details for the reset modal
*/
function loadItemDetailsForReset(itemId) {
// Show loading state
document.getElementById('reset-item-name').textContent = 'Lade Item-Details...';
document.getElementById('reset-item-id').textContent = itemId;
document.getElementById('reset-item-location').textContent = 'Wird geladen...';
document.getElementById('reset-item-status').textContent = 'Wird geladen...';
document.getElementById('reset-item-borrower').textContent = 'Wird geladen...';
// Fetch item details
fetch(`/get_item/${itemId}`)
.then(response => response.json())
.then(data => {
if (data.status === 'success' && data.item) {
const item = data.item;
// Update item information
document.getElementById('reset-item-name').textContent = item.Name || 'Unbekanntes Item';
document.getElementById('reset-item-location').textContent = item.Ort || 'Kein Ort angegeben';
// Determine status
let statusText = 'Verfügbar';
let borrowerText = 'Nicht ausgeliehen';
if (!item.Verfuegbar) {
statusText = 'Ausgeliehen';
borrowerText = item.User || 'Unbekannter Benutzer';
} else if (item.ExemplareStatus && item.ExemplareStatus.length > 0) {
statusText = `${item.ExemplareStatus.length} Exemplar(e) ausgeliehen`;
const borrowers = item.ExemplareStatus.map(ex => ex.user).join(', ');
borrowerText = borrowers;
} else if (item.BorrowerInfo) {
statusText = 'Möglicherweise ausgeliehen (inkonsistent)';
borrowerText = item.BorrowerInfo.username || 'Unbekannt';
}
document.getElementById('reset-item-status').textContent = statusText;
document.getElementById('reset-item-borrower').textContent = borrowerText;
// Show/hide exemplar section
const exemplarSection = document.getElementById('reset-exemplar-section');
if (item.ExemplareStatus && item.ExemplareStatus.length > 0) {
exemplarSection.style.display = 'block';
} else {
exemplarSection.style.display = 'none';
}
// Load active borrowings
loadActiveBorrowingsForReset(itemId);
} else {
document.getElementById('reset-item-name').textContent = 'Fehler beim Laden';
document.getElementById('reset-item-status').textContent = 'Unbekannt';
document.getElementById('reset-item-borrower').textContent = 'Unbekannt';
}
})
.catch(error => {
console.error('Error loading item details:', error);
document.getElementById('reset-item-name').textContent = 'Fehler beim Laden';
});
}
/**
* Loads active borrowings for the item
*/
function loadActiveBorrowingsForReset(itemId) {
fetch(`/get_ausleihung_by_item/${itemId}`)
.then(response => response.json())
.then(data => {
const borrowingsList = document.getElementById('active-borrowings-list');
if (data.status === 'success' && data.ausleihung) {
const borrowing = data.ausleihung;
borrowingsList.innerHTML = `
<div class="borrowing-entry">
<strong>Benutzer:</strong> ${borrowing.User || 'Unbekannt'}<br>
<strong>Start:</strong> ${borrowing.Start ? new Date(borrowing.Start).toLocaleString('de-DE') : 'Unbekannt'}<br>
<strong>Status:</strong> ${borrowing.Status || 'Unbekannt'}
</div>
`;
} else {
borrowingsList.innerHTML = '<em>Keine aktiven Ausleihungen gefunden</em>';
}
})
.catch(error => {
console.error('Error loading borrowings:', error);
document.getElementById('active-borrowings-list').innerHTML = '<em>Fehler beim Laden der Ausleihungen</em>';
});
}
/**
* Resets the modal form state
*/
function resetModalFormState() {
// Reset checkboxes
document.getElementById('reset-create-log').checked = true;
document.getElementById('reset-notify-users').checked = false;
document.getElementById('reset-final-confirmation').checked = false;
// Clear reason textarea
document.getElementById('reset-reason').value = '';
// Disable execute button
document.getElementById('reset-execute-btn').disabled = true;
}
/**
* Sets up event listeners for the reset modal
*/
function setupResetModalEventListeners() {
// Confirmation checkbox listener
const confirmationCheckbox = document.getElementById('reset-final-confirmation');
const executeButton = document.getElementById('reset-execute-btn');
confirmationCheckbox.addEventListener('change', function() {
executeButton.disabled = !this.checked;
});
// ESC key to close modal
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && document.getElementById('reset-item-modal').style.display === 'flex') {
closeResetModal();
}
});
// Click outside modal to close
document.getElementById('reset-item-modal').addEventListener('click', function(event) {
if (event.target === this) {
closeResetModal();
}
});
}
/**
* Executes the item reset
*/
function executeItemReset() {
if (!currentResetItem) {
alert('Kein Item ausgewählt');
return;
}
const executeButton = document.getElementById('reset-execute-btn');
const originalText = executeButton.innerHTML;
// Show loading state
executeButton.classList.add('reset-loading');
executeButton.disabled = true;
executeButton.innerHTML = '<i class="reset-execute-icon">🔄</i> Wird zurückgesetzt...';
// Collect form data
const resetData = {
item_id: currentResetItem,
create_log: document.getElementById('reset-create-log').checked,
notify_users: document.getElementById('reset-notify-users').checked,
reason: document.getElementById('reset-reason').value.trim() || 'Item-Status Reset über Admin-Interface',
timestamp: new Date().toISOString()
};
// Send reset request
fetch(`/reset_item/${currentResetItem}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(resetData)
})
.then(response => {
if (!response.ok) {
return response.json().then(data => {
throw new Error(data.message || data.error || `HTTP error! status: ${response.status}`);
});
}
return response.json();
})
.then(data => {
executeButton.classList.remove('reset-loading');
if (data.success) {
// Show success state
executeButton.classList.add('reset-success');
executeButton.innerHTML = '<i class="reset-execute-icon">✅</i> Erfolgreich zurückgesetzt!';
// Show success message
showResetSuccessMessage(data.message);
// Close modal after delay
setTimeout(() => {
closeResetModal();
// Reload the page to reflect changes
window.location.reload();
}, 2000);
} else {
throw new Error(data.message || 'Unbekannter Fehler beim Zurücksetzen');
}
})
.catch(error => {
executeButton.classList.remove('reset-loading');
executeButton.disabled = false;
executeButton.innerHTML = originalText;
console.error('Error resetting item:', error);
showResetErrorMessage(`Fehler beim Zurücksetzen: ${error.message}`);
});
}
/**
* Shows a success message
*/
function showResetSuccessMessage(message) {
const successDiv = document.createElement('div');
successDiv.className = 'reset-success-message';
successDiv.innerHTML = `
<strong>✅ Erfolg!</strong><br>
${message}
`;
successDiv.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
border-radius: 8px;
padding: 15px 20px;
z-index: 10003;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
font-weight: 500;
max-width: 400px;
animation: resetSlideIn 0.3s ease, resetFadeOut 0.5s ease 3s forwards;
`;
document.body.appendChild(successDiv);
setTimeout(() => {
if (successDiv.parentNode) {
successDiv.parentNode.removeChild(successDiv);
}
}, 3500);
}
/**
* Shows an error message
*/
function showResetErrorMessage(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'reset-error-message';
errorDiv.innerHTML = `
<strong>⚠️ Fehler</strong><br>
${message}
<button onclick="this.parentElement.remove()" style="
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 20px;
color: #721c24;
cursor: pointer;
">×</button>
`;
errorDiv.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
border-radius: 8px;
padding: 15px 40px 15px 20px;
z-index: 10003;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
max-width: 400px;
animation: resetSlideIn 0.3s ease;
position: relative;
`;
document.body.appendChild(errorDiv);
setTimeout(() => {
if (errorDiv.parentNode) {
errorDiv.parentNode.removeChild(errorDiv);
}
}, 5000);
}
// Update the original resetItemBorrowingStatus function to use the new modal
function resetItemBorrowingStatus(itemId) {
openResetModal(itemId);
}
</script>