feat(timeline): add country flags to detail rows, allow multiple cards open
- Add flag emojis to Origin and Country detail rows in timeline events - Change expanded-card signal from single-value to comma-separated list so multiple timeline cards can be open simultaneously - Build subtitle before adding flags to keep condensed text clean
This commit is contained in:
parent
639d748384
commit
a9fd069a3c
2 changed files with 26 additions and 11 deletions
|
|
@ -1,3 +1,4 @@
|
|||
use crate::domain::countries::{country_to_iso, iso_to_flag_emoji};
|
||||
use crate::domain::timeline::{TimelineEvent, TimelineEventDetail};
|
||||
|
||||
use super::relative_date;
|
||||
|
|
@ -114,7 +115,12 @@ impl TimelineEventView {
|
|||
_ => String::from("#"),
|
||||
};
|
||||
|
||||
let (mapped_details, external_link) = Self::map_details(details);
|
||||
let (mut mapped_details, external_link) = Self::map_details(details);
|
||||
|
||||
// Build subtitle before adding flags so it stays clean text.
|
||||
let subtitle = Self::build_subtitle(entity_type.as_str(), &mapped_details);
|
||||
|
||||
Self::add_country_flags(&mut mapped_details);
|
||||
|
||||
let tasting_notes = if entity_type == "roast" {
|
||||
let notes = tasting_notes
|
||||
|
|
@ -132,8 +138,6 @@ impl TimelineEventView {
|
|||
None
|
||||
};
|
||||
|
||||
let subtitle = Self::build_subtitle(entity_type.as_str(), &mapped_details);
|
||||
|
||||
let brew_data_view = brew_data.map(|bd| TimelineBrewDataView {
|
||||
bag_id: bd.bag_id,
|
||||
grinder_id: bd.grinder_id,
|
||||
|
|
@ -247,4 +251,15 @@ impl TimelineEventView {
|
|||
|
||||
(mapped, external_link)
|
||||
}
|
||||
|
||||
fn add_country_flags(details: &mut [TimelineEventDetailView]) {
|
||||
for detail in details.iter_mut() {
|
||||
let label_lower = detail.label.to_ascii_lowercase();
|
||||
if matches!(label_lower.as_str(), "origin" | "country")
|
||||
&& let Some(flag) = country_to_iso(detail.value.trim()).map(iso_to_flag_emoji)
|
||||
{
|
||||
detail.value = format!("{flag} {}", detail.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
></span>
|
||||
<div
|
||||
class="tl-card rounded-lg border bg-surface p-5 text-left"
|
||||
data-on:click="if (!evt.target.closest('a, button, form')) { if ($_expandedMonths.includes(',{{ month.anchor }}')) { $_collapsedCards = $_collapsedCards.includes(',{{ event.id }}') ? $_collapsedCards.replace(',{{ event.id }}', '') : $_collapsedCards + ',{{ event.id }}' } else { $_expandedCard = $_expandedCard === '{{ event.id }}' ? '' : '{{ event.id }}' } }"
|
||||
data-on:click="if (!evt.target.closest('a, button, form')) { if ($_expandedMonths.includes(',{{ month.anchor }}')) { $_collapsedCards = $_collapsedCards.includes(',{{ event.id }}') ? $_collapsedCards.replace(',{{ event.id }}', '') : $_collapsedCards + ',{{ event.id }}' } else { $_expandedCard = $_expandedCard.includes(',{{ event.id }}') ? $_expandedCard.replace(',{{ event.id }}', '') : $_expandedCard + ',{{ event.id }}' } }"
|
||||
>
|
||||
<div class="flex flex-wrap items-center justify-between gap-2">
|
||||
{# Category — always visible #}
|
||||
|
|
@ -30,20 +30,20 @@
|
|||
{% if event.entity_type == "brew" %}{{ icons::beaker("h-3 w-3 shrink-0") }}{% elif event.entity_type == "roast" %}{{ icons::coffee_bean("h-3 w-3 shrink-0") }}{% elif event.entity_type == "roaster" %}{{ icons::fire("h-3 w-3 shrink-0") }}{% elif event.entity_type == "bag" %}{{ icons::bag("h-3 w-3 shrink-0") }}{% elif event.entity_type == "cup" %}{{ icons::cup("h-3 w-3 shrink-0") }}{% elif event.entity_type == "cafe" %}{{ icons::location("h-3 w-3 shrink-0") }}{% elif event.entity_type == "gear" %}{{ icons::grinder("h-3 w-3 shrink-0") }}{% endif %}
|
||||
<span class="uppercase tracking-wide">{{ event.kind_label }}</span>
|
||||
{# Relative date — shown when collapsed #}
|
||||
<span class="tl-condensed uppercase tracking-wide" data-show="($_expandedCard !== '{{ event.id }}' && !$_expandedMonths.includes(',{{ month.anchor }}')) || $_collapsedCards.includes(',{{ event.id }}')"> · {{ event.relative_date_label }}</span>
|
||||
<span class="tl-condensed uppercase tracking-wide" data-show="(!$_expandedCard.includes(',{{ event.id }}') && !$_expandedMonths.includes(',{{ month.anchor }}')) || $_collapsedCards.includes(',{{ event.id }}')"> · {{ event.relative_date_label }}</span>
|
||||
{# Full timestamp — shown when expanded #}
|
||||
<time
|
||||
datetime="{{ event.iso_timestamp }}"
|
||||
class="tl-detail uppercase tracking-wide"
|
||||
data-show="($_expandedCard === '{{ event.id }}' || $_expandedMonths.includes(',{{ month.anchor }}')) && !$_collapsedCards.includes(',{{ event.id }}')"
|
||||
data-show="($_expandedCard.includes(',{{ event.id }}') || $_expandedMonths.includes(',{{ month.anchor }}')) && !$_collapsedCards.includes(',{{ event.id }}')"
|
||||
style="display:none"
|
||||
> · {{ event.date_label }}{% if let Some(label) = event.time_label %} · {{ label }}{% endif %}</time>
|
||||
</span>
|
||||
{# Expand/collapse chevron #}
|
||||
<span class="tl-chevron text-text-muted" data-show="($_expandedCard !== '{{ event.id }}' && !$_expandedMonths.includes(',{{ month.anchor }}')) || $_collapsedCards.includes(',{{ event.id }}')">
|
||||
<span class="tl-chevron text-text-muted" data-show="(!$_expandedCard.includes(',{{ event.id }}') && !$_expandedMonths.includes(',{{ month.anchor }}')) || $_collapsedCards.includes(',{{ event.id }}')">
|
||||
{{ icons::chevron_down("h-4 w-4") }}
|
||||
</span>
|
||||
<span class="tl-chevron text-text-muted" data-show="($_expandedCard === '{{ event.id }}' || $_expandedMonths.includes(',{{ month.anchor }}')) && !$_collapsedCards.includes(',{{ event.id }}')" style="display:none">
|
||||
<span class="tl-chevron text-text-muted" data-show="($_expandedCard.includes(',{{ event.id }}') || $_expandedMonths.includes(',{{ month.anchor }}')) && !$_collapsedCards.includes(',{{ event.id }}')" style="display:none">
|
||||
{{ icons::chevron_up("h-4 w-4") }}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
<a
|
||||
href="{{ url }}"
|
||||
class="tl-detail ml-auto text-accent transition hover:text-accent-hover"
|
||||
data-show="($_expandedCard === '{{ event.id }}' || $_expandedMonths.includes(',{{ month.anchor }}')) && !$_collapsedCards.includes(',{{ event.id }}')"
|
||||
data-show="($_expandedCard.includes(',{{ event.id }}') || $_expandedMonths.includes(',{{ month.anchor }}')) && !$_collapsedCards.includes(',{{ event.id }}')"
|
||||
style="display:none"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
|
|
@ -69,14 +69,14 @@
|
|||
{% if let Some(sub) = event.subtitle %}
|
||||
<p
|
||||
class="tl-condensed mt-1 text-sm text-text-muted"
|
||||
data-show="($_expandedCard !== '{{ event.id }}' && !$_expandedMonths.includes(',{{ month.anchor }}')) || $_collapsedCards.includes(',{{ event.id }}')"
|
||||
data-show="(!$_expandedCard.includes(',{{ event.id }}') && !$_expandedMonths.includes(',{{ month.anchor }}')) || $_collapsedCards.includes(',{{ event.id }}')"
|
||||
>{{ sub }}</p>
|
||||
{% endif %}
|
||||
{# Detail rows — shown when expanded #}
|
||||
{% if event.details.len() > 0 %}
|
||||
<dl
|
||||
class="tl-detail mt-4 flex flex-col gap-2 text-sm text-text-secondary"
|
||||
data-show="($_expandedCard === '{{ event.id }}' || $_expandedMonths.includes(',{{ month.anchor }}')) && !$_collapsedCards.includes(',{{ event.id }}')"
|
||||
data-show="($_expandedCard.includes(',{{ event.id }}') || $_expandedMonths.includes(',{{ month.anchor }}')) && !$_collapsedCards.includes(',{{ event.id }}')"
|
||||
style="display:none"
|
||||
>
|
||||
{% for detail in event.details %}
|
||||
|
|
|
|||
Loading…
Reference in a new issue