Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e76d525e4b | |||
| 7117dac67c | |||
| 5313c507ed |
+1
-1
@@ -1 +1 @@
|
||||
v0.8.25
|
||||
v0.8.26
|
||||
|
||||
+46
-8
@@ -4822,6 +4822,7 @@ def get_user_appointments():
|
||||
appointments = termin.get_upcoming_for_user(username, limit=250)
|
||||
|
||||
result = []
|
||||
import re as _re
|
||||
for appt in appointments:
|
||||
appt_id = str(appt.get('_id') or '')
|
||||
if not appt_id:
|
||||
@@ -4831,19 +4832,56 @@ def get_user_appointments():
|
||||
date_end = str(appt.get('date_end') or date_start)
|
||||
time_span = appt.get('time_span', []) or []
|
||||
|
||||
# Try to extract a time range from the first time_span entry
|
||||
# Determine a sensible start and end datetime for the event.
|
||||
# Prefer a time from the time_span for the first day and a time for the last day when multi-day.
|
||||
start_dt_str = date_start
|
||||
end_dt_str = date_end
|
||||
first_time = None
|
||||
last_time = None
|
||||
generic_first = None
|
||||
generic_last = None
|
||||
try:
|
||||
import re as _re
|
||||
if time_span:
|
||||
first = str(time_span[0])
|
||||
m = _re.search(r"(\d{2}:\d{2})-(\d{2}:\d{2})", first)
|
||||
# collect times from spans
|
||||
for entry in time_span:
|
||||
s = str(entry or '').strip()
|
||||
if not s:
|
||||
continue
|
||||
m_date = _re.match(r"^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})-(\d{2}:\d{2})$", s)
|
||||
if m_date:
|
||||
d = m_date.group(1)
|
||||
if d == date_start and not first_time:
|
||||
first_time = m_date.group(2)
|
||||
if d == date_end:
|
||||
last_time = m_date.group(3)
|
||||
continue
|
||||
m = _re.match(r"^(\d{2}:\d{2})-(\d{2}:\d{2})$", s)
|
||||
if m:
|
||||
start_dt_str = f"{date_start}T{m.group(1)}"
|
||||
end_dt_str = f"{date_start}T{m.group(2)}"
|
||||
if generic_first is None:
|
||||
generic_first = m.group(1)
|
||||
generic_last = m.group(2)
|
||||
|
||||
if not first_time and generic_first:
|
||||
first_time = generic_first
|
||||
if not last_time and generic_last:
|
||||
last_time = generic_last
|
||||
|
||||
if first_time:
|
||||
start_dt_str = f"{date_start}T{first_time}"
|
||||
|
||||
# If appointment spans multiple days and we have a last_time for the final day, use it
|
||||
if date_end != date_start:
|
||||
if last_time:
|
||||
end_dt_str = f"{date_end}T{last_time}"
|
||||
else:
|
||||
# span until end of final day
|
||||
end_dt_str = f"{date_end}T23:59:59"
|
||||
else:
|
||||
if last_time:
|
||||
end_dt_str = f"{date_start}T{last_time}"
|
||||
except Exception:
|
||||
pass
|
||||
# fallback: use whole-day span
|
||||
if date_end != date_start:
|
||||
end_dt_str = f"{date_end}T23:59:59"
|
||||
|
||||
title = appt.get('note') or f"Termin von {appt.get('user') or ''}"
|
||||
result.append({
|
||||
|
||||
@@ -357,63 +357,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Build background gap events to grey-out times that are not available
|
||||
function buildBackgroundGaps() {
|
||||
const bgEvents = [];
|
||||
const uiMin = slotMinTime; // UI visible window start
|
||||
const uiMax = slotMaxTime; // UI visible window end
|
||||
|
||||
// Helper to collect allowed intervals per day
|
||||
const spans = Array.isArray(available.time_span) ? available.time_span : [];
|
||||
|
||||
allDays.forEach(function(day) {
|
||||
// collect allowed intervals for this day
|
||||
const allowed = [];
|
||||
spans.forEach(function(entry) {
|
||||
const p = parseTimeSpanEntry(entry);
|
||||
if (!p) return;
|
||||
if (p.date && p.date !== day) return;
|
||||
// for generic spans (no date) include for all days
|
||||
allowed.push({from: p.from + ':00', to: p.to + ':00'});
|
||||
});
|
||||
|
||||
// sort allowed intervals
|
||||
allowed.sort(function(a,b){ return a.from < b.from ? -1 : (a.from > b.from ? 1 : 0); });
|
||||
|
||||
// if no allowed intervals -> grey the full ui window
|
||||
if (allowed.length === 0) {
|
||||
bgEvents.push({
|
||||
start: new Date(day + 'T' + uiMin),
|
||||
end: new Date(day + 'T' + uiMax),
|
||||
display: 'background',
|
||||
backgroundColor: '#f1f3f5'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// gap before first allowed
|
||||
if (allowed[0].from > uiMin) {
|
||||
bgEvents.push({ start: new Date(day + 'T' + uiMin), end: new Date(day + 'T' + allowed[0].from), display: 'background', backgroundColor: '#f1f3f5' });
|
||||
}
|
||||
|
||||
// gaps between allowed intervals
|
||||
for (let i = 0; i < allowed.length - 1; i++) {
|
||||
const endA = allowed[i].to;
|
||||
const startB = allowed[i+1].from;
|
||||
if (startB > endA) {
|
||||
bgEvents.push({ start: new Date(day + 'T' + endA), end: new Date(day + 'T' + startB), display: 'background', backgroundColor: '#f1f3f5' });
|
||||
}
|
||||
}
|
||||
|
||||
// gap after last allowed
|
||||
const last = allowed[allowed.length - 1];
|
||||
if (last.to < uiMax) {
|
||||
bgEvents.push({ start: new Date(day + 'T' + last.to), end: new Date(day + 'T' + uiMax), display: 'background', backgroundColor: '#f1f3f5' });
|
||||
}
|
||||
});
|
||||
|
||||
return bgEvents;
|
||||
}
|
||||
// No background greying: show full calendar skeleton and only mark possible slots
|
||||
|
||||
function updateSliderLabel() {
|
||||
const dateList = dateRangeInclusive(String(available.date_start || ''), String(available.date_end || ''));
|
||||
@@ -471,11 +415,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Add background gap events first so they appear behind slot events
|
||||
const bgGaps = buildBackgroundGaps();
|
||||
bgGaps.forEach(function(be) {
|
||||
calendar.addEvent(be);
|
||||
});
|
||||
// No background gaps: keep full skeleton/grid visible
|
||||
|
||||
// Add candidate free slots (blue)
|
||||
candidateSlots.forEach(function (slot) {
|
||||
|
||||
@@ -287,7 +287,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const filteredEvents = showCompletedBookings
|
||||
? processedEvents
|
||||
: processedEvents.filter(event => event.extendedProps.status !== 'completed');
|
||||
|
||||
|
||||
// If any event spans multiple days, switch to week view for better visibility
|
||||
try {
|
||||
const multiDay = processedEvents.some(ev => {
|
||||
try {
|
||||
const s = new Date(ev.start);
|
||||
const e = new Date(ev.end);
|
||||
return s && e && s.toDateString() !== e.toDateString();
|
||||
} catch (e) { return false; }
|
||||
});
|
||||
if (multiDay && calendar && calendar.view && calendar.view.type === 'timeGridDay') {
|
||||
calendar.changeView('timeGridWeek');
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// Provide events to calendar
|
||||
successCallback(filteredEvents);
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user