compact calendar now

This commit is contained in:
Priec
2026-05-16 17:06:18 +02:00
parent a2c851a853
commit 7220ec87bc
2 changed files with 58 additions and 1 deletions

View File

@@ -95,7 +95,7 @@
<div class="text-sm font-medium opacity-60">{{ court_name }} · {{ week_label }}</div>
</div>
<div class="overflow-x-auto rounded-lg border border-base-300 bg-base-100 shadow-sm">
<div class="overflow-x-auto border border-base-300 bg-base-100 shadow-sm">
<table id="cal" class="w-full border-collapse text-sm">
<thead>
<tr class="bg-base-200">
@@ -110,6 +110,14 @@
</thead>
<tbody>
{% for row in rows %}
{% if row.free_group %}
<tr>
<td class="border border-base-300 bg-base-200 px-2 py-1 text-center text-xs font-medium">{{ row.hour_label }}</td>
<td colspan="7" class="border border-base-300 px-2 py-1">
<div class="text-center text-xs uppercase tracking-wide opacity-30">{{ t(key="free", lang=lang) }}</div>
</td>
</tr>
{% else %}
<tr>
<td class="border border-base-300 bg-base-200 p-2 text-center font-medium opacity-70">{{ row.hour_label }}</td>
{% for cell in row.cells %}
@@ -147,6 +155,7 @@
</td>
{% endfor %}
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>

View File

@@ -64,6 +64,9 @@ pub struct Cell {
pub struct Row {
pub hour_label: String,
pub cells: Vec<Cell>,
/// Public view only: `true` when this row collapses a run of fully-free
/// hours into one compact strip. The admin grid never sets it.
pub free_group: bool,
}
#[derive(Debug, Serialize)]
@@ -107,6 +110,46 @@ fn week_monday(week: Option<&str>) -> NaiveDate {
monday_of(base)
}
/// Collapses runs of fully-free hour rows into one compact strip so a court
/// that isn't booked solid doesn't waste vertical space. Rows holding at
/// least one booking are kept full-size so bookings stay easy to read.
/// Public calendar only — the admin grid always shows every hour.
fn group_free_rows(rows: Vec<Row>) -> Vec<Row> {
let mut out: Vec<Row> = Vec::new();
let mut run: Vec<Row> = Vec::new();
for row in rows {
if row.cells.iter().all(|c| !c.booked) {
run.push(row);
} else {
flush_free_run(&mut run, &mut out);
out.push(row);
}
}
flush_free_run(&mut run, &mut out);
out
}
/// Drains a pending run of free rows into `out` as a single collapsed row.
fn flush_free_run(run: &mut Vec<Row>, out: &mut Vec<Row>) {
if run.is_empty() {
return;
}
let hour_of = |r: &Row| r.cells.first().map_or(FIRST_HOUR, |c| c.hour);
let first = run.first().map_or(FIRST_HOUR, hour_of);
let last = run.last().map_or(first, hour_of);
let hour_label = if run.len() > 1 {
format!("{first:02}:00 {:02}:00", last + 1)
} else {
format!("{first:02}:00")
};
out.push(Row {
hour_label,
cells: Vec::new(),
free_group: true,
});
run.clear();
}
/// Builds the calendar grid for the selected court and week.
pub async fn build_calendar(
ctx: &AppContext,
@@ -201,10 +244,15 @@ pub async fn build_calendar(
Row {
hour_label: format!("{hour:02}:00"),
cells,
free_group: false,
}
})
.collect();
// The public calendar collapses empty stretches to stay compact; the admin
// grid always shows every hour so each slot stays individually editable.
let rows = if is_admin { rows } else { group_free_rows(rows) };
Ok(CalendarPage {
lang: lang.to_string(),
is_admin,