Skip to content

Commit 4f9e3ab

Browse files
committed
Merge branch 'feature/add-ratatui' into development
2 parents 2318071 + f09144f commit 4f9e3ab

15 files changed

Lines changed: 1846 additions & 169 deletions

Cargo.lock

Lines changed: 257 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ chrono = "0.4"
99
clap = "4.1.6"
1010
clap_mangen = "0.2.10"
1111
console = "0.16"
12+
crossterm = "0.28"
1213
dialoguer = "0.11.0"
1314
dirs = "6.0"
1415

@@ -20,6 +21,7 @@ log = "0.4.14"
2021
maybe-async = "0.2"
2122
open = "5.3"
2223
openssl = { version = "0.10", features = ["vendored"] }
24+
ratatui = { version = "0.29", default-features = false, features = ["crossterm"] }
2325
regex = "1.11"
2426
reqwest = { version = "0.12.18", default-features = false }
2527
serde = "1"

crates/cli/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ chrono = { workspace = true }
2727
clap = { workspace = true, features = ["derive", "env"] }
2828
clap_mangen = { workspace = true }
2929
console = { workspace = true }
30+
crossterm = { workspace = true }
3031
dialoguer = { workspace = true }
3132
dirs = { workspace = true }
3233

@@ -37,6 +38,7 @@ home = { workspace = true }
3738
indicatif = { workspace = true }
3839
log = { workspace = true }
3940
open = { workspace = true }
41+
ratatui = { workspace = true }
4042
regex = { workspace = true }
4143
reqwest = { workspace = true, features = ["json", "rustls-tls-native-roots"] }
4244
serde = { workspace = true, features = ["derive"] }

crates/cli/src/account/me/mod.rs

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,13 @@ use crate::client;
33
use crate::token::get_smb_token::get_smb_token;
44
use crate::{
55
cli::CommandResult,
6-
ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
6+
ui::{fail_message, fail_symbol, me_view::show_user_tui, succeed_message, succeed_symbol},
77
};
8-
use anyhow::Result;
8+
use anyhow::{anyhow, Result};
99
use smbcloud_auth::me::me;
10-
use smbcloud_model::account::User;
10+
1111
use smbcloud_network::environment::Environment;
1212
use spinners::Spinner;
13-
use tabled::{Table, Tabled};
14-
15-
#[derive(Tabled)]
16-
struct UserRow {
17-
#[tabled(rename = "ID")]
18-
id: i32,
19-
#[tabled(rename = "Email")]
20-
email: String,
21-
#[tabled(rename = "Created At")]
22-
created_at: String,
23-
#[tabled(rename = "Updated At")]
24-
updated_at: String,
25-
}
26-
27-
fn show_user(user: &User) {
28-
let row = UserRow {
29-
id: user.id,
30-
email: user.email.clone(),
31-
created_at: user.created_at.format("%Y-%m-%d %H:%M:%S").to_string(),
32-
updated_at: user.updated_at.format("%Y-%m-%d %H:%M:%S").to_string(),
33-
};
34-
let table = Table::new(vec![row]);
35-
println!("{table}");
36-
}
3713

3814
pub async fn process_me(env: Environment) -> Result<CommandResult> {
3915
if !is_logged_in(env) {
@@ -54,14 +30,14 @@ pub async fn process_me(env: Environment) -> Result<CommandResult> {
5430
match me(env, client(), &token).await {
5531
Ok(user) => {
5632
spinner.stop_and_persist(&succeed_symbol(), succeed_message("Loaded."));
57-
show_user(&user);
33+
show_user_tui(&user).map_err(|e| anyhow!(e))?;
5834
Ok(CommandResult {
5935
spinner: Spinner::new(
6036
spinners::Spinners::SimpleDotsScrolling,
6137
succeed_message("Loading"),
6238
),
6339
symbol: succeed_symbol(),
64-
msg: succeed_message("User info loaded."),
40+
msg: succeed_message("Done."),
6541
})
6642
}
6743
Err(e) => {

crates/cli/src/project/crud_delete.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ use crate::token::get_smb_token::get_smb_token;
33
use crate::{
44
account::lib::is_logged_in,
55
cli::CommandResult,
6-
ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
6+
ui::{
7+
confirm_dialog::confirm_delete_tui, fail_message, fail_symbol, succeed_message,
8+
succeed_symbol,
9+
},
710
};
811
use anyhow::{anyhow, Result};
9-
use dialoguer::{theme::ColorfulTheme, Input};
1012
use smbcloud_network::environment::Environment;
1113
use smbcloud_networking_project::crud_project_delete::delete_project;
1214
use spinners::Spinner;
@@ -16,17 +18,14 @@ pub async fn process_project_delete(env: Environment, id: String) -> Result<Comm
1618
return Err(anyhow!(fail_message("Please log in with `smb init`.")));
1719
}
1820

19-
let confirmation = Input::<String>::with_theme(&ColorfulTheme::default())
20-
.with_prompt("Are you sure you want to delete this project? (y/n)")
21-
.interact()
22-
.unwrap();
21+
let confirmed = confirm_delete_tui(&format!("Delete project #{id}")).map_err(|e| anyhow!(e))?;
2322

2423
let spinner = Spinner::new(
2524
spinners::Spinners::SimpleDotsScrolling,
2625
succeed_message("Deleting project"),
2726
);
2827

29-
if confirmation != "y" {
28+
if !confirmed {
3029
return Ok(CommandResult {
3130
spinner,
3231
symbol: succeed_symbol(),

crates/cli/src/project/crud_read.rs

Lines changed: 12 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,17 @@ use crate::client;
44
use crate::token::get_smb_token::get_smb_token;
55
use crate::{
66
cli::CommandResult,
7-
ui::{fail_message, fail_symbol, succeed_message, succeed_symbol},
7+
ui::{
8+
fail_message, fail_symbol, project_detail_view::show_project_detail_tui,
9+
project_table::show_projects_tui, succeed_message, succeed_symbol,
10+
},
811
};
912
use anyhow::{anyhow, Result};
1013
use log::debug;
1114
use smbcloud_model::project::{Config, Project};
1215
use smbcloud_network::environment::Environment;
1316
use smbcloud_networking_project::crud_project_read::{get_project, get_projects};
1417
use spinners::Spinner;
15-
use tabled::{Table, Tabled};
16-
17-
#[derive(Tabled)]
18-
struct ProjectRow {
19-
#[tabled(rename = "ID")]
20-
id: i32,
21-
#[tabled(rename = "Name")]
22-
name: String,
23-
#[tabled(rename = "Runner")]
24-
runner: String,
25-
#[tabled(rename = "Repository")]
26-
repository: String,
27-
#[tabled(rename = "Description")]
28-
description: String,
29-
}
30-
31-
#[derive(Tabled)]
32-
struct ProjectDetailRow {
33-
#[tabled(rename = "ID")]
34-
id: i32,
35-
#[tabled(rename = "Name")]
36-
name: String,
37-
#[tabled(rename = "Repository")]
38-
repository: String,
39-
#[tabled(rename = "Description")]
40-
description: String,
41-
#[tabled(rename = "Created at")]
42-
created_at: String,
43-
#[tabled(rename = "Updated at")]
44-
updated_at: String,
45-
}
4618

4719
pub async fn process_project_list(env: Environment) -> Result<CommandResult> {
4820
let mut spinner = Spinner::new(
@@ -56,9 +28,9 @@ pub async fn process_project_list(env: Environment) -> Result<CommandResult> {
5628
let msg = if projects.is_empty() {
5729
succeed_message("No projects found.")
5830
} else {
59-
succeed_message("Showing all projects.")
31+
succeed_message("Done.")
6032
};
61-
show_projects(projects);
33+
show_projects(projects)?;
6234
Ok(CommandResult {
6335
spinner: Spinner::new(
6436
spinners::Spinners::SimpleDotsScrolling,
@@ -89,7 +61,7 @@ pub async fn process_project_show(env: Environment, id: String) -> Result<Comman
8961
Ok(project) => {
9062
spinner.stop_and_persist(&succeed_symbol(), succeed_message("Loaded."));
9163
let message = succeed_message(&format!("Showing project {}.", &project.name));
92-
show_project_detail(&project);
64+
show_project_detail(&project)?;
9365
Ok(CommandResult {
9466
spinner: Spinner::new(
9567
spinners::Spinners::SimpleDotsScrolling,
@@ -106,35 +78,15 @@ pub async fn process_project_show(env: Environment, id: String) -> Result<Comman
10678
}
10779
}
10880

109-
pub(crate) fn show_projects(projects: Vec<Project>) {
81+
pub(crate) fn show_projects(projects: Vec<Project>) -> Result<()> {
11082
if projects.is_empty() {
111-
return;
83+
return Ok(());
11284
}
113-
let rows: Vec<ProjectRow> = projects
114-
.into_iter()
115-
.map(|p| ProjectRow {
116-
id: p.id,
117-
name: p.name,
118-
runner: p.runner.to_string(),
119-
repository: p.repository.unwrap_or("-".to_string()),
120-
description: p.description.unwrap_or("-".to_owned()),
121-
})
122-
.collect();
123-
let table = Table::new(rows);
124-
println!("{table}");
85+
show_projects_tui(projects).map_err(|e| anyhow!(e))
12586
}
12687

127-
pub(crate) fn show_project_detail(project: &Project) {
128-
let row = ProjectDetailRow {
129-
id: project.id,
130-
name: project.name.clone(),
131-
repository: project.repository.clone().unwrap_or("-".to_owned()),
132-
description: project.description.clone().unwrap_or("-".to_owned()),
133-
created_at: project.created_at.date_naive().to_string(),
134-
updated_at: project.updated_at.date_naive().to_string(),
135-
};
136-
let table = Table::new(vec![row]);
137-
println!("{table}");
88+
pub(crate) fn show_project_detail(project: &Project) -> Result<()> {
89+
show_project_detail_tui(project).map_err(|e| anyhow!(e))
13890
}
13991

14092
pub(crate) async fn process_project_use(env: Environment, id: String) -> Result<CommandResult> {

crates/cli/src/project/deployment.rs

Lines changed: 7 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ use crate::token::get_smb_token::get_smb_token;
33
use crate::{
44
cli::CommandResult,
55
deploy::config::{check_project, get_config},
6-
ui::{succeed_message, succeed_symbol},
6+
ui::{
7+
deployment_detail_view::show_deployment_detail_tui, deployment_table::show_deployments_tui,
8+
succeed_message, succeed_symbol,
9+
},
710
};
8-
use anyhow::Result;
9-
use smbcloud_model::project::Deployment;
11+
use anyhow::{anyhow, Result};
1012
use smbcloud_network::environment::Environment;
1113
use smbcloud_networking_project::crud_project_deployment_read::{get_deployment, get_deployments};
1214
use spinners::Spinner;
13-
use tabled::{Table, Tabled};
1415

1516
pub(crate) async fn process_deployment(
1617
env: Environment,
@@ -37,13 +38,13 @@ pub(crate) async fn process_deployment(
3738
)
3839
.await?;
3940
spinner.stop_and_persist(&succeed_symbol(), succeed_message("Loaded"));
40-
show_deployment_detail(&deployment);
41+
show_deployment_detail_tui(&deployment).map_err(|e| anyhow!(e))?;
4142
} else {
4243
// List all deployments for the project
4344
let access_token = get_smb_token(env)?;
4445
let deployments = get_deployments(env, client(), access_token, config.project.id).await?;
4546
spinner.stop_and_persist(&succeed_symbol(), succeed_message("Load all deployments"));
46-
show_project_deployments(&deployments);
47+
show_deployments_tui(deployments).map_err(|e| anyhow!(e))?;
4748
};
4849

4950
Ok(CommandResult {
@@ -52,61 +53,3 @@ pub(crate) async fn process_deployment(
5253
msg: succeed_message("Loaded"),
5354
})
5455
}
55-
56-
// Helper struct for table display
57-
#[derive(Tabled)]
58-
struct DeploymentRow {
59-
id: i32,
60-
commit_hash: String,
61-
status: String, // Keep as String, but populate with Display trait output + Debug output
62-
}
63-
64-
pub fn show_project_deployments(deployments: &[Deployment]) {
65-
let rows: Vec<DeploymentRow> = deployments
66-
.iter()
67-
.map(|d| DeploymentRow {
68-
id: d.id,
69-
commit_hash: d.commit_hash.clone(),
70-
status: format!("{} {:?}", d.status, d.status), // Use Display for emoji and Debug for text
71-
})
72-
.collect();
73-
74-
let table = Table::new(rows);
75-
println!("{table}");
76-
}
77-
78-
pub fn show_deployment_detail(deployment: &Deployment) {
79-
#[derive(Tabled)]
80-
struct Detail {
81-
#[tabled(rename = "ID")]
82-
id: i32,
83-
#[tabled(rename = "Project ID")]
84-
project_id: i32,
85-
#[tabled(rename = "Commit Hash")]
86-
commit_hash: String,
87-
#[tabled(rename = "Status")]
88-
status: String,
89-
#[tabled(rename = "Created At")]
90-
created_at: String,
91-
#[tabled(rename = "Updated At")]
92-
updated_at: String,
93-
}
94-
95-
let row = Detail {
96-
id: deployment.id,
97-
project_id: deployment.project_id,
98-
commit_hash: deployment.commit_hash.clone(),
99-
status: format!("{} {:?}", deployment.status, deployment.status), // Use Display for emoji and Debug for text
100-
created_at: deployment
101-
.created_at
102-
.format("%Y-%m-%d %H:%M:%S")
103-
.to_string(),
104-
updated_at: deployment
105-
.updated_at
106-
.format("%Y-%m-%d %H:%M:%S")
107-
.to_string(),
108-
};
109-
110-
let table = Table::new(vec![row]);
111-
println!("{table}");
112-
}

0 commit comments

Comments
 (0)