agenda/templates/conference_list.html
Edward Betts ef517c98ff Replace CSS grid with Bootstrap table on conference list page
- Proper <table> with colgroup widths, table-sm table-hover align-middle
- Month-divider rows (MARCH 2026, APRIL 2026, …) break up long lists
- Date ranges collapsed to a single column (e.g. "25–28 Mar 2026")
- Row highlight (conf-going) for conferences marked going=true
- Topic and date columns styled text-muted small to reduce visual noise
- Trip links replaced with 🧳 emoji: shows just the emoji when the trip
  title matches the conference name (the common case), otherwise appends
  the trip title (e.g. "🧳 Budapest" for FOSDEM); full title always in
  tooltip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 10:56:44 +00:00

216 lines
6.5 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% from "macros.html" import trip_link with context %}
{% block title %}Conferences - Edward Betts{% endblock %}
{% block style %}
<style>
/* Timeline */
.conf-timeline {
position: relative;
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
overflow: hidden;
user-select: none;
}
.conf-tl-month {
position: absolute;
top: 0;
height: 100%;
border-left: 1px solid #dee2e6;
pointer-events: none;
}
.conf-tl-month-label {
position: absolute;
top: 3px;
left: 3px;
font-size: 0.68em;
color: #6c757d;
white-space: nowrap;
}
.conf-tl-today {
position: absolute;
top: 22px;
height: calc(100% - 22px);
border-left: 2px solid #dc3545;
pointer-events: none;
z-index: 10;
}
.conf-tl-bar {
position: absolute;
height: 26px;
border-radius: 3px;
overflow: hidden;
z-index: 5;
}
.conf-tl-bar a, .conf-tl-bar span {
display: block;
padding: 4px 6px;
font-size: 0.72em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 18px;
color: white;
text-decoration: none;
}
/* Conference table */
.conf-month-row td {
background: #e9ecef !important;
font-weight: 600;
font-size: 0.8em;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #495057;
padding-top: 0.5rem;
padding-bottom: 0.3rem;
border-bottom: none;
}
.conf-going {
--bs-table-bg: rgba(25, 135, 84, 0.07);
}
</style>
{% endblock %}
{% set tl_colors = ["#0d6efd","#198754","#dc3545","#fd7e14","#6f42c1","#20c997","#0dcaf0","#d63384"] %}
{% macro render_timeline(timeline) %}
{% if timeline %}
{% set row_h = 32 %}
{% set header_h = 22 %}
{% set total_h = timeline.lane_count * row_h + header_h %}
<div class="mb-4">
<h3>Next 90 days</h3>
<div class="conf-timeline" style="height: {{ total_h }}px;">
{% for m in timeline.months %}
<div class="conf-tl-month" style="left: {{ m.left_pct }}%;">
<span class="conf-tl-month-label">{{ m.label }}</span>
</div>
{% endfor %}
<div class="conf-tl-today" style="left: 0;"></div>
{% for conf in timeline.confs %}
{% set color = tl_colors[conf.lane % tl_colors | length] %}
{% set top_px = conf.lane * row_h + header_h %}
<div class="conf-tl-bar"
style="left: {{ conf.left_pct }}%; width: {{ conf.width_pct }}%; top: {{ top_px }}px; background: {{ color }};"
title="{{ conf.label }}">
{% if conf.url %}<a href="{{ conf.url }}">{{ conf.name }}</a>
{% else %}<span>{{ conf.name }}</span>{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endmacro %}
{% macro conf_table(heading, item_list, badge) %}
{% if item_list %}
{% set count = item_list | length %}
<h2>{{ heading }} <small class="text-muted fs-6 fw-normal">{{ count }} conference{{ "" if count == 1 else "s" }}</small></h2>
<table class="table table-sm table-hover align-middle mb-4">
<colgroup>
<col style="width: 9rem">
<col>
<col style="width: 18rem">
<col style="width: 14rem">
<col style="width: 7rem">
<col style="width: 10rem">
</colgroup>
<thead class="table-light">
<tr>
<th>Dates</th>
<th>Conference</th>
<th>Topic</th>
<th>Location</th>
<th>CFP ends</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{% set ns = namespace(prev_month="") %}
{% for item in item_list %}
{% set month_label = item.start_date.strftime("%B %Y") %}
{% if month_label != ns.prev_month %}
{% set ns.prev_month = month_label %}
<tr class="conf-month-row">
<td colspan="6">{{ month_label }}</td>
</tr>
{% endif %}
<tr{% if item.going %} class="conf-going"{% endif %}>
<td class="text-nowrap text-muted small">
{%- if item.start_date == item.end_date -%}
{{ item.start_date.strftime("%-d %b %Y") }}
{%- elif item.start_date.year == item.end_date.year and item.start_date.month == item.end_date.month -%}
{{ item.start_date.strftime("%-d") }}{{ item.end_date.strftime("%-d %b %Y") }}
{%- else -%}
{{ item.start_date.strftime("%-d %b") }}{{ item.end_date.strftime("%-d %b %Y") }}
{%- endif -%}
</td>
<td>
{% if item.url %}<a href="{{ item.url }}">{{ item.name }}</a>
{% else %}{{ item.name }}{% endif %}
{% if item.going and not (item.accommodation_booked or item.travel_booked) %}
<span class="badge text-bg-primary ms-1">{{ badge }}</span>
{% endif %}
{% if item.accommodation_booked %}
<span class="badge text-bg-success ms-1">accommodation</span>
{% endif %}
{% if item.transport_booked %}
<span class="badge text-bg-success ms-1">transport</span>
{% endif %}
{% if item.linked_trip %}
{% set trip = item.linked_trip %}
<a href="{{ url_for('trip_page', start=trip.start.isoformat()) }}"
class="text-muted ms-1 text-decoration-none"
title="Trip: {{ trip.title }}">
🧳{% if trip.title != item.name %} {{ trip.title }}{% endif %}
</a>
{% endif %}
</td>
<td class="text-muted small">{{ item.topic }}</td>
<td class="text-nowrap">
{% set country = get_country(item.country) if item.country else None %}
{% if country %}{{ country.flag }} {{ item.location }}
{% elif item.online %}💻 Online
{% else %}{{ item.location }}{% endif %}
</td>
<td class="text-nowrap text-muted small">
{% if item.cfp_end %}{{ item.cfp_end.strftime("%-d %b %Y") }}{% endif %}
</td>
<td>
{% if item.price and item.currency %}
<span class="badge bg-info text-nowrap">{{ "{:,d}".format(item.price | int) }} {{ item.currency }}</span>
{% if item.currency != "GBP" and item.currency in fx_rate %}
<span class="badge bg-info text-nowrap">{{ "{:,.0f}".format(item.price / fx_rate[item.currency]) }} GBP</span>
{% endif %}
{% elif item.free %}
<span class="badge bg-success text-nowrap">free</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endmacro %}
{% block content %}
<div class="container-fluid mt-2">
<h1>Conferences</h1>
{{ render_timeline(timeline) }}
{{ conf_table("Current", current, "attending") }}
{{ conf_table("Future", future, "going") }}
{{ conf_table("Past", past|reverse|list, "went") }}
</div>
{% endblock %}