Clean up project structure
This commit is contained in:
108
brittle-ui/src/pub_detail.rs
Normal file
108
brittle-ui/src/pub_detail.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
//! Publication detail: right pane showing full reference fields.
|
||||
|
||||
use leptos::prelude::*;
|
||||
|
||||
use brittle_model::Reference;
|
||||
|
||||
/// Right pane: displays the fields of the currently selected reference.
|
||||
///
|
||||
/// When `reference` is `None`, a placeholder is shown.
|
||||
#[component]
|
||||
pub fn PubDetail(reference: RwSignal<Option<Reference>>) -> impl IntoView {
|
||||
use leptos::either::Either;
|
||||
|
||||
view! {
|
||||
<div class="pub-detail-pane">
|
||||
{move || match reference.get() {
|
||||
None => Either::Left(view! {
|
||||
<div class="empty-state">"Select a publication to see details"</div>
|
||||
}),
|
||||
Some(r) => Either::Right(view! {
|
||||
<div class="detail-content">
|
||||
<h2 class="detail-title">
|
||||
{r.fields.get("title").cloned().unwrap_or_else(|| "[no title]".into())}
|
||||
</h2>
|
||||
<dl class="detail-fields">
|
||||
// Entry type + cite key
|
||||
<dt>"Type"</dt>
|
||||
<dd>{r.entry_type.to_string()}</dd>
|
||||
|
||||
<dt>"Cite key"</dt>
|
||||
<dd class="mono">{r.cite_key.clone()}</dd>
|
||||
|
||||
// Authors
|
||||
{if r.authors.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let names = r.authors.iter()
|
||||
.map(|p| p.display_name())
|
||||
.collect::<Vec<_>>()
|
||||
.join("; ");
|
||||
Some(view! {
|
||||
<dt>"Authors"</dt>
|
||||
<dd>{names}</dd>
|
||||
})
|
||||
}}
|
||||
|
||||
// Editors
|
||||
{if r.editors.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let names = r.editors.iter()
|
||||
.map(|p| p.display_name())
|
||||
.collect::<Vec<_>>()
|
||||
.join("; ");
|
||||
Some(view! {
|
||||
<dt>"Editors"</dt>
|
||||
<dd>{names}</dd>
|
||||
})
|
||||
}}
|
||||
|
||||
// Prioritised well-known fields
|
||||
{PRIORITY_FIELDS.iter().filter_map(|&key| {
|
||||
r.fields.get(key).map(|val| view! {
|
||||
<dt>{field_label(key)}</dt>
|
||||
<dd>{val.clone()}</dd>
|
||||
})
|
||||
}).collect::<Vec<_>>()}
|
||||
|
||||
// Remaining fields in alphabetical order
|
||||
{r.fields.iter()
|
||||
.filter(|(k, _)| !PRIORITY_FIELDS.contains(&k.as_str())
|
||||
&& *k != "title")
|
||||
.map(|(k, v)| view! {
|
||||
<dt>{field_label(k)}</dt>
|
||||
<dd>{v.clone()}</dd>
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
</dl>
|
||||
<p class="detail-timestamps">
|
||||
"Modified: "{r.modified_at.format("%Y-%m-%d %H:%M UTC").to_string()}
|
||||
</p>
|
||||
</div>
|
||||
}),
|
||||
}}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields shown before the alphabetical remainder (excluding "title").
|
||||
const PRIORITY_FIELDS: &[&str] = &["year", "journal", "booktitle", "volume", "doi", "abstract"];
|
||||
|
||||
/// Pretty-print a BibTeX field key.
|
||||
fn field_label(key: &str) -> String {
|
||||
match key {
|
||||
"doi" => "DOI".into(),
|
||||
"isbn" => "ISBN".into(),
|
||||
"issn" => "ISSN".into(),
|
||||
"url" => "URL".into(),
|
||||
_ => {
|
||||
let mut s = key.replace('_', " ");
|
||||
if let Some(c) = s.get_mut(0..1) {
|
||||
c.make_ascii_uppercase();
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user