mod resources;
use ratatui::{
Frame,
layout::Rect,
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, BorderType, Borders, Paragraph}
};
use resources::{
render_ai_agent_details, render_app_details, render_balancer_details, render_database_details,
render_dedicated_details, render_domain_details, render_firewall_details,
render_floating_ip_details, render_image_details, render_k8s_details,
render_knowledge_details, render_mail_details, render_network_drive_details,
render_project_details, render_registry_details, render_s3_details, render_server_details,
render_string_details, render_vpc_details
};
use rust_i18n::t;
use crate::tui::{
app::{App, ResourceTab},
themes::Palette
};
const KEY_WIDTH: usize = 10;
const RULE_WIDTH: usize = 32;
pub fn render(frame: &mut Frame, area: Rect, app: &App, border_color: Color) {
let palette = app.theme.palette();
let text = match app.active_tab {
ResourceTab::Servers => render_server_details(app, palette),
ResourceTab::Databases => render_database_details(app, palette),
ResourceTab::S3 => render_s3_details(app, palette),
ResourceTab::Kubernetes => render_k8s_details(app, palette),
ResourceTab::Projects => render_project_details(app, palette),
ResourceTab::Balancers => render_balancer_details(app, palette),
ResourceTab::Registry => render_registry_details(app, palette),
ResourceTab::Domains => render_domain_details(app, palette),
ResourceTab::Firewall => render_firewall_details(app, palette),
ResourceTab::FloatingIps => render_floating_ip_details(app, palette),
ResourceTab::Images => render_image_details(app, palette),
ResourceTab::NetworkDrives => render_network_drive_details(app, palette),
ResourceTab::Vpc => render_vpc_details(app, palette),
ResourceTab::DedicatedServers => render_dedicated_details(app, palette),
ResourceTab::Mail => render_mail_details(app, palette),
ResourceTab::Apps => render_app_details(app, palette),
ResourceTab::AiAgents => render_ai_agent_details(app, palette),
ResourceTab::KnowledgeBases => render_knowledge_details(app, palette),
ResourceTab::SshKeys => {
render_string_details(&app.ssh_keys, app, &t!("details.ssh_key"), palette)
}
ResourceTab::Finances => {
render_string_details(&app.finances, app, &t!("details.finance"), palette)
}
};
let paragraph = Paragraph::new(text).block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(border_color))
.title(Line::from(Span::styled(
format!(" {} ", t!("details.title")),
Style::default()
.fg(palette.title)
.add_modifier(Modifier::BOLD)
)))
);
frame.render_widget(paragraph, area);
}
pub(super) fn heading(name: &str, palette: Palette) -> Line<'static> {
Line::from(Span::styled(
name.to_string(),
Style::default()
.fg(palette.title)
.add_modifier(Modifier::BOLD)
))
}
pub(super) fn rule(palette: Palette) -> Line<'static> {
Line::from(Span::styled(
"\u{2500}".repeat(RULE_WIDTH),
Style::default().fg(palette.dim)
))
}
pub(super) fn section(label: &str, palette: Palette) -> Line<'static> {
Line::from(Span::styled(
label.to_string(),
Style::default()
.fg(palette.header)
.add_modifier(Modifier::BOLD)
))
}
pub(super) fn kv(key: &str, value: String, value_style: Style, palette: Palette) -> Line<'static> {
Line::from(vec![
Span::styled(
format!("{key:<KEY_WIDTH$}"),
Style::default().fg(palette.dim)
),
Span::styled(value, value_style),
])
}
pub(super) fn chip(key: &str, label: &str, color: Color, palette: Palette) -> Line<'static> {
Line::from(vec![
Span::styled(
format!("{key:<KEY_WIDTH$}"),
Style::default().fg(palette.dim)
),
Span::styled(
format!("\u{25CF} {label}"),
Style::default().fg(color).add_modifier(Modifier::BOLD)
),
])
}
pub(super) fn status_chip(key: &str, status: &str, palette: Palette) -> Line<'static> {
let (color, label) = crate::tui::widgets::resource_list::status_view(status, &palette);
chip(key, &label, color, palette)
}
pub(super) fn empty(message: &str, palette: Palette) -> Vec<Line<'static>> {
vec![
Line::from(""),
Line::from(Span::styled(
format!(" {message}"),
Style::default()
.fg(palette.dim)
.add_modifier(Modifier::ITALIC)
)),
]
}
pub(super) fn accent(palette: Palette) -> Style {
Style::default().fg(palette.accent)
}
pub(super) fn name_style(palette: Palette) -> Style {
Style::default().fg(palette.fg).add_modifier(Modifier::BOLD)
}
pub(super) fn warn(palette: Palette) -> Style {
Style::default().fg(palette.warning)
}
pub struct DetailsWidget {
enabled: bool
}
impl DetailsWidget {
#[must_use]
pub const fn new(enabled: bool) -> Self {
Self {
enabled
}
}
}
impl crate::tui::widgets::Widget for DetailsWidget {
fn id(&self) -> &'static str {
"details"
}
fn name(&self) -> &'static str {
"Details"
}
fn enabled(&self) -> bool {
self.enabled
}
fn toggle(&mut self) {
self.enabled = !self.enabled;
}
fn render(&self, frame: &mut Frame, area: Rect, app: &App) {
let border_color = if app.focus == crate::tui::app::Focus::Details {
app.theme.palette().accent
} else {
app.theme.palette().border
};
render(frame, area, app, border_color);
}
}