feat(home): add inline detail subtitles to timeline preview cards
Show contextual details inline with event titles on home page timeline cards. Each event type picks relevant fields: brew shows roaster and brewer, roast shows roaster and origin, cafe shows city and country, cup shows roaster and cafe, others show first three detail values.
This commit is contained in:
parent
cdf90d1ff3
commit
939743a224
2 changed files with 40 additions and 1 deletions
|
|
@ -34,6 +34,7 @@ pub struct TimelineEventView {
|
||||||
pub link: String,
|
pub link: String,
|
||||||
pub external_link: Option<String>,
|
pub external_link: Option<String>,
|
||||||
pub details: Vec<TimelineEventDetailView>,
|
pub details: Vec<TimelineEventDetailView>,
|
||||||
|
pub subtitle: Option<String>,
|
||||||
pub tasting_notes: Option<Vec<String>>,
|
pub tasting_notes: Option<Vec<String>>,
|
||||||
pub brew_data: Option<TimelineBrewDataView>,
|
pub brew_data: Option<TimelineBrewDataView>,
|
||||||
}
|
}
|
||||||
|
|
@ -104,6 +105,8 @@ impl TimelineEventView {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let subtitle = Self::build_subtitle(entity_type.as_str(), &mapped_details);
|
||||||
|
|
||||||
let brew_data_view = brew_data.map(|bd| TimelineBrewDataView {
|
let brew_data_view = brew_data.map(|bd| TimelineBrewDataView {
|
||||||
bag_id: bd.bag_id,
|
bag_id: bd.bag_id,
|
||||||
grinder_id: bd.grinder_id,
|
grinder_id: bd.grinder_id,
|
||||||
|
|
@ -126,11 +129,47 @@ impl TimelineEventView {
|
||||||
link,
|
link,
|
||||||
external_link,
|
external_link,
|
||||||
details: mapped_details,
|
details: mapped_details,
|
||||||
|
subtitle,
|
||||||
tasting_notes,
|
tasting_notes,
|
||||||
brew_data: brew_data_view,
|
brew_data: brew_data_view,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_subtitle(entity_type: &str, details: &[TimelineEventDetailView]) -> Option<String> {
|
||||||
|
let find_value = |label: &str| {
|
||||||
|
details
|
||||||
|
.iter()
|
||||||
|
.find(|d| d.label.eq_ignore_ascii_case(label))
|
||||||
|
.map(|d| d.value.trim())
|
||||||
|
.filter(|v| !v.is_empty() && *v != "\u{2014}")
|
||||||
|
};
|
||||||
|
|
||||||
|
let picks: &[&str] = match entity_type {
|
||||||
|
"brew" => &["Roaster", "Brewer"],
|
||||||
|
"roast" => &["Roaster", "Origin"],
|
||||||
|
"cafe" => &["City", "Country"],
|
||||||
|
"cup" => &["Roaster", "Cafe"],
|
||||||
|
_ => &[],
|
||||||
|
};
|
||||||
|
|
||||||
|
let parts: Vec<&str> = if picks.is_empty() {
|
||||||
|
details
|
||||||
|
.iter()
|
||||||
|
.take(3)
|
||||||
|
.map(|d| d.value.trim())
|
||||||
|
.filter(|v| !v.is_empty() && *v != "\u{2014}")
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
picks.iter().filter_map(|l| find_value(l)).collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
if parts.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(parts.join(" \u{00b7} "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn map_details(
|
fn map_details(
|
||||||
details: Vec<TimelineEventDetail>,
|
details: Vec<TimelineEventDetail>,
|
||||||
) -> (Vec<TimelineEventDetailView>, Option<String>) {
|
) -> (Vec<TimelineEventDetailView>, Option<String>) {
|
||||||
|
|
|
||||||
|
|
@ -263,7 +263,7 @@
|
||||||
<div class="rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 shadow-sm flex items-center justify-between gap-3">
|
<div class="rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 shadow-sm flex items-center justify-between gap-3">
|
||||||
<div class="flex items-center gap-3 min-w-0">
|
<div class="flex items-center gap-3 min-w-0">
|
||||||
<span class="inline-flex w-28 shrink-0 items-center justify-center whitespace-nowrap rounded-full px-2 py-0.5 text-xs font-semibold bg-amber-200 text-amber-800">{{ event.kind_label }}</span>
|
<span class="inline-flex w-28 shrink-0 items-center justify-center whitespace-nowrap rounded-full px-2 py-0.5 text-xs font-semibold bg-amber-200 text-amber-800">{{ event.kind_label }}</span>
|
||||||
<a href="{{ event.link }}" class="truncate font-medium text-stone-700 hover:text-amber-600 text-sm">{{ event.title }}</a>
|
<p class="min-w-0 truncate text-sm"><a href="{{ event.link }}" class="font-medium text-stone-700 hover:text-amber-600">{{ event.title }}</a>{% if let Some(sub) = event.subtitle %} <span class="text-xs text-stone-400 italic">· {{ sub }}</span>{% endif %}</p>
|
||||||
</div>
|
</div>
|
||||||
<time class="text-xs text-stone-500 whitespace-nowrap shrink-0">{{ event.relative_date_label }}</time>
|
<time class="text-xs text-stone-500 whitespace-nowrap shrink-0">{{ event.relative_date_label }}</time>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue