use crate::{AdminInstance, StatCard, QuickAction};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardView {
pub title: String,
pub stats: Vec<StatCard>,
pub quick_actions: Vec<QuickAction>,
pub recent_activity: Vec<ActivityItem>,
pub model_summaries: Vec<ModelSummary>,
}
impl DashboardView {
pub fn new(admin: &AdminInstance) -> Self {
let model_summaries = admin
.models()
.iter()
.map(|m| ModelSummary {
name: m.name.clone(),
verbose_name: m.verbose_name.clone(),
icon: m.icon.clone(),
count: 0, recent_count: 0,
url: format!("{}/{}", admin.config.base_path, m.name),
})
.collect();
Self {
title: admin.config.title.clone(),
stats: vec![
StatCard {
title: "Total Records".to_string(),
value: "0".to_string(),
change: None,
icon: Some("database".to_string()),
color: None,
link: None,
},
],
quick_actions: admin
.models()
.iter()
.filter(|m| m.can_add)
.take(4)
.map(|m| QuickAction {
label: format!("Add {}", m.verbose_name_singular),
url: format!("{}/{}/add", admin.config.base_path, m.name),
icon: Some("plus".to_string()),
css_class: None,
})
.collect(),
recent_activity: Vec::new(),
model_summaries,
}
}
pub fn with_stats(mut self, stats: Vec<StatCard>) -> Self {
self.stats = stats;
self
}
pub fn with_activity(mut self, activity: Vec<ActivityItem>) -> Self {
self.recent_activity = activity;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActivityItem {
pub action: ActivityAction,
pub model: String,
pub record: String,
pub record_id: String,
pub user: Option<String>,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub url: Option<String>,
}
impl ActivityItem {
pub fn new(action: ActivityAction, model: impl Into<String>, record: impl Into<String>) -> Self {
Self {
action,
model: model.into(),
record: record.into(),
record_id: String::new(),
user: None,
timestamp: chrono::Utc::now(),
url: None,
}
}
pub fn record_id(mut self, id: impl Into<String>) -> Self {
self.record_id = id.into();
self
}
pub fn user(mut self, user: impl Into<String>) -> Self {
self.user = Some(user.into());
self
}
pub fn url(mut self, url: impl Into<String>) -> Self {
self.url = Some(url.into());
self
}
pub fn description(&self) -> String {
match self.action {
ActivityAction::Create => format!("Created {} \"{}\"", self.model, self.record),
ActivityAction::Update => format!("Updated {} \"{}\"", self.model, self.record),
ActivityAction::Delete => format!("Deleted {} \"{}\"", self.model, self.record),
ActivityAction::View => format!("Viewed {} \"{}\"", self.model, self.record),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ActivityAction {
Create,
Update,
Delete,
View,
}
impl ActivityAction {
pub fn icon(&self) -> &'static str {
match self {
Self::Create => "plus-circle",
Self::Update => "edit",
Self::Delete => "trash",
Self::View => "eye",
}
}
pub fn color(&self) -> &'static str {
match self {
Self::Create => "success",
Self::Update => "warning",
Self::Delete => "error",
Self::View => "info",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelSummary {
pub name: String,
pub verbose_name: String,
pub icon: Option<String>,
pub count: usize,
pub recent_count: usize,
pub url: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_activity_description() {
let activity = ActivityItem::new(ActivityAction::Create, "User", "Alice");
assert_eq!(activity.description(), "Created User \"Alice\"");
}
#[test]
fn test_activity_action_icon() {
assert_eq!(ActivityAction::Create.icon(), "plus-circle");
assert_eq!(ActivityAction::Delete.icon(), "trash");
}
}