A stored file on a record shows as its name and opens from the row
Test / test (pull_request) Successful in 1m16s
Test / test (pull_request) Successful in 1m16s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GT37Z1Xtfd9pUuQtMTg6Yt
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a822f2175f
commit
c766dedc0e
+38
@@ -1400,6 +1400,7 @@ fn AlternativeCard(
|
||||
feature_name=feature_name.clone()
|
||||
transitions=spec.transitions.clone()
|
||||
link=spec.link.clone()
|
||||
bucket=spec.bucket().map(str::to_string)
|
||||
empty=spec.empty.clone()
|
||||
pending_transitions=pending_transitions
|
||||
transition_batch=transition_batch
|
||||
@@ -1853,6 +1854,7 @@ fn ResourceFeature(
|
||||
feature_name: String,
|
||||
transitions: Vec<Transition>,
|
||||
link: Option<RowLink>,
|
||||
bucket: Option<String>,
|
||||
empty: Option<String>,
|
||||
pending_transitions: RwSignal<std::collections::HashMap<(String, String), String>>,
|
||||
transition_batch: ServerAction<TransitionAnswers>,
|
||||
@@ -1888,6 +1890,7 @@ fn ResourceFeature(
|
||||
let feature_name = feature_name.clone();
|
||||
let transitions = transitions.clone();
|
||||
let link = link.clone();
|
||||
let bucket = bucket.clone();
|
||||
let empty = empty.clone().unwrap_or_else(|| t(&lang().get(), "nothing_here_yet").to_string());
|
||||
data.get()
|
||||
.map(|res| match res {
|
||||
@@ -1897,6 +1900,7 @@ fn ResourceFeature(
|
||||
feature_name=feature_name
|
||||
transitions=transitions
|
||||
link=link
|
||||
bucket=bucket
|
||||
empty=empty
|
||||
value=value
|
||||
pending_transitions=pending_transitions
|
||||
@@ -1925,6 +1929,7 @@ fn ResourceValue(
|
||||
feature_name: String,
|
||||
transitions: Vec<Transition>,
|
||||
link: Option<RowLink>,
|
||||
bucket: Option<String>,
|
||||
empty: String,
|
||||
value: serde_json::Value,
|
||||
pending_transitions: RwSignal<std::collections::HashMap<(String, String), String>>,
|
||||
@@ -1957,12 +1962,14 @@ fn ResourceValue(
|
||||
let feature_name = feature_name.clone();
|
||||
let transitions = transitions.clone();
|
||||
let link = link.clone();
|
||||
let bucket = bucket.clone();
|
||||
move |answer: Answer| {
|
||||
view! {
|
||||
<AnswerRow
|
||||
feature_name=feature_name.clone()
|
||||
transitions=transitions.clone()
|
||||
link=link.clone()
|
||||
bucket=bucket.clone()
|
||||
answer=answer
|
||||
pending_transitions=pending_transitions
|
||||
transition_batch=transition_batch
|
||||
@@ -2217,11 +2224,20 @@ fn SelectField(
|
||||
}
|
||||
}
|
||||
|
||||
/// The file's own name, if a field's value is an upload's key: a
|
||||
/// uuid, a dash, and the name it was sent with (`upload.rs`).
|
||||
pub fn uploaded_filename(value: &str) -> Option<&str> {
|
||||
let (id, rest) = value.split_at_checked(36)?;
|
||||
let shaped = id.char_indices().all(|(i, c)| if matches!(i, 8 | 13 | 18 | 23) { c == '-' } else { c.is_ascii_hexdigit() });
|
||||
rest.strip_prefix('-').filter(|name| shaped && !name.is_empty())
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn AnswerRow(
|
||||
feature_name: String,
|
||||
transitions: Vec<Transition>,
|
||||
link: Option<RowLink>,
|
||||
bucket: Option<String>,
|
||||
answer: Answer,
|
||||
pending_transitions: RwSignal<std::collections::HashMap<(String, String), String>>,
|
||||
transition_batch: ServerAction<TransitionAnswers>,
|
||||
@@ -2255,6 +2271,9 @@ fn AnswerRow(
|
||||
let item_id = answer.id.clone();
|
||||
let key = (feature_name.clone(), item_id.clone());
|
||||
let link = link.map(|l| (l.href(&answer.id, &answer.responses), l.label));
|
||||
// Where a stored file on this record is fetched from: the record
|
||||
// names it, never the key (see `upload::attachment`).
|
||||
let attachment_base = bucket.map(|b| format!("/attachment/{b}/{}", answer.id));
|
||||
|
||||
view! {
|
||||
<article class="answer-row" class:decided=move || decided>
|
||||
@@ -2288,6 +2307,17 @@ fn AnswerRow(
|
||||
.into_any();
|
||||
}
|
||||
let text = value.as_str().map(str::to_string).unwrap_or_else(|| value.to_string());
|
||||
// A stored file shows as its name, and opens.
|
||||
if let (Some(base), Some(filename)) = (attachment_base.clone(), uploaded_filename(&text)) {
|
||||
let href = format!("{base}/{key}");
|
||||
return view! {
|
||||
<div class="answer-field">
|
||||
<span class="answer-field-key">{key}</span>
|
||||
<a class="answer-field-value answer-file" href=href rel="external">{filename.to_string()}</a>
|
||||
</div>
|
||||
}
|
||||
.into_any();
|
||||
}
|
||||
view! {
|
||||
<div class="answer-field">
|
||||
<span class="answer-field-key">{key}</span>
|
||||
@@ -3028,6 +3058,14 @@ mod responsible_tests {
|
||||
Responsible { name: "R".into(), contact: contact.into(), group: group.map(String::from) }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_uploads_key_shows_as_the_name_it_was_sent_with() {
|
||||
assert_eq!(super::uploaded_filename("3f2b8c1e-9a4d-4e7b-8c21-0a1b2c3d4e5f-rapport 2026.pdf"), Some("rapport 2026.pdf"));
|
||||
assert_eq!(super::uploaded_filename("rapport.pdf"), None);
|
||||
assert_eq!(super::uploaded_filename("3f2b8c1e-9a4d-4e7b-8c21-0a1b2c3d4e5f-"), None);
|
||||
assert_eq!(super::uploaded_filename("Storgata 1, 1850 Mysen - se vedlegg i e-post fra oss"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_the_person_a_page_names_may_suggest_a_change_to_it() {
|
||||
assert!(is_responsible(Some(&user(Some("kari@vel.no"), &[])), &named("mailto:Kari@Vel.no", None)));
|
||||
|
||||
Reference in New Issue
Block a user