feat(lists): add flag emojis to country/origin columns

- Add country_flag to RoasterView and CafeView
- Add origin_flag to RoastView
- Display flag prefixes in roaster, cafe, and roast list partials
- Swap country/city column order in cafe list
This commit is contained in:
Jon Seager 2026-02-08 17:59:37 +00:00
parent f0eb346086
commit 639d748384
No known key found for this signature in database
6 changed files with 24 additions and 6 deletions

View file

@ -45,6 +45,7 @@ pub struct CafeView {
pub name: String, pub name: String,
pub city: String, pub city: String,
pub country: String, pub country: String,
pub country_flag: String,
pub latitude: f64, pub latitude: f64,
pub longitude: f64, pub longitude: f64,
pub map_url: String, pub map_url: String,
@ -75,6 +76,9 @@ impl From<Cafe> for CafeView {
let has_website = !website.is_empty(); let has_website = !website.is_empty();
let detail_path = format!("/cafes/{slug}"); let detail_path = format!("/cafes/{slug}");
let map_url = format!("https://www.google.com/maps?q={latitude},{longitude}"); let map_url = format!("https://www.google.com/maps?q={latitude},{longitude}");
let country_flag = country_to_iso(&country)
.map(iso_to_flag_emoji)
.unwrap_or_default();
let created_at_sort_key = created_at.timestamp(); let created_at_sort_key = created_at.timestamp();
let created_date = created_at.format("%Y-%m-%d").to_string(); let created_date = created_at.format("%Y-%m-%d").to_string();
@ -85,6 +89,7 @@ impl From<Cafe> for CafeView {
id: id.to_string(), id: id.to_string(),
name, name,
city, city,
country_flag,
country, country,
latitude, latitude,
longitude, longitude,

View file

@ -66,6 +66,7 @@ pub struct RoasterView {
pub detail_path: String, pub detail_path: String,
pub name: String, pub name: String,
pub country: String, pub country: String,
pub country_flag: String,
pub city: String, pub city: String,
pub has_homepage: bool, pub has_homepage: bool,
pub homepage_url: String, pub homepage_url: String,
@ -90,6 +91,9 @@ impl From<Roaster> for RoasterView {
let homepage = homepage.unwrap_or_default(); let homepage = homepage.unwrap_or_default();
let has_homepage = !homepage.is_empty(); let has_homepage = !homepage.is_empty();
let detail_path = format!("/roasters/{slug}"); let detail_path = format!("/roasters/{slug}");
let country_flag = country_to_iso(&country)
.map(iso_to_flag_emoji)
.unwrap_or_default();
let created_at_sort_key = created_at.timestamp(); let created_at_sort_key = created_at.timestamp();
let created_date = created_at.format("%Y-%m-%d").to_string(); let created_date = created_at.format("%Y-%m-%d").to_string();
@ -99,6 +103,7 @@ impl From<Roaster> for RoasterView {
detail_path, detail_path,
id: id.to_string(), id: id.to_string(),
name, name,
country_flag,
country, country,
city: city.unwrap_or_else(|| "".to_string()), city: city.unwrap_or_else(|| "".to_string()),
has_homepage, has_homepage,

View file

@ -1,3 +1,4 @@
use crate::domain::countries::{country_to_iso, iso_to_flag_emoji};
use crate::domain::roasters::Roaster; use crate::domain::roasters::Roaster;
use crate::domain::roasts::{Roast, RoastWithRoaster}; use crate::domain::roasts::{Roast, RoastWithRoaster};
@ -11,6 +12,7 @@ pub struct RoastView {
pub name: String, pub name: String,
pub roaster_label: String, pub roaster_label: String,
pub origin: String, pub origin: String,
pub origin_flag: String,
pub region: String, pub region: String,
pub producer: String, pub producer: String,
pub process: String, pub process: String,
@ -55,6 +57,11 @@ impl RoastView {
} else { } else {
roaster_name.to_string() roaster_name.to_string()
}; };
let origin_flag = origin
.as_deref()
.and_then(country_to_iso)
.map(iso_to_flag_emoji)
.unwrap_or_default();
let origin = origin.unwrap_or_else(|| "".to_string()); let origin = origin.unwrap_or_else(|| "".to_string());
let region = region.unwrap_or_else(|| "".to_string()); let region = region.unwrap_or_else(|| "".to_string());
let producer = producer.unwrap_or_else(|| "".to_string()); let producer = producer.unwrap_or_else(|| "".to_string());
@ -81,6 +88,7 @@ impl RoastView {
name, name,
roaster_label, roaster_label,
origin, origin,
origin_flag,
region, region,
producer, producer,
process, process,

View file

@ -25,8 +25,8 @@
<tr> <tr>
{{ table::sortable_header("Added", "created-at", navigator, "#cafe-list") }} {{ table::sortable_header("Added", "created-at", navigator, "#cafe-list") }}
{{ table::sortable_header("Name", "name", navigator, "#cafe-list") }} {{ table::sortable_header("Name", "name", navigator, "#cafe-list") }}
{{ table::sortable_header("City", "city", navigator, "#cafe-list") }}
{{ table::sortable_header("Country", "country", navigator, "#cafe-list") }} {{ table::sortable_header("Country", "country", navigator, "#cafe-list") }}
{{ table::sortable_header("City", "city", navigator, "#cafe-list") }}
<th scope="col" class="actions-col px-4 py-3 text-right"></th> <th scope="col" class="actions-col px-4 py-3 text-right"></th>
</tr> </tr>
</thead> </thead>
@ -48,10 +48,10 @@
<td data-label="Name" class="card-title px-4 py-3 font-medium text-text"> <td data-label="Name" class="card-title px-4 py-3 font-medium text-text">
{{ cafe.name }} {{ cafe.name }}
</td> </td>
<td data-label="Location" class="px-4 py-3 whitespace-nowrap md:hidden">{{ cafe.country }}</td> <td data-label="Location" class="px-4 py-3 whitespace-nowrap md:hidden">{% if !cafe.country_flag.is_empty() %}{{ cafe.country_flag }} {% endif %}{{ cafe.country }}</td>
<td data-label="City" class="px-4 py-3 whitespace-nowrap md:hidden">{{ cafe.city }}</td> <td data-label="City" class="px-4 py-3 whitespace-nowrap md:hidden">{{ cafe.city }}</td>
<td data-label="Country" class="mobile-hidden px-4 py-3 whitespace-nowrap">{% if !cafe.country_flag.is_empty() %}{{ cafe.country_flag }} {% endif %}{{ cafe.country }}</td>
<td data-label="City" class="mobile-hidden px-4 py-3 whitespace-nowrap">{{ cafe.city }}</td> <td data-label="City" class="mobile-hidden px-4 py-3 whitespace-nowrap">{{ cafe.city }}</td>
<td data-label="Country" class="mobile-hidden px-4 py-3 whitespace-nowrap">{{ cafe.country }}</td>
<td data-label="" class="card-actions px-4 py-3 text-right"> <td data-label="" class="card-actions px-4 py-3 text-right">
<a href="{{ cafe.detail_path }}" <a href="{{ cafe.detail_path }}"
class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt"> class="inline-flex h-8 w-8 items-center justify-center rounded-md text-text-muted transition hover:text-accent hover:bg-surface-alt">

View file

@ -52,7 +52,7 @@
</td> </td>
<td data-label="Roaster" class="px-4 py-3 whitespace-nowrap md:hidden">{{ roast.roaster_label }}</td> <td data-label="Roaster" class="px-4 py-3 whitespace-nowrap md:hidden">{{ roast.roaster_label }}</td>
<td data-label="Origin" class="px-4 py-3 whitespace-nowrap"> <td data-label="Origin" class="px-4 py-3 whitespace-nowrap">
{{ roast.origin }} {% if !roast.origin_flag.is_empty() %}{{ roast.origin_flag }} {% endif %}{{ roast.origin }}
{% if !roast.producer.is_empty() %} {% if !roast.producer.is_empty() %}
<div class="hidden md:block text-xs text-text-muted">{{ roast.producer }}</div> <div class="hidden md:block text-xs text-text-muted">{{ roast.producer }}</div>
{% endif %} {% endif %}

View file

@ -48,11 +48,11 @@
<td data-label="Name" class="card-title px-4 py-3 font-medium text-text"> <td data-label="Name" class="card-title px-4 py-3 font-medium text-text">
{{ roaster.name }} {{ roaster.name }}
</td> </td>
<td data-label="Location" class="px-4 py-3 whitespace-nowrap md:hidden">{{ roaster.country }}</td> <td data-label="Location" class="px-4 py-3 whitespace-nowrap md:hidden">{% if !roaster.country_flag.is_empty() %}{{ roaster.country_flag }} {% endif %}{{ roaster.country }}</td>
{% if !roaster.city.is_empty() %} {% if !roaster.city.is_empty() %}
<td data-label="City" class="px-4 py-3 whitespace-nowrap md:hidden">{{ roaster.city }}</td> <td data-label="City" class="px-4 py-3 whitespace-nowrap md:hidden">{{ roaster.city }}</td>
{% endif %} {% endif %}
<td data-label="Country" class="mobile-hidden px-4 py-3 whitespace-nowrap">{{ roaster.country }}</td> <td data-label="Country" class="mobile-hidden px-4 py-3 whitespace-nowrap">{% if !roaster.country_flag.is_empty() %}{{ roaster.country_flag }} {% endif %}{{ roaster.country }}</td>
<td data-label="City" class="mobile-hidden px-4 py-3 whitespace-nowrap">{{ roaster.city }}</td> <td data-label="City" class="mobile-hidden px-4 py-3 whitespace-nowrap">{{ roaster.city }}</td>
<td data-label="" class="card-actions px-4 py-3 text-right"> <td data-label="" class="card-actions px-4 py-3 text-right">
<a href="{{ roaster.detail_path }}" <a href="{{ roaster.detail_path }}"