manager: redo day view to always have one cell/one hour (#21213)
This commit is contained in:
parent
ea93550742
commit
c932aea280
|
@ -88,7 +88,6 @@ a.timeperiod-exception-all {
|
|||
}
|
||||
|
||||
$dayview-column-width: 14vw;
|
||||
$dayview-row-height: 4.5ex;
|
||||
|
||||
.dayview thead th {
|
||||
width: $dayview-column-width;
|
||||
|
@ -98,9 +97,8 @@ $dayview-row-height: 4.5ex;
|
|||
.dayview tbody th {
|
||||
box-sizing: border-box;
|
||||
text-align: left;
|
||||
padding: 0 2ex;
|
||||
line-height: $dayview-row-height;
|
||||
height: $dayview-row-height;
|
||||
padding: 1ex 2ex;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.dayview tbody tr:nth-child(2n+1) th,
|
||||
|
@ -111,28 +109,32 @@ $dayview-row-height: 4.5ex;
|
|||
}
|
||||
}
|
||||
|
||||
.dayview td {
|
||||
padding: 0.5ex 1ex;
|
||||
.dayview tbody td {
|
||||
padding: 0 1ex;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* attr(data-rowspan) is not supported by browsers; emulate it by getting
|
||||
* the attribute value into a CSS variable. */
|
||||
@for $i from 2 through 100 {
|
||||
[data-rowspan="#{$i}"] { --rowspan: #{$i}; }
|
||||
@for $i from 1 through 60 {
|
||||
table.hourspan-#{$i} tbody td {
|
||||
height: calc(#{$i} * 2.5em);
|
||||
}
|
||||
}
|
||||
|
||||
.dayview div[data-rowspan] {
|
||||
margin-top: -1ex;
|
||||
.dayview tbody td div {
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
padding: 1ex;
|
||||
background: #eef;
|
||||
position: absolute;
|
||||
width: calc(#{$dayview-column-width} - 2ex);
|
||||
height: calc(#{$dayview-row-height} * var(--rowspan) - 2ex);
|
||||
min-height: calc(#{$dayview-row-height} * var(--rowspan) - 2ex);
|
||||
border: 1px solid #666;
|
||||
border: 1px solid #aaa;
|
||||
overflow: hidden;
|
||||
&:hover {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
span.start-time {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
{% for period, desk_bookings in view.get_timeperiods %}
|
||||
|
||||
{% if forloop.first %}
|
||||
<table>
|
||||
<table class="hourspan-{{ hour_span }}">
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
|
@ -39,17 +39,20 @@
|
|||
|
||||
<tr>
|
||||
<th>{{ period|date:"TIME_FORMAT" }}</th>
|
||||
{% for booking in desk_bookings %}
|
||||
{% for bookings in desk_bookings %}
|
||||
<td>
|
||||
{% if booking %}
|
||||
<div class="booked" {% if booking.rowspan %}data-rowspan="{{ booking.rowspan }}"{% endif %}>
|
||||
<a {% if booking.backoffice_url %}href="{{booking.backoffice_url}}"{% endif %}
|
||||
{% for booking in bookings %}
|
||||
<div class="booked"
|
||||
style="height: {{ booking.css_height }}%; top: {{ booking.css_top }}%;"
|
||||
><span class="start-time">{{booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
|
||||
<a {% if booking.backoffice_url %}href="{{booking.backoffice_url}}"{% endif %}
|
||||
>{% if booking.label or booking.user_name %}
|
||||
{{booking.label}}{% if booking.label and booking.user_name %} - {% endif %} {{booking.user_name}}
|
||||
{% else %}{% trans "booked" %}{% endif %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
|
||||
{% if forloop.last %}
|
||||
|
|
|
@ -175,6 +175,10 @@ class AgendaDayView(DayArchiveView):
|
|||
def get_context_data(self, **kwargs):
|
||||
context = super(AgendaDayView, self).get_context_data(**kwargs)
|
||||
context['agenda'] = self.agenda
|
||||
try:
|
||||
context['hour_span'] = max(60 / self.agenda.get_base_meeting_duration(), 1)
|
||||
except ValueError: # no meeting types defined
|
||||
context['hour_span'] = 1
|
||||
context['user_can_manage'] = self.agenda.can_be_managed(self.request.user)
|
||||
return context
|
||||
|
||||
|
@ -205,31 +209,34 @@ class AgendaDayView(DayArchiveView):
|
|||
min_timeperiod = min([x.start_time for x in timeperiods])
|
||||
max_timeperiod = max([x.end_time for x in timeperiods])
|
||||
|
||||
interval = datetime.timedelta(minutes=self.agenda.get_base_meeting_duration())
|
||||
current_date = self.date.replace(hour=min_timeperiod.hour, minute=min_timeperiod.minute)
|
||||
max_date = self.date.replace(hour=max_timeperiod.hour, minute=max_timeperiod.minute)
|
||||
interval = datetime.timedelta(minutes=60)
|
||||
current_date = self.date.replace(hour=min_timeperiod.hour, minute=0)
|
||||
if max_timeperiod.minute == 0:
|
||||
max_date = self.date.replace(hour=max_timeperiod.hour, minute=0)
|
||||
else:
|
||||
# until the end of the last hour.
|
||||
max_date = self.date.replace(hour=max_timeperiod.hour + 1, minute=0)
|
||||
|
||||
desks = self.agenda.desk_set.all()
|
||||
|
||||
while current_date < max_date:
|
||||
# for each timeslot return the timeslot date and a list of per-desk
|
||||
# bookings
|
||||
bookings = []
|
||||
desks_bookings = [] # list of bookings for each desk
|
||||
for desk in desks:
|
||||
booking = None
|
||||
event = [x for x in self.object_list if x.desk_id == desk.id and x.start_datetime == current_date]
|
||||
if event:
|
||||
# if an event exist, check it has a non cancelled booking
|
||||
event = event[0]
|
||||
event_bookings = [x for x in event.booking_set.all() if not x.cancellation_datetime]
|
||||
if event_bookings:
|
||||
booking = event_bookings[0]
|
||||
if event.meeting_type.duration > (interval.total_seconds() / 60):
|
||||
booking.rowspan = int(event.meeting_type.duration / (interval.total_seconds() / 60))
|
||||
bookings = [] # bookings for this desk
|
||||
finish_datetime = current_date + interval
|
||||
for event in [x for x in self.object_list if x.desk_id == desk.id and
|
||||
x.start_datetime >= current_date and x.start_datetime < finish_datetime]:
|
||||
# don't consider cancelled bookings
|
||||
for booking in [x for x in event.booking_set.all() if not x.cancellation_datetime]:
|
||||
booking.css_top = int(100 * event.start_datetime.minute / 60)
|
||||
booking.css_height = int(100 * event.meeting_type.duration / 60)
|
||||
bookings.append(booking)
|
||||
|
||||
bookings.append(booking)
|
||||
desks_bookings.append(bookings)
|
||||
|
||||
yield current_date, bookings
|
||||
yield current_date, desks_bookings
|
||||
current_date += interval
|
||||
|
||||
agenda_day_view = AgendaDayView.as_view()
|
||||
|
|
|
@ -1192,13 +1192,19 @@ def test_agenda_day_view(app, admin_user, manager_user, api_user):
|
|||
resp = resp.follow()
|
||||
assert 'No opening hours this day.' in resp.body # no time pediod
|
||||
|
||||
TimePeriod(desk=desk, weekday=today.weekday(),
|
||||
timeperiod = TimePeriod(desk=desk, weekday=today.weekday(),
|
||||
start_time=datetime.time(10, 0),
|
||||
end_time=datetime.time(18, 0)).save()
|
||||
end_time=datetime.time(18, 0))
|
||||
timeperiod.save()
|
||||
resp = app.get('/manage/agendas/%s/' % agenda.id, status=302).follow()
|
||||
assert not 'No opening hours this day.' in resp.body
|
||||
assert not 'div class="booked' in resp.body
|
||||
assert resp.body.count('<tr') == 17
|
||||
assert resp.body.count('<tr') == 9 # 10->18 (not included)
|
||||
|
||||
timeperiod.end_time = datetime.time(18, 30) # end during an hour
|
||||
timeperiod.save()
|
||||
resp = app.get('/manage/agendas/%s/' % agenda.id, status=302).follow()
|
||||
assert resp.body.count('<tr') == 10 # 10->18 (included)
|
||||
|
||||
# book some slots
|
||||
app.reset()
|
||||
|
@ -1216,17 +1222,17 @@ def test_agenda_day_view(app, admin_user, manager_user, api_user):
|
|||
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (
|
||||
agenda.id, date.year, date.month, date.day))
|
||||
assert resp.body.count('div class="booked') == 2
|
||||
assert resp.body.count('data-rowspan') == 0
|
||||
assert 'hourspan-2' in resp.body # table CSS class
|
||||
assert 'height: 50%; top: 0%;' in resp.body # booking cells
|
||||
|
||||
# create a shorted meeting type, this will double the number of rows and
|
||||
# the bookings will have to span multiple rows.
|
||||
# create a shorter meeting type, this will change the table CSS class
|
||||
# (and visually this will give more room for events)
|
||||
meetingtype = MeetingType(agenda=agenda, label='Baz', duration=15)
|
||||
meetingtype.save()
|
||||
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (
|
||||
agenda.id, date.year, date.month, date.day))
|
||||
assert resp.body.count('<tr') == 33
|
||||
assert resp.body.count('div class="booked') == 2
|
||||
assert resp.body.count('data-rowspan') == 2
|
||||
assert 'hourspan-4' in resp.body # table CSS class
|
||||
|
||||
# cancel a booking
|
||||
app.reset()
|
||||
|
@ -1240,7 +1246,6 @@ def test_agenda_day_view(app, admin_user, manager_user, api_user):
|
|||
resp = app.get('/manage/agendas/%s/%d/%d/%d/' % (
|
||||
agenda.id, date.year, date.month, date.day))
|
||||
assert resp.body.count('div class="booked') == 1
|
||||
assert resp.body.count('data-rowspan') == 1
|
||||
|
||||
# wrong type
|
||||
agenda2 = Agenda(label=u'Foo bar')
|
||||
|
|
Loading…
Reference in New Issue