agenda/templates/conference_list.html
Edward Betts 1fff1f136d Show day of week in conference date column
Add %a prefix: "Wed 25 Mar 2026", "Wed 25–28 Mar 2026", etc.

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

261 lines
7.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;
}
/* Bidirectional hover highlight */
.conf-tl-bar.conf-hl {
filter: brightness(1.25);
box-shadow: inset 0 0 0 2px rgba(255,255,255,0.8);
z-index: 15;
}
tr.conf-hl > td {
background-color: rgba(255, 193, 7, 0.25) !important;
}
/* Conference table */
.conf-section-row td {
background: #343a40 !important;
color: #fff;
font-weight: 700;
font-size: 0.85em;
padding-top: 0.5rem;
padding-bottom: 0.4rem;
border-bottom: none;
}
.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 }}"
data-conf-key="{{ conf.key }}">
{% if conf.url %}<a href="{{ conf.url }}">{{ conf.name }}</a>
{% else %}<span>{{ conf.name }}</span>{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endmacro %}
{% macro conf_rows(heading, item_list, badge) %}
{% if item_list %}
{% set count = item_list | length %}
<tr class="conf-section-row">
<td colspan="6">{{ heading }} <span class="fw-normal opacity-75">{{ count }} conference{{ "" if count == 1 else "s" }}</span></td>
</tr>
{% 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 %} data-conf-key="{{ item.start_date.isoformat() }}|{{ item.name }}">
<td class="text-nowrap text-muted small">
{%- if item.start_date == item.end_date -%}
{{ item.start_date.strftime("%a %-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("%a %-d") }}{{ item.end_date.strftime("%-d %b %Y") }}
{%- else -%}
{{ item.start_date.strftime("%a %-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 %}
{% endif %}
{% endmacro %}
{% block content %}
<div class="container-fluid mt-2">
<h1>Conferences</h1>
{{ render_timeline(timeline) }}
<table class="table table-sm table-hover align-middle">
<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>
{{ conf_rows("Current", current, "attending") }}
{{ conf_rows("Future", future, "going") }}
{{ conf_rows("Past", past|reverse|list, "went") }}
</tbody>
</table>
</div>
<script>
(function () {
// Build a map from conf-key → all matching elements (bars + rows)
const map = new Map();
document.querySelectorAll('[data-conf-key]').forEach(el => {
const k = el.dataset.confKey;
if (!map.has(k)) map.set(k, []);
map.get(k).push(el);
});
function setHighlight(key, on) {
(map.get(key) || []).forEach(el => el.classList.toggle('conf-hl', on));
}
map.forEach((els, key) => {
els.forEach(el => {
el.addEventListener('mouseenter', () => setHighlight(key, true));
el.addEventListener('mouseleave', () => setHighlight(key, false));
});
});
})();
</script>
{% endblock %}