All checks were successful
CI / Clippy (push) Successful in 2m17s
CI / Security Audit (push) Successful in 1m38s
CI / Format (pull_request) Successful in 6m28s
CI / Tests (push) Successful in 2m51s
CI / Format (push) Successful in 6m17s
CI / Clippy (pull_request) Successful in 2m15s
CI / Security Audit (pull_request) Successful in 1m42s
CI / Tests (pull_request) Successful in 2m48s
CI / Deploy (push) Has been skipped
CI / Deploy (pull_request) Has been skipped
146 lines
5.8 KiB
Rust
146 lines
5.8 KiB
Rust
use dioxus::prelude::*;
|
|
|
|
use crate::components::ChatBubble;
|
|
use crate::models::{ChatMessage, ChatRole, ChatSession};
|
|
|
|
/// ChatGPT-style chat interface with session list and message area.
|
|
///
|
|
/// Full-height layout: left panel shows session history,
|
|
/// right panel shows messages and input bar.
|
|
#[component]
|
|
pub fn ChatPage() -> Element {
|
|
let sessions = use_signal(mock_sessions);
|
|
let mut active_session_id = use_signal(|| "session-1".to_string());
|
|
let mut input_text = use_signal(String::new);
|
|
|
|
// Clone data out of signals before entering the rsx! block to avoid
|
|
// holding a `Signal::read()` borrow across potential await points.
|
|
let sessions_list = sessions.read().clone();
|
|
let current_id = active_session_id.read().clone();
|
|
let active_session = sessions_list.iter().find(|s| s.id == current_id).cloned();
|
|
|
|
rsx! {
|
|
section { class: "chat-page",
|
|
div { class: "chat-sidebar-panel",
|
|
div { class: "chat-sidebar-header",
|
|
h3 { "Conversations" }
|
|
button { class: "btn-icon", "+" }
|
|
}
|
|
div { class: "chat-session-list",
|
|
for session in &sessions_list {
|
|
{
|
|
let is_active = session.id == current_id;
|
|
let class = if is_active {
|
|
"chat-session-item chat-session-item--active"
|
|
} else {
|
|
"chat-session-item"
|
|
};
|
|
let id = session.id.clone();
|
|
rsx! {
|
|
button { class: "{class}", onclick: move |_| active_session_id.set(id.clone()),
|
|
div { class: "chat-session-title", "{session.title}" }
|
|
div { class: "chat-session-date", "{session.created_at}" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
div { class: "chat-main-panel",
|
|
if let Some(session) = &active_session {
|
|
div { class: "chat-messages",
|
|
for msg in &session.messages {
|
|
ChatBubble { key: "{msg.id}", message: msg.clone() }
|
|
}
|
|
}
|
|
} else {
|
|
div { class: "chat-empty",
|
|
p { "Select a conversation or start a new one." }
|
|
}
|
|
}
|
|
div { class: "chat-input-bar",
|
|
button { class: "btn-icon chat-attach-btn", "+" }
|
|
input {
|
|
class: "chat-input",
|
|
r#type: "text",
|
|
placeholder: "Type a message...",
|
|
value: "{input_text}",
|
|
oninput: move |evt: Event<FormData>| {
|
|
input_text.set(evt.value());
|
|
},
|
|
}
|
|
button { class: "btn-primary chat-send-btn", "Send" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns mock chat sessions with sample messages.
|
|
fn mock_sessions() -> Vec<ChatSession> {
|
|
vec![
|
|
ChatSession {
|
|
id: "session-1".into(),
|
|
title: "RAG Pipeline Setup".into(),
|
|
messages: vec![
|
|
ChatMessage {
|
|
id: "msg-1".into(),
|
|
role: ChatRole::User,
|
|
content: "How do I set up a RAG pipeline with Ollama?".into(),
|
|
attachments: vec![],
|
|
timestamp: "10:30".into(),
|
|
},
|
|
ChatMessage {
|
|
id: "msg-2".into(),
|
|
role: ChatRole::Assistant,
|
|
content: "To set up a RAG pipeline with Ollama, you'll need to: \
|
|
1) Install Ollama and pull your preferred model, \
|
|
2) Set up a vector database (e.g. ChromaDB), \
|
|
3) Create an embedding pipeline for your documents, \
|
|
4) Wire the retrieval step into your prompt chain."
|
|
.into(),
|
|
attachments: vec![],
|
|
timestamp: "10:31".into(),
|
|
},
|
|
],
|
|
created_at: "2026-02-18".into(),
|
|
},
|
|
ChatSession {
|
|
id: "session-2".into(),
|
|
title: "GDPR Compliance Check".into(),
|
|
messages: vec![
|
|
ChatMessage {
|
|
id: "msg-3".into(),
|
|
role: ChatRole::User,
|
|
content: "What data does CERTifAI store about users?".into(),
|
|
attachments: vec![],
|
|
timestamp: "09:15".into(),
|
|
},
|
|
ChatMessage {
|
|
id: "msg-4".into(),
|
|
role: ChatRole::Assistant,
|
|
content: "CERTifAI stores only the minimum data required: \
|
|
email address, session tokens, and usage metrics. \
|
|
All data stays on your infrastructure."
|
|
.into(),
|
|
attachments: vec![],
|
|
timestamp: "09:16".into(),
|
|
},
|
|
],
|
|
created_at: "2026-02-17".into(),
|
|
},
|
|
ChatSession {
|
|
id: "session-3".into(),
|
|
title: "MCP Server Configuration".into(),
|
|
messages: vec![ChatMessage {
|
|
id: "msg-5".into(),
|
|
role: ChatRole::User,
|
|
content: "How do I add a new MCP server?".into(),
|
|
attachments: vec![],
|
|
timestamp: "14:00".into(),
|
|
}],
|
|
created_at: "2026-02-16".into(),
|
|
},
|
|
]
|
|
}
|