manager: update partial bookings html & css (#78728)
gitea/chrono/pipeline/head This commit looks good Details

This commit is contained in:
Thomas Jund 2023-06-21 17:45:14 +02:00 committed by Valentin Deniaud
parent b615c57bad
commit 00ad7b0747
4 changed files with 144 additions and 49 deletions

View File

@ -598,10 +598,94 @@ div#appbar a.active {
color: white;
}
table.partial-bookings {
border-spacing: 0;
td.hour-cell {
outline: solid 1px;
// Partial booking view
.partial-booking {
--registrant-name-width: 15rem;
--zebra-color: hsla(0,0%,0%,0.05);
--separator-color: white;
--separator-size: 2px;
position: relative;
background: white;
padding: 0.5rem;
&--hours-list {
background: white;
position: sticky;
z-index: 2;
top: 0;
display: grid;
grid-template-columns: repeat(var(--nb-hours), 1fr);
font-size: 80%;
@media (min-width: 761px) {
padding-left: var(--registrant-name-width);
}
}
&--hour {
text-align: center;
transform: translateX(-50%);
&:first-child {
visibility: hidden;
}
}
&--registrant-items {
margin-top: 0.5rem;
}
&--registrant {
display: flex;
flex-wrap: wrap;
&:nth-child(odd) {
background-color: var(--zebra-color);
}
&:nth-child(even) {
--separator-color: var(--zebra-color);
}
.registrant {
&--name {
box-sizing: border-box;
margin: 0;
padding: .66rem;
font-size: 130%;
color: #505050;
font-weight: normal;
@media (min-width: 761px) {
flex: 0 0 var(--registrant-name-width);
text-align: right;
}
}
&--datas {
box-sizing: border-box;
flex: 1 0 100%;
@media (min-width: 761px) {
flex-basis: auto;
}
background: linear-gradient(
to left,
var(--separator-color) var(--separator-size),
transparent var(--separator-size),
transparent 100%
);
background-size: calc(100% / var(--nb-hours)) 100%;
@media (min-width: 761px) {
border-left: var(--separator-size) solid var(--separator-color);
}
}
&--bar-container {
position: relative;
padding: .33rem 0;
}
&--bar {
--background: #1066bc;
--color: white;
box-sizing: border-box;
margin: 0.33rem 0;
position: relative;
padding: 0.33em 0.66em;
background-color: var(--background);
color: var(--color);
}
}
}
}

View File

@ -8,36 +8,37 @@
<p>{% trans "No opening hours this day." %}</p>
</div>
{% else %}
<table class="agenda-table partial-bookings">
<thead>
<tr>
<td></td>
{% for hour in hours %}
<th>{{ hour|date:"TIME_FORMAT" }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for user, bookings in bookings_by_user.items %}
<tr class="{% cycle 'odd' 'even' %}">
<th>{{ bookings.0.get_user_block }}</th>
{% for _ in hours %}
<td class="hour-cell">
{% if forloop.first %}
{% for booking in bookings %}
<div class="booking"
style="left: {{ booking.css_left }}%; width: {{ booking.css_width }}%;"
>{{ booking.start_time }} - {{ booking.end_time }}</div>
{% endfor %}
{% endif %}
</td>
{% endfor %}
</tr>
<div class="partial-booking" style="--nb-hours: {{ hours|length }}">
<div class="partial-booking--hours-list" aria-hidden="true">
{% for hour in hours %}
<div class="partial-booking--hour">{{ hour|time:"H" }}&#x202Fh</div>
{% endfor %}
</tbody>
</div>
</table>
<div class="partial-booking--registrant-items">
{% for user, bookings in bookings_by_user.items %}
<section class="partial-booking--registrant">
<h3 class="registrant--name">{{ bookings.0.get_user_block }}</h3>
<div class="registrant--datas">
{% for booking in bookings %}
<div class="registrant--bar-container">
<p
class="registrant--bar booking"
title="{% trans "Booked period" %}"
style="left: {{ booking.css_left }}%; width: {{ booking.css_width }}%;"
>
<strong class="sr-only">{% trans "Booked period:" %}</strong>
<time datetime="{{ booking.start_time|time:"H:i" }}">{{ booking.start_time|time:"H:i" }}</time>
<time datetime="{{ booking.end_time|time:"H:i" }}">{{ booking.end_time|time:"H:i" }}</time>
</p>
</div>
{% endfor %}
</div>
</section>
{% endfor %}
</div>
</div>
{% endif %}
{% endblock %}

View File

@ -1509,8 +1509,14 @@ class AgendaDayView(AgendaDateView, DayArchiveView):
end_time = datetime.time(min(max_time.hour + 1, 23), 0)
context['hours'] = [datetime.time(hour=i) for i in range(start_time.hour, end_time.hour + 1)]
opening_range_minutes = (
(end_time.hour + 1) * 60 + end_time.minute - (start_time.hour * 60 + start_time.minute)
)
def get_time_ratio(t1, t2):
return 100 * ((t1.hour - t2.hour) * 60 + t1.minute - t2.minute) // 60
return str(
round(100 * ((t1.hour - t2.hour) * 60 + t1.minute - t2.minute) / opening_range_minutes, 2)
)
bookings_by_user = collections.defaultdict(list)
for booking in event.booking_set.all():

View File

@ -104,21 +104,25 @@ def test_manager_partial_bookings_day_view(app, admin_user, freezer):
assert 'Month' not in resp.text
# time range from one hour before event start to one hour after end
assert (
resp.pyquery('thead th').text()
== '7 a.m. 8 a.m. 9 a.m. 10 a.m. 11 a.m. noon 1 p.m. 2 p.m. 3 p.m. 4 p.m. 5 p.m. 6 p.m. 7 p.m.'
assert resp.pyquery('.partial-booking--hour')[0].text == '07\u202fh'
assert resp.pyquery('.partial-booking--hour')[-1].text == '19\u202fh'
assert [int(x.text.replace('\u202fh', '')) for x in resp.pyquery('.partial-booking--hour')] == list(
range(7, 20)
)
assert len(resp.pyquery('tbody tr')) == 2
assert resp.pyquery('tbody tr th')[0].text == 'Jane Doe'
assert resp.pyquery('tbody tr th')[1].text == 'Jon Doe'
assert len(resp.pyquery('.partial-booking--registrant')) == 2
assert resp.pyquery('.registrant--name')[0].text == 'Jane Doe'
assert resp.pyquery('.registrant--name')[1].text == 'Jon Doe'
assert resp.pyquery('tbody tr div.booking')[0].text == '11 a.m. - 1:30 p.m.'
assert resp.pyquery('tbody tr div.booking')[0].attrib['style'] == 'left: 400%; width: 250%;'
assert resp.pyquery('tbody tr div.booking')[1].text == '8 a.m. - 10 a.m.'
assert resp.pyquery('tbody tr div.booking')[1].attrib['style'] == 'left: 100%; width: 200%;'
assert resp.pyquery('tbody tr div.booking')[2].text == 'noon - 2 p.m.'
assert resp.pyquery('tbody tr div.booking')[2].attrib['style'] == 'left: 500%; width: 200%;'
assert resp.pyquery('.registrant--bar')[0].findall('time')[0].text == '11:00'
assert resp.pyquery('.registrant--bar')[0].findall('time')[1].text == '13:30'
assert resp.pyquery('.registrant--bar')[0].attrib['style'] == 'left: 30.77%; width: 19.23%;'
assert resp.pyquery('.registrant--bar')[1].findall('time')[0].text == '08:00'
assert resp.pyquery('.registrant--bar')[1].findall('time')[1].text == '10:00'
assert resp.pyquery('.registrant--bar')[1].attrib['style'] == 'left: 7.69%; width: 15.38%;'
assert resp.pyquery('.registrant--bar')[2].findall('time')[0].text == '12:00'
assert resp.pyquery('.registrant--bar')[2].findall('time')[1].text == '14:00'
assert resp.pyquery('.registrant--bar')[2].attrib['style'] == 'left: 38.46%; width: 15.38%;'
resp = resp.click('Next day')
assert 'No opening hours this day.' in resp.text
@ -135,7 +139,7 @@ def test_manager_partial_bookings_day_view_24_hours(app, admin_user, freezer):
today = start_datetime.date()
resp = app.get('/manage/agendas/%s/day/%d/%d/%d/' % (agenda.pk, today.year, today.month, today.day))
assert resp.pyquery('thead th').text() == (
'midnight 1 a.m. 2 a.m. 3 a.m. 4 a.m. 5 a.m. 6 a.m. 7 a.m. 8 a.m. 9 a.m. 10 a.m. 11 a.m. noon '
'1 p.m. 2 p.m. 3 p.m. 4 p.m. 5 p.m. 6 p.m. 7 p.m. 8 p.m. 9 p.m. 10 p.m. 11 p.m.'
# from 00h to 23h
assert [int(x.text.replace('\u202fh', '')) for x in resp.pyquery('.partial-booking--hour')] == list(
range(0, 24)
)