- Split created_at into created_date + created_time across all view models - Add inline card-detail pattern for mobile action buttons - Add desktop detail-row with ellipsis toggle for table actions - Add pill-warning and pill-success CSS variants with dark mode - Center mobile card-detail buttons, fix card-date vertical spacing - Rename action labels: "View on Map" → "View Map", "Visit website/homepage" → "Homepage"
118 lines
3 KiB
Rust
118 lines
3 KiB
Rust
use crate::domain::cafes::Cafe;
|
|
use crate::infrastructure::foursquare::NearbyCafe;
|
|
|
|
pub struct CafeView {
|
|
pub id: String,
|
|
pub detail_path: String,
|
|
pub name: String,
|
|
pub city: String,
|
|
pub country: String,
|
|
pub latitude: f64,
|
|
pub longitude: f64,
|
|
pub map_url: String,
|
|
pub has_website: bool,
|
|
pub website_url: String,
|
|
pub website_label: String,
|
|
pub created_date: String,
|
|
pub created_time: String,
|
|
pub created_at_sort_key: i64,
|
|
}
|
|
|
|
impl From<Cafe> for CafeView {
|
|
fn from(cafe: Cafe) -> Self {
|
|
let Cafe {
|
|
id,
|
|
slug,
|
|
name,
|
|
city,
|
|
country,
|
|
latitude,
|
|
longitude,
|
|
website,
|
|
created_at,
|
|
updated_at: _,
|
|
} = cafe;
|
|
|
|
let website = website.unwrap_or_default();
|
|
let has_website = !website.is_empty();
|
|
let detail_path = format!("/cafes/{slug}");
|
|
let map_url = format!("https://www.google.com/maps?q={latitude},{longitude}");
|
|
|
|
let created_at_sort_key = created_at.timestamp();
|
|
let created_date = created_at.format("%Y-%m-%d").to_string();
|
|
let created_time = created_at.format("%H:%M").to_string();
|
|
|
|
Self {
|
|
detail_path,
|
|
id: id.to_string(),
|
|
name,
|
|
city,
|
|
country,
|
|
latitude,
|
|
longitude,
|
|
map_url,
|
|
has_website,
|
|
website_url: website.clone(),
|
|
website_label: website,
|
|
created_date,
|
|
created_time,
|
|
created_at_sort_key,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct CafeOptionView {
|
|
pub id: String,
|
|
pub label: String,
|
|
pub name: String,
|
|
pub city: String,
|
|
}
|
|
|
|
impl From<Cafe> for CafeOptionView {
|
|
fn from(cafe: Cafe) -> Self {
|
|
Self {
|
|
id: cafe.id.to_string(),
|
|
label: format!("{} ({})", cafe.name, cafe.city),
|
|
name: cafe.name,
|
|
city: cafe.city,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct NearbyCafeView {
|
|
pub name: String,
|
|
pub city: String,
|
|
pub country: String,
|
|
pub latitude: f64,
|
|
pub longitude: f64,
|
|
pub website: String,
|
|
pub distance: String,
|
|
pub location: String,
|
|
}
|
|
|
|
impl From<NearbyCafe> for NearbyCafeView {
|
|
fn from(cafe: NearbyCafe) -> Self {
|
|
let distance = if cafe.distance_meters < 1000 {
|
|
format!("{} m", cafe.distance_meters)
|
|
} else {
|
|
format!("{:.1} km", f64::from(cafe.distance_meters) / 1000.0)
|
|
};
|
|
let location = [&cafe.city, &cafe.country]
|
|
.iter()
|
|
.filter(|s| !s.is_empty())
|
|
.copied()
|
|
.cloned()
|
|
.collect::<Vec<_>>()
|
|
.join(", ");
|
|
Self {
|
|
name: cafe.name,
|
|
city: cafe.city,
|
|
country: cafe.country,
|
|
latitude: cafe.latitude,
|
|
longitude: cafe.longitude,
|
|
website: cafe.website.unwrap_or_default(),
|
|
distance,
|
|
location,
|
|
}
|
|
}
|
|
}
|