use anyhow::Result;
use owo_colors::OwoColorize;
use crate::api::client::ApiClient;
use crate::render::table::{Cell, Table};
use crate::render::theme;
fn role_label(role: &str) -> &str {
match role {
"admin" => "administrador",
_ => "miembro",
}
}
fn status_label(status: &str) -> &str {
match status {
"pending" => "pendiente",
"accepted" => "aceptada",
_ => "caducada",
}
}
pub async fn list(client: &ApiClient) -> Result<()> {
let workspaces = client.workspaces().await?;
let mut table = Table::new(&["id", "espacio", "slug", "tu papel"]);
for workspace in &workspaces {
table.push(vec![
Cell::styled(workspace.id.clone(), theme::muted()),
Cell::styled(workspace.name.clone(), theme::strong()),
Cell::plain(workspace.slug.clone()),
Cell::plain(
workspace
.role
.as_deref()
.map(role_label)
.unwrap_or("—")
.to_string(),
),
]);
}
if table.is_empty() {
println!("Todavía no hay ningún espacio.");
return Ok(());
}
table.print();
Ok(())
}
pub async fn members(client: &ApiClient, reference: Option<&str>) -> Result<()> {
let workspace = client.find_workspace(reference).await?;
println!();
println!(" {}", workspace.name.style(theme::strong()));
println!();
let members = client.members(&workspace.id).await?;
let mut table = Table::new(&["usuario", "github", "correo", "papel"]);
for member in &members {
table.push(vec![
Cell::styled(
member
.display_name
.clone()
.unwrap_or_else(|| member.user_id.clone()),
theme::strong(),
),
Cell::plain(
member
.github_login
.as_ref()
.map(|login| format!("@{login}"))
.unwrap_or_else(|| "—".to_string()),
),
Cell::plain(member.email.clone().unwrap_or_else(|| "—".to_string())),
Cell::plain(role_label(&member.role).to_string()),
]);
}
table.print();
let pending: Vec<_> = client
.invitations(&workspace.id)
.await?
.into_iter()
.filter(|invitation| invitation.status == "pending")
.collect();
if pending.is_empty() {
return Ok(());
}
println!();
println!(" {}", "Invitaciones pendientes".style(theme::muted()));
println!();
let mut invitations = Table::new(&["id", "correo", "papel", "estado"]);
for invitation in &pending {
invitations.push(vec![
Cell::styled(invitation.id.clone(), theme::muted()),
Cell::styled(invitation.email.clone(), theme::strong()),
Cell::plain(role_label(&invitation.role).to_string()),
Cell::plain(status_label(&invitation.status).to_string()),
]);
}
invitations.print();
Ok(())
}
pub async fn invite(
client: &ApiClient,
reference: Option<&str>,
email: &str,
admin: bool,
) -> Result<()> {
let workspace = client.find_workspace(reference).await?;
let role = if admin { "admin" } else { "member" };
let sent = client.invite(&workspace.id, email, role).await?;
println!();
println!(
" {} Invitación enviada a {} como {}",
theme::CHECK.style(theme::success()),
email.style(theme::strong()),
role_label(role),
);
println!(" {}", sent.accept_url.style(theme::muted()));
println!();
Ok(())
}
pub async fn revoke(
client: &ApiClient,
reference: Option<&str>,
invitation_id: &str,
) -> Result<()> {
let workspace = client.find_workspace(reference).await?;
client
.revoke_invitation(&workspace.id, invitation_id)
.await?;
println!(
" {} Invitación retirada",
theme::CHECK.style(theme::success())
);
Ok(())
}
pub async fn remove(client: &ApiClient, reference: Option<&str>, user_id: &str) -> Result<()> {
let workspace = client.find_workspace(reference).await?;
client.remove_member(&workspace.id, user_id).await?;
println!(
" {} {user_id} ya no está en {}",
theme::CHECK.style(theme::success()),
workspace.name.style(theme::strong()),
);
Ok(())
}