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

643 lines
21 KiB
HTML
Executable File

<!--
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
-->
{% extends "base.html" %}
{% block title %}Meine Ausleihungen{% endblock %}
{% block content %}
<style>
/* ...existing styles... */
/* Reset button styling for item cards */
.item-reset-button {
background-color: #ffc107;
color: #212529;
border: 2px solid #ffc107;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
font-size: 0.85rem;
text-decoration: none;
display: inline-block;
text-align: center;
transition: all 0.3s ease;
margin-left: 10px;
min-width: 90px;
}
.item-reset-button:hover {
background-color: #e0a800;
border-color: #d39e00;
transform: translateY(-1px);
box-shadow: 0 3px 6px rgba(255, 193, 7, 0.3);
}
.item-reset-button:active {
transform: translateY(0);
box-shadow: 0 1px 3px rgba(255, 193, 7, 0.3);
}
/* Admin-only indicator */
.admin-only-button {
opacity: 0.7;
pointer-events: none;
}
.admin-only-tooltip {
position: relative;
display: inline-block;
}
.admin-only-tooltip:hover::after {
content: "Nur für Administratoren verfügbar";
position: absolute;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
background-color: var(--ui-text);
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
z-index: 1000;
}
/* Card action buttons container */
.card-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 15px;
align-items: center;
}
.card-actions form {
display: inline-block;
margin: 0;
}
/* Update action-buttons-container to also use flex layout */
.action-buttons-container {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.action-buttons-container form {
display: inline-block;
margin: 0;
}
/* Ensure buttons have consistent spacing */
.action-buttons-container .return-button,
.action-buttons-container .cancel-button {
margin-right: 0;
flex: 0 0 auto;
width: auto;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.card-actions {
flex-direction: column;
align-items: stretch;
}
.item-reset-button {
margin-left: 0;
margin-top: 5px;
}
}
</style>
<div class="container">
<div class="content">
<h1>Meine aktuellen Ausleihungen</h1>
{% if not items and not planned_items %}
<div class="no-items-message">
Sie haben derzeit keine Objekte ausgeliehen oder geplant.
</div>
{% else %}
{% if items %}
<h2 class="section-title">Aktive Ausleihungen</h2>
<div class="borrowed-items">
{% for item in items %}
<div class="borrowed-item">
<div class="item-header">
<h3>{{ item.Name }}</h3>
{% if item.UserExemplarCount is defined and item.UserExemplarCount > 0 %}
<span class="exemplar-count">{{ item.UserExemplarCount }} Exemplar(e)</span>
{% endif %}
{% if item.ActiveAppointment %}
<span class="appointment-status status-active">Aktiv</span>
{% endif %}
</div>
<div class="item-details">
<div class="item-info">
<p><strong>Ort:</strong> {{ item.Ort or '-' }}</p>
{% if item.Filter is defined %}
<p><strong>Unterrichtsfach:</strong>
{% if item.Filter is string %}
{{ item.Filter }}
{% elif item.Filter | length > 0 %}
{{ item.Filter | join(', ') }}
{% else %}
-
{% endif %}
</p>
{% endif %}
{% if item.Filter2 is defined %}
<p><strong>Jahrgangsstufe:</strong>
{% if item.Filter2 is string %}
{{ item.Filter2 }}
{% elif item.Filter2 | length > 0 %}
{{ item.Filter2 | join(', ') }}
{% else %}
-
{% endif %}
</p>
{% endif %}
<p><strong>Barcode:</strong> {{ item.Code_4 or '-' }}</p>
{% if item.UserExemplars is defined and item.UserExemplars %}
<div class="exemplar-details">
<p><strong>Ausgeliehene Exemplare:</strong></p>
<ul class="exemplar-list">
{% for exemplar in item.UserExemplars %}
<li>
Exemplar {{ exemplar.number }}
{% if exemplar.date %}<span class="borrow-date">(seit {{ exemplar.date }})</span>{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
{% if item.Images and item.Images | length > 0 %}
<div class="item-image">
<img src="{{ url_for('uploaded_file', filename=item.Images[0]) }}" alt="{{ item.Name }}">
</div>
{% endif %}
</div>
<div class="item-actions">
{% if item.AppointmentData is defined and item.AppointmentData %}
<!-- Item with appointment data (from active/planned appointments) -->
<div class="action-buttons-container">
<form method="POST" action="{{ url_for('zurueckgeben', id=item._id) }}">
<input type="hidden" name="source_page" value="my_borrowed_items">
<button class="return-button" type="submit">Zurückgeben</button>
</form>
<form method="POST" action="{{ url_for('cancel_ausleihung_route', id=item.AppointmentData.id) }}" onsubmit="return confirm('Möchten Sie diese aktive Ausleihung wirklich stornieren?');">
<button class="cancel-button" type="submit">Stornieren</button>
</form>
<!-- Add reset button for admins -->
{% if user_is_admin %}
<button type="button"
class="item-reset-button"
onclick="resetItemBorrowingStatus('{{ item._id }}')"
title="Status des Items zurücksetzen">
Status zurücksetzen
</button>
{% endif %}
</div>
{% else %}
<!-- Regular borrowed item without appointment data -->
<div class="card-actions">
<form method="POST" action="{{ url_for('zurueckgeben', id=item._id) }}">
<input type="hidden" name="source_page" value="my_borrowed_items">
{% if item.UserExemplarCount is defined and item.UserExemplarCount > 1 %}
<div class="return-count-group">
<label for="exemplare_count_{{ loop.index }}">Anzahl zurückgeben:</label>
<input type="number" id="exemplare_count_{{ loop.index }}" name="exemplare_count"
min="1" max="{{ item.UserExemplarCount }}" value="1" class="exemplare-count-input">
</div>
{% endif %}
<button class="return-button" type="submit">Zurückgeben</button>
</form>
<!-- Add reset button for every borrowed item -->
{% if user_is_admin %}
<button type="button"
class="item-reset-button"
onclick="resetItemBorrowingStatus('{{ item._id }}')"
title="Status des Items zurücksetzen">
Status zurücksetzen
</button>
{% else %}
<div class="admin-only-tooltip">
<button type="button"
class="item-reset-button admin-only-button"
title="Nur für Administratoren verfügbar">
Status zurücksetzen
</button>
</div>
{% endif %}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% if planned_items %}
<h2 class="section-title">Geplante Ausleihungen</h2>
<div class="borrowed-items planned-items">
{% for item in planned_items %}
<div class="borrowed-item planned-item">
<div class="item-header">
<h3>{{ item.Name }}</h3>
{% set status = item.AppointmentData.status | default('planned') %}
<span class="appointment-status status-{{ status }}">
{% if status == 'planned' %}
Geplant
{% elif status == 'active' %}
Aktiv
{% elif status == 'completed' %}
Abgeschlossen
{% elif status == 'cancelled' %}
Storniert
{% else %}
{{ status }}
{% endif %}
</span>
</div>
<div class="item-details">
<div class="item-info">
<p><strong>Ort:</strong> {{ item.Ort or '-' }}</p>
{% if item.Filter is defined %}
<p><strong>Unterrichtsfach:</strong>
{% if item.Filter is string %}
{{ item.Filter }}
{% elif item.Filter | length > 0 %}
{{ item.Filter | join(', ') }}
{% else %}
-
{% endif %}
</p>
{% endif %}
{% if item.Filter2 is defined %}
<p><strong>Jahrgangsstufe:</strong>
{% if item.Filter2 is string %}
{{ item.Filter2 }}
{% elif item.Filter2 | length > 0 %}
{{ item.Filter2 | join(', ') }}
{% else %}
-
{% endif %}
</p>
{% endif %}
<p><strong>Barcode:</strong> {{ item.Code_4 or '-' }}</p>
<div class="appointment-details">
<p><strong>Termin:</strong></p>
{% set start_date = item.AppointmentData.start %}
{% set end_date = item.AppointmentData.end %}
<p class="appointment-date">
{% if item.AppointmentData.period %}
{% if start_date %}{{ start_date.strftime('%d.%m.%Y') }}{% else %}Unbekannt{% endif %}
<span class="appointment-period">{{ item.AppointmentData.period }}. Stunde</span>
{% else %}
{% if start_date and end_date and start_date.date() != end_date.date() %}
{{ start_date.strftime('%d.%m.%Y') }} - {{ end_date.strftime('%d.%m.%Y') }}
<span class="appointment-period">Mehrtägig</span>
{% else %}
{% if start_date %}{{ start_date.strftime('%d.%m.%Y') }}{% else %}Unbekannt{% endif %}
{% endif %}
{% endif %}
</p>
{% if item.AppointmentData.notes %}
<p class="appointment-notes"><i>{{ item.AppointmentData.notes }}</i></p>
{% endif %}
</div>
</div>
{% if item.Images and item.Images | length > 0 %}
<div class="item-image">
<img src="{{ url_for('uploaded_file', filename=item.Images[0]) }}" alt="{{ item.Name }}">
</div>
{% endif %}
</div>
<div class="item-actions">
<div class="card-actions">
<form method="POST" action="{{ url_for('cancel_ausleihung_route', id=item.AppointmentData.id) }}" onsubmit="return confirm('Möchten Sie diesen geplanten Termin wirklich stornieren?');">
<button class="cancel-button" type="submit">Stornieren</button>
</form>
<!-- Add reset button for planned items too -->
{% if user_is_admin %}
<button type="button"
class="item-reset-button"
onclick="resetItemBorrowingStatus('{{ item._id }}')"
title="Status des Items zurücksetzen">
Status zurücksetzen
</button>
{% else %}
<div class="admin-only-tooltip">
<button type="button"
class="item-reset-button admin-only-button"
title="Nur für Administratoren verfügbar">
Status zurücksetzen
</button>
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% endif %}
</div>
</div>
<!-- Include reset item functions -->
{% include "reset_item_functions.html" %}
<!-- Include the new reset modal -->
{% include "reset_item_modal.html" %}
<!-- Add user admin status check -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Check if current user is admin and store globally
fetch('/user_status')
.then(response => response.json())
.then(data => {
window.currentUserIsAdmin = data.is_admin || false;
})
.catch(error => {
console.error('Error checking user status:', error);
window.currentUserIsAdmin = false;
});
});
</script>
<style>
.borrowed-items {
display: flex;
flex-direction: column;
gap: 20px;
margin-top: 20px;
}
.borrowed-item {
background-color: var(--ui-surface);
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
border-bottom: 1px solid #e9ecef;
padding-bottom: 10px;
}
.item-header h3 {
margin: 0;
color: #343a40;
}
.exemplar-count {
background-color: #17a2b8;
color: white;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8rem;
font-weight: bold;
}
.item-details {
display: flex;
gap: 20px;
}
.item-info {
flex: 1;
}
.item-image {
flex: 0 0 150px;
}
.item-image img {
width: 100%;
max-height: 150px;
object-fit: cover;
border-radius: 4px;
}
.exemplar-details {
margin-top: 10px;
background-color: var(--ui-surface-soft);
padding: 10px;
border-radius: 5px;
border-left: 3px solid #6c757d;
}
.exemplar-list {
margin: 5px 0 0 0;
padding-left: 20px;
}
.exemplar-list li {
margin-bottom: 5px;
}
.borrow-date {
color: #6c757d;
font-size: 0.9em;
font-style: italic;
}
.item-actions {
margin-top: 15px;
display: flex;
justify-content: flex-end;
}
.return-button {
padding: 8px 16px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
}
.return-button:hover {
background-color: #0069d9;
}
.return-count-group {
display: flex;
align-items: center;
gap: 10px;
margin-right: 15px;
}
.exemplare-count-input {
width: 60px;
padding: 5px;
border: 1px solid #ced4da;
border-radius: 4px;
}
.no-items-message {
text-align: center;
padding: 30px;
background-color: var(--ui-surface-soft);
border-radius: 5px;
color: #6c757d;
margin-top: 20px;
}
/* Planned items styling */
.section-title {
margin-top: 30px;
margin-bottom: 20px;
font-size: 1.5rem;
color: #343a40;
border-bottom: 2px solid #e9ecef;
padding-bottom: 10px;
}
.planned-badge {
background-color: #17a2b8;
color: white;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8rem;
font-weight: bold;
}
.planned-item {
border-left: 4px solid #17a2b8;
}
.appointment-details {
margin-top: 10px;
background-color: #e1f5fe;
padding: 10px;
border-radius: 5px;
border-left: 3px solid #17a2b8;
}
.appointment-date {
font-weight: bold;
color: #0277bd;
}
.appointment-period {
font-weight: normal;
font-style: italic;
margin-left: 10px;
}
.appointment-notes {
margin-top: 5px;
font-style: italic;
}
.action-buttons-container {
display: flex;
gap: 10px;
justify-content: space-between;
}
.action-buttons-container form {
flex: 1;
}
.action-buttons-container .return-button,
.action-buttons-container .cancel-button {
width: 100%;
}
.cancel-button {
background-color: #dc3545;
color: white;
border: none;
border-radius: 5px;
padding: 10px 15px;
cursor: pointer;
font-weight: bold;
}
.cancel-button:hover {
background-color: #c82333;
}
.appointment-status {
padding: 2px 8px;
border-radius: 10px;
font-size: 0.8em;
font-weight: bold;
color: white;
margin-left: 10px;
}
.status-planned {
background-color: #03a9f4;
}
.status-active {
background-color: #4CAF50;
}
.status-completed {
background-color: #9E9E9E;
}
.status-cancelled {
background-color: #F44336;
}
/* Responsive layout */
@media (max-width: 768px) {
.item-details {
flex-direction: column-reverse;
}
.item-image {
flex: 0 0 auto;
text-align: center;
margin-bottom: 15px;
}
.item-image img {
max-width: 250px;
}
}
</style>
{% endblock %}