use crate::addon::Addon;
use crate::generator::context::markers;
use anyhow::Result;
use std::path::Path;
pub struct DashboardAddon;
impl Addon for DashboardAddon {
fn name(&self) -> &str {
"dashboard"
}
fn check_prerequisites(&self, project_root: &Path) -> Result<()> {
super::check_romance_project(project_root)
}
fn is_already_installed(&self, project_root: &Path) -> bool {
project_root
.join("frontend/src/features/dev/DevDashboard.tsx")
.exists()
}
fn install(&self, project_root: &Path) -> Result<()> {
install_dashboard(project_root)
}
fn uninstall(&self, project_root: &Path) -> Result<()> {
use colored::Colorize;
println!("{}", "Uninstalling dev dashboard...".bold());
if super::remove_file_if_exists(
&project_root.join("backend/src/handlers/dev_dashboard.rs"),
)? {
println!(
" {} backend/src/handlers/dev_dashboard.rs",
"delete".red()
);
}
if super::remove_file_if_exists(
&project_root.join("backend/src/routes/dev_dashboard.rs"),
)? {
println!(
" {} backend/src/routes/dev_dashboard.rs",
"delete".red()
);
}
if super::remove_file_if_exists(
&project_root.join("frontend/src/features/dev/DevDashboard.tsx"),
)? {
println!(
" {} frontend/src/features/dev/DevDashboard.tsx",
"delete".red()
);
}
super::remove_line_from_file(
&project_root.join("backend/src/handlers/mod.rs"),
"pub mod dev_dashboard;",
)?;
super::remove_line_from_file(
&project_root.join("backend/src/routes/mod.rs"),
"pub mod dev_dashboard;",
)?;
super::remove_line_from_file(
&project_root.join("backend/src/routes/mod.rs"),
".merge(dev_dashboard::router())",
)?;
super::remove_line_from_file(
&project_root.join("frontend/src/App.tsx"),
"DevDashboard",
)?;
crate::ai_context::regenerate(project_root).ok();
println!();
println!(
"{}",
"Dev dashboard uninstalled successfully.".green().bold()
);
Ok(())
}
}
fn install_dashboard(project_root: &Path) -> Result<()> {
use crate::relation;
use crate::template::TemplateEngine;
use crate::utils;
use colored::Colorize;
use heck::{ToLowerCamelCase, ToPascalCase};
use tera::Context;
println!("{}", "Installing dev dashboard...".bold());
let engine = TemplateEngine::new()?;
let entity_names = relation::discover_entities(project_root)?;
let entities_dir = project_root.join("backend/src/entities");
let entities: Vec<serde_json::Value> = entity_names
.iter()
.filter(|n| {
if *n == "user" || *n == "audit_entry" {
return false;
}
let model_path = entities_dir.join(format!("{}.rs", n));
if model_path.exists() {
if let Ok(content) = std::fs::read_to_string(&model_path) {
return content.contains("ROMANCE:CUSTOM");
}
}
false
})
.map(|name| {
serde_json::json!({
"name": name.to_pascal_case(),
"name_snake": name,
"name_camel": name.to_lower_camel_case(),
})
})
.collect();
let has_auth = project_root.join("backend/src/auth.rs").exists();
let has_audit = project_root.join("backend/src/audit.rs").exists();
let mut ctx = Context::new();
ctx.insert("entities", &entities);
ctx.insert("has_auth", &has_auth);
ctx.insert("has_audit", &has_audit);
let content = engine.render("addon/dashboard/dev_handlers.rs.tera", &ctx)?;
utils::write_file(
&project_root.join("backend/src/handlers/dev_dashboard.rs"),
&content,
)?;
println!(
" {} backend/src/handlers/dev_dashboard.rs",
"create".green()
);
let content = engine.render("addon/dashboard/dev_routes.rs.tera", &ctx)?;
utils::write_file(
&project_root.join("backend/src/routes/dev_dashboard.rs"),
&content,
)?;
println!(
" {} backend/src/routes/dev_dashboard.rs",
"create".green()
);
let content = engine.render("addon/dashboard/DevDashboard.tsx.tera", &ctx)?;
utils::write_file(
&project_root.join("frontend/src/features/dev/DevDashboard.tsx"),
&content,
)?;
println!(
" {} frontend/src/features/dev/DevDashboard.tsx",
"create".green()
);
let mods_marker = "// === ROMANCE:MODS ===";
utils::insert_at_marker(
&project_root.join("backend/src/handlers/mod.rs"),
mods_marker,
"pub mod dev_dashboard;",
)?;
utils::insert_at_marker(
&project_root.join("backend/src/routes/mod.rs"),
mods_marker,
"pub mod dev_dashboard;",
)?;
utils::insert_at_marker(
&project_root.join("backend/src/routes/mod.rs"),
"// === ROMANCE:ROUTES ===",
" .merge(dev_dashboard::router())",
)?;
utils::insert_at_marker(
&project_root.join("frontend/src/App.tsx"),
"// === ROMANCE:IMPORTS ===",
"import DevDashboard from '@/features/dev/DevDashboard';",
)?;
utils::insert_at_marker(
&project_root.join("frontend/src/App.tsx"),
markers::APP_ROUTES,
" <Route path=\"/dev\" element={<DevDashboard />} />",
)?;
println!();
println!(
"{}",
"Dev dashboard installed successfully!".green().bold()
);
println!(" Visit /dev to see the developer dashboard.");
Ok(())
}