Files
AgentCompany/AgentCompany-Rust/crates/app_ui/src/app_root/documents.rs
T

242 lines
9.5 KiB
Rust

use super::*;
impl AppRoot {
pub(super) fn render_documents(&self, cx: &mut Context<Self>) -> impl IntoElement + '_ {
let selected = self
.state
.selected_document_id
.as_ref()
.and_then(|id| self.snapshot.documents.iter().find(|item| &item.id == id));
let work_products = self.snapshot.artifacts.iter().take(12).collect::<Vec<_>>();
let total_count = self.snapshot.documents.len() + work_products.len();
div()
.flex()
.flex_col()
.gap_6()
.child(
div()
.flex()
.justify_between()
.items_center()
.child(
div()
.child(
div()
.text_2xl()
.text_color(theme::text_primary())
.child("Document Center"),
)
.child(
div()
.mt(px(6.0))
.text_sm()
.text_color(theme::text_muted())
.child(format!("{} items", total_count)),
),
)
.child(primary_button("Save draft", cx, |this, cx| {
this.create_document(cx)
})),
)
.child(document_section_label("DOCUMENTS PROSE"))
.child(self.render_document_prose_list(cx, selected))
.when(!work_products.is_empty(), |container| {
container
.child(document_section_label("WORK PRODUCTS"))
.child(render_work_products(work_products.as_slice()))
})
.child(self.render_document_editor(selected))
}
fn render_document_prose_list(
&self,
cx: &mut Context<Self>,
selected: Option<&domain::Document>,
) -> impl IntoElement + '_ {
div()
.rounded_lg()
.border_1()
.border_color(theme::panel_border())
.bg(theme::panel())
.when(self.snapshot.documents.is_empty(), |container| {
container.child(
div()
.min_h(px(180.0))
.flex()
.items_center()
.justify_center()
.text_color(theme::text_muted())
.child("No documents yet. Create the first company document to start the knowledge base."),
)
})
.when(!self.snapshot.documents.is_empty(), |container| {
container.children(self.snapshot.documents.iter().map(|document| {
let active =
selected.map(|item| item.id.as_str()) == Some(document.id.as_str());
let document_id = document.id.clone();
div()
.id(SharedString::from(format!("document-{}", document.id)))
.cursor_pointer()
.hover(|style| style.bg(theme::panel_hover()))
.bg(if active {
theme::selection_fill()
} else {
theme::panel()
})
.on_click(cx.listener(move |this, _, _, cx| {
this.state.selected_document_id = Some(document_id.clone());
cx.notify();
}))
.px_5()
.py_4()
.border_b_1()
.border_color(theme::panel_border())
.child(
div()
.flex()
.justify_between()
.items_center()
.child(
div()
.flex_1()
.child(
div()
.text_base()
.text_color(theme::text_primary())
.child(document.title.clone()),
)
.child(
div()
.mt(px(4.0))
.text_sm()
.text_color(theme::text_muted())
.child(format!(
"{} · v{} · {}",
document.document_type.replace('_', " "),
document.version,
short_date(document.updated_at.as_str())
)),
),
)
.child(document_status(document.status.as_str())),
)
}))
})
}
fn render_document_editor(&self, selected: Option<&domain::Document>) -> impl IntoElement + '_ {
div()
.rounded_lg()
.border_1()
.border_color(theme::panel_border())
.bg(theme::panel())
.p_5()
.child(
div().text_lg().text_color(theme::text_primary()).child(
selected
.map(|item| item.title.clone())
.unwrap_or_else(|| "New document".to_string()),
),
)
.child(self.document_title_input.clone())
.child(self.document_body_input.clone())
.when_some(selected, |container, document| {
container.child(
div()
.mt(px(12.0))
.rounded_lg()
.border_1()
.border_color(theme::panel_inset_border())
.bg(theme::panel_inset())
.p_4()
.text_sm()
.line_height(px(22.0))
.text_color(theme::text_secondary())
.child(document.content.clone()),
)
})
}
}
fn document_section_label(label: &'static str) -> impl IntoElement {
div().text_xs().text_color(theme::text_muted()).child(label)
}
fn render_work_products<'a>(products: &'a [&'a domain::ArtifactRecord]) -> impl IntoElement + 'a {
div()
.rounded_lg()
.border_1()
.border_color(theme::panel_border())
.bg(theme::panel())
.children(products.iter().map(|artifact| {
div()
.px_5()
.py_4()
.border_b_1()
.border_color(theme::panel_border())
.child(
div()
.flex()
.justify_between()
.items_center()
.child(
div()
.flex_1()
.child(
div()
.text_base()
.text_color(theme::text_primary())
.child(artifact.title.clone()),
)
.child(
div()
.mt(px(4.0))
.text_sm()
.text_color(theme::text_muted())
.child(format!(
"{} · {} · {}",
artifact.provider,
artifact.external_id,
short_date(artifact.created_at.as_str())
)),
),
)
.child(
div()
.px_3()
.py_2()
.rounded_full()
.bg(theme::accent_blue_soft())
.text_xs()
.text_color(theme::accent_blue())
.child("ARTIFACT"),
),
)
}))
}
fn document_status(status: &str) -> impl IntoElement {
div()
.flex()
.items_center()
.gap_2()
.child(
div()
.w(px(8.0))
.h(px(8.0))
.rounded_full()
.bg(theme::badge_dark()),
)
.child(
div()
.text_xs()
.text_color(theme::text_secondary())
.child(status.to_uppercase()),
)
}
fn short_date(value: &str) -> &str {
value.get(..10).unwrap_or(value)
}