at main 144 lines 5.1 kB view raw
1#![allow(non_snake_case)] 2 3use crate::components::notebook::NotebookSettingsPanel; 4use crate::components::{AppLink, AppLinkTarget}; 5use crate::components::AuthorList; 6use crate::components::button::{Button, ButtonVariant}; 7use dioxus::prelude::*; 8use jacquard::IntoStatic; 9use jacquard::smol_str::SmolStr; 10use jacquard::types::ident::AtIdentifier; 11use weaver_api::sh_weaver::notebook::book::Book; 12use weaver_api::sh_weaver::notebook::NotebookView; 13 14const NOTEBOOK_COVER_CSS: Asset = asset!("/assets/styling/notebook-cover.css"); 15 16#[component] 17pub fn NotebookCover( 18 notebook: NotebookView<'static>, 19 title: String, 20 #[props(default = false)] is_owner: bool, 21 #[props(default)] ident: Option<AtIdentifier<'static>>, 22) -> Element { 23 use jacquard::from_data; 24 25 let mut show_settings = use_signal(|| false); 26 27 // Deserialize the book record from the view. 28 let book: Book<'static> = match from_data::<Book>(&notebook.record) { 29 Ok(book) => book.into_static(), 30 Err(_) => { 31 return rsx! { 32 document::Stylesheet { href: NOTEBOOK_COVER_CSS } 33 div { class: "notebook-cover", 34 h1 { class: "notebook-cover-title", "{title}" } 35 div { "Error loading notebook details" } 36 } 37 }; 38 } 39 }; 40 41 let entry_count = book.entry_list.len(); 42 let created_at = book.created_at.clone(); 43 let notebook_uri = notebook.uri.clone().into_static(); 44 45 let handle_settings_saved = move |_| { 46 show_settings.set(false); 47 }; 48 49 let handle_settings_cancel = move |_| { 50 show_settings.set(false); 51 }; 52 53 rsx! { 54 document::Stylesheet { href: NOTEBOOK_COVER_CSS } 55 56 div { class: "notebook-cover", 57 h1 { class: "notebook-cover-title", "{title}" } 58 59 // Authors section 60 if !notebook.authors.is_empty() { 61 { 62 let owner = notebook.uri.authority().clone().into_static(); 63 rsx! { 64 div { class: "notebook-cover-authors", 65 AuthorList { 66 authors: notebook.authors.clone(), 67 owner_ident: Some(owner), 68 avatar_size: 48, 69 } 70 } 71 } 72 } 73 } 74 75 // Metadata 76 div { class: "notebook-cover-meta", 77 // Entry count 78 span { class: "notebook-cover-stat", 79 "{entry_count} " 80 if entry_count == 1 { "entry" } else { "entries" } 81 } 82 83 // Created date 84 if let Some(ref ca) = created_at { 85 { 86 let formatted_date = ca.as_ref().format("%B %d, %Y").to_string(); 87 rsx! { 88 span { class: "notebook-cover-date", 89 "Created {formatted_date}" 90 } 91 } 92 } 93 } 94 } 95 96 // Tags if present 97 if let Some(ref tags) = notebook.tags { 98 if !tags.is_empty() { 99 div { class: "notebook-cover-tags", 100 for tag in tags.iter() { 101 span { class: "notebook-cover-tag", "{tag}" } 102 } 103 } 104 } 105 } 106 107 // Owner actions 108 if is_owner { 109 if let Some(ref owner_ident) = ident { 110 div { class: "notebook-cover-actions", 111 AppLink { 112 to: AppLinkTarget::NewDraft { 113 ident: owner_ident.clone(), 114 notebook: Some(SmolStr::from(title.as_str())) 115 }, 116 class: Some("notebook-cover-action-link".to_string()), 117 Button { 118 variant: ButtonVariant::Outline, 119 "+ Add Entry" 120 } 121 } 122 Button { 123 variant: ButtonVariant::Ghost, 124 onclick: move |_| show_settings.toggle(), 125 if show_settings() { "Close Settings" } else { "Settings" } 126 } 127 } 128 } 129 } 130 131 // Settings panel (inline below actions) 132 if show_settings() { 133 div { class: "notebook-cover-settings", 134 NotebookSettingsPanel { 135 notebook_uri: notebook_uri.clone(), 136 book: book.clone(), 137 on_saved: handle_settings_saved, 138 on_cancel: handle_settings_cancel, 139 } 140 } 141 } 142 } 143 } 144}