Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a8f3907f34 | |||
| 6311e7710a |
+19
-8
@@ -7990,8 +7990,12 @@ def check_availability():
|
||||
items_col = db['items']
|
||||
|
||||
# Collect potential conflicts (planned and active) for this day
|
||||
same_day_start = datetime.datetime.combine(booking_date.date(), datetime.time.min)
|
||||
same_day_end = datetime.datetime.combine(booking_date.date(), datetime.time.max)
|
||||
same_day_start = datetime.datetime.combine(
|
||||
booking_date.date(), datetime.time.min, tzinfo=ZoneInfo("Europe/Berlin")
|
||||
)
|
||||
same_day_end = datetime.datetime.combine(
|
||||
booking_date.date(), datetime.time.max, tzinfo=ZoneInfo("Europe/Berlin")
|
||||
)
|
||||
candidates = list(ausleihungen.find({
|
||||
'Item': item_id,
|
||||
'Status': {'$in': ['planned', 'active']},
|
||||
@@ -8009,6 +8013,8 @@ def check_availability():
|
||||
if r_start is None:
|
||||
r_start = same_day_start
|
||||
# Overlap check: req_start < r_end and req_end > r_start
|
||||
r_start = au.ensure_timezone_aware(r_start)
|
||||
r_end = au.ensure_timezone_aware(r_end)
|
||||
if req_start < r_end and req_end > r_start:
|
||||
conflicts.append({
|
||||
'id': str(r.get('_id')),
|
||||
@@ -8223,16 +8229,19 @@ def add_booking():
|
||||
period = request.form.get('period')
|
||||
notes = request.form.get('notes', '')
|
||||
|
||||
# Parse dates as naive datetime objects
|
||||
# Form timestamps represent local school time.
|
||||
try:
|
||||
# Simple datetime parsing without timezone
|
||||
if start_date_str:
|
||||
start_date = datetime.datetime.strptime(start_date_str, '%Y-%m-%d %H:%M:%S')
|
||||
start_date = datetime.datetime.strptime(
|
||||
start_date_str, '%Y-%m-%d %H:%M:%S'
|
||||
).replace(tzinfo=ZoneInfo("Europe/Berlin"))
|
||||
else:
|
||||
return jsonify({'success': False, 'error': 'Missing start date'})
|
||||
|
||||
if end_date_str:
|
||||
end_date = datetime.datetime.strptime(end_date_str, '%Y-%m-%d %H:%M:%S')
|
||||
end_date = datetime.datetime.strptime(
|
||||
end_date_str, '%Y-%m-%d %H:%M:%S'
|
||||
).replace(tzinfo=ZoneInfo("Europe/Berlin"))
|
||||
else:
|
||||
end_date = None
|
||||
|
||||
@@ -11274,12 +11283,14 @@ def get_period_times(booking_date, period_num):
|
||||
# Create datetime objects for start and end times
|
||||
start_datetime = datetime.datetime.combine(
|
||||
booking_date.date(),
|
||||
datetime.time(start_hour, start_min)
|
||||
datetime.time(start_hour, start_min),
|
||||
tzinfo=ZoneInfo("Europe/Berlin")
|
||||
)
|
||||
|
||||
end_datetime = datetime.datetime.combine(
|
||||
booking_date.date(),
|
||||
datetime.time(end_hour, end_min)
|
||||
datetime.time(end_hour, end_min),
|
||||
tzinfo=ZoneInfo("Europe/Berlin")
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
@@ -42,12 +42,12 @@ def _get_client():
|
||||
return MongoClient(cfg.MONGODB_HOST, cfg.MONGODB_PORT)
|
||||
# Add this helper function after imports
|
||||
def ensure_timezone_aware(dt):
|
||||
"""Ensures a datetime is timezone-aware, using UTC if naive"""
|
||||
"""Return a timezone-aware datetime, treating naive DB values as UTC."""
|
||||
if dt is None:
|
||||
return None
|
||||
if dt.tzinfo is None:
|
||||
# Treat naive datetimes as UTC
|
||||
return dt.replace(tzinfo=None)
|
||||
# PyMongo returns BSON datetimes as naive UTC unless tz_aware is enabled.
|
||||
return dt.replace(tzinfo=datetime.timezone.utc)
|
||||
return dt
|
||||
|
||||
def get_current_status(ausleihung, log_changes=False, user=None):
|
||||
@@ -82,8 +82,8 @@ def get_current_status(ausleihung, log_changes=False, user=None):
|
||||
return 'completed'
|
||||
|
||||
current_time = datetime.datetime.now(ZoneInfo("Europe/Berlin"))
|
||||
start_time = ausleihung.get('Start')
|
||||
end_time = ausleihung.get('End')
|
||||
start_time = ensure_timezone_aware(ausleihung.get('Start'))
|
||||
end_time = ensure_timezone_aware(ausleihung.get('End'))
|
||||
|
||||
# Wenn kein Startdatum vorhanden ist, Status auf 'planned' setzen
|
||||
if not start_time:
|
||||
|
||||
+48
-25
@@ -1136,7 +1136,7 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item" data-nav-fixed="true">
|
||||
<button id="themeToggleBtn" class="btn btn-link nav-link px-3" aria-label="Dark Mode umschalten" title="Theme umschalten">
|
||||
<button type="button" class="btn btn-link nav-link px-3" data-theme-toggle aria-label="Dark Mode umschalten" aria-pressed="false" title="Theme umschalten">
|
||||
<span class="theme-icon-light" style="display: none;">☀️</span>
|
||||
<span class="theme-icon-dark" style="display: none;">🌙</span>
|
||||
</button>
|
||||
@@ -1229,7 +1229,7 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li class="nav-item" data-nav-fixed="true">
|
||||
<button id="themeToggleBtn" class="btn btn-link nav-link px-3" aria-label="Dark Mode umschalten" title="Theme umschalten">
|
||||
<button type="button" class="btn btn-link nav-link px-3" data-theme-toggle aria-label="Dark Mode umschalten" aria-pressed="false" title="Theme umschalten">
|
||||
<span class="theme-icon-light" style="display: none;">☀️</span>
|
||||
<span class="theme-icon-dark" style="display: none;">🌙</span>
|
||||
</button>
|
||||
@@ -1358,7 +1358,7 @@
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item" data-nav-fixed="true">
|
||||
<button id="themeToggleBtn" class="btn btn-link nav-link px-3" aria-label="Dark Mode umschalten" title="Theme umschalten">
|
||||
<button type="button" class="btn btn-link nav-link px-3" data-theme-toggle aria-label="Dark Mode umschalten" aria-pressed="false" title="Theme umschalten">
|
||||
<span class="theme-icon-light" style="display: none;">☀️</span>
|
||||
<span class="theme-icon-dark" style="display: none;">🌙</span>
|
||||
</button>
|
||||
@@ -2345,33 +2345,56 @@
|
||||
|
||||
<!-- Theme Toggle Script -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const toggleBtns = document.querySelectorAll('#themeToggleBtn');
|
||||
if (toggleBtns.length === 0) return;
|
||||
|
||||
function updateIcons(theme) {
|
||||
const isDark = theme === 'dark';
|
||||
document.querySelectorAll('.theme-icon-light').forEach(icon => icon.style.display = isDark ? 'inline' : 'none');
|
||||
document.querySelectorAll('.theme-icon-dark').forEach(icon => icon.style.display = isDark ? 'none' : 'inline');
|
||||
(function () {
|
||||
const root = document.documentElement;
|
||||
const metaThemeColor = document.getElementById('meta-theme-color');
|
||||
const themeToggleSelector = '[data-theme-toggle]';
|
||||
|
||||
function getTheme() {
|
||||
return root.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
// Get current setup from initial script in head
|
||||
let currentTheme = document.documentElement.getAttribute('data-theme') || 'light';
|
||||
updateIcons(currentTheme);
|
||||
function updateThemeUi(theme) {
|
||||
const isDark = theme === 'dark';
|
||||
document.querySelectorAll('.theme-icon-light').forEach(icon => {
|
||||
icon.style.display = isDark ? 'inline' : 'none';
|
||||
});
|
||||
document.querySelectorAll('.theme-icon-dark').forEach(icon => {
|
||||
icon.style.display = isDark ? 'none' : 'inline';
|
||||
});
|
||||
document.querySelectorAll(themeToggleSelector).forEach(button => {
|
||||
button.setAttribute('aria-pressed', String(isDark));
|
||||
button.setAttribute('aria-label', isDark ? 'Light Mode einschalten' : 'Dark Mode einschalten');
|
||||
button.setAttribute('title', isDark ? 'Light Mode einschalten' : 'Dark Mode einschalten');
|
||||
});
|
||||
if (metaThemeColor) {
|
||||
metaThemeColor.setAttribute('content', isDark ? '#1a252f' : '#2c3e50');
|
||||
}
|
||||
}
|
||||
|
||||
toggleBtns.forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
currentTheme = currentTheme === 'light' ? 'dark' : 'light';
|
||||
|
||||
document.documentElement.setAttribute('data-theme', currentTheme);
|
||||
localStorage.setItem('inventarsystem-theme', currentTheme);
|
||||
document.getElementById('meta-theme-color').setAttribute('content', currentTheme === 'dark' ? '#1a252f' : '#2c3e50');
|
||||
|
||||
updateIcons(currentTheme);
|
||||
function applyTheme(theme, persist) {
|
||||
const normalizedTheme = theme === 'dark' ? 'dark' : 'light';
|
||||
root.setAttribute('data-theme', normalizedTheme);
|
||||
if (persist) {
|
||||
try {
|
||||
localStorage.setItem('inventarsystem-theme', normalizedTheme);
|
||||
} catch (error) {
|
||||
console.warn('Theme konnte nicht gespeichert werden:', error);
|
||||
}
|
||||
}
|
||||
updateThemeUi(normalizedTheme);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
updateThemeUi(getTheme());
|
||||
document.addEventListener('click', function (event) {
|
||||
const button = event.target.closest(themeToggleSelector);
|
||||
if (!button) return;
|
||||
event.preventDefault();
|
||||
applyTheme(getTheme() === 'dark' ? 'light' : 'dark', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
(function () {
|
||||
|
||||
Reference in New Issue
Block a user