clickup_cli/commands/
workspace.rs1use clap::Subcommand;
2use crate::client::ClickUpClient;
3use crate::commands::auth::resolve_token;
4use crate::config::Config;
5use crate::error::CliError;
6use crate::output::OutputConfig;
7use crate::Cli;
8
9#[derive(Subcommand)]
10pub enum WorkspaceCommands {
11 List,
13 Seats,
15 Plan,
17}
18
19pub fn resolve_workspace(cli: &Cli) -> Result<String, CliError> {
20 if let Some(ws) = &cli.workspace {
22 return Ok(ws.clone());
23 }
24 if let Ok(ws) = std::env::var("CLICKUP_WORKSPACE") {
26 if !ws.is_empty() {
27 return Ok(ws);
28 }
29 }
30 let config = Config::load()?;
32 config
33 .defaults
34 .workspace_id
35 .ok_or_else(|| CliError::ConfigError("No default workspace. Use --workspace, CLICKUP_WORKSPACE, or run 'clickup setup'".into()))
36}
37
38pub async fn execute(command: WorkspaceCommands, cli: &Cli) -> Result<(), CliError> {
39 let token = resolve_token(cli)?;
40 let client = ClickUpClient::new(&token, cli.timeout)?;
41 let output = OutputConfig::from_cli(&cli.output, &cli.fields, cli.no_header, cli.quiet);
42
43 match command {
44 WorkspaceCommands::List => {
45 let resp = client.get("/v2/team").await?;
46 let teams = resp
47 .get("teams")
48 .and_then(|t| t.as_array())
49 .cloned()
50 .unwrap_or_default();
51 let items: Vec<serde_json::Value> = teams
53 .iter()
54 .map(|ws| {
55 serde_json::json!({
56 "id": ws.get("id").and_then(|v| v.as_str()).unwrap_or("-"),
57 "name": ws.get("name").and_then(|v| v.as_str()).unwrap_or("-"),
58 "members": ws.get("members").and_then(|m| m.as_array()).map(|a| a.len()).unwrap_or(0),
59 })
60 })
61 .collect();
62 output.print_items(&items, &["id", "name", "members"], "id");
63 Ok(())
64 }
65 WorkspaceCommands::Seats => {
66 let ws_id = resolve_workspace(cli)?;
67 let resp = client.get(&format!("/v2/team/{}/seats", ws_id)).await?;
68 if cli.output == "json" {
69 println!("{}", serde_json::to_string_pretty(&resp).unwrap());
70 } else {
71 let filled = resp.get("seats").and_then(|s| s.get("members"))
73 .and_then(|m| m.get("filled_members_seats"))
74 .and_then(|v| v.as_u64())
75 .unwrap_or(0);
76 let total = resp.get("seats").and_then(|s| s.get("members"))
77 .and_then(|m| m.get("total_members_seats"))
78 .and_then(|v| v.as_u64())
79 .or_else(|| {
80 resp.get("filled_members_seats").and_then(|v| v.as_u64())
82 })
83 .unwrap_or(0);
84 let items = vec![serde_json::json!({
85 "filled_seats": filled,
86 "total_seats": total,
87 })];
88 output.print_items(&items, &["filled_seats", "total_seats"], "filled_seats");
89 }
90 Ok(())
91 }
92 WorkspaceCommands::Plan => {
93 let ws_id = resolve_workspace(cli)?;
94 let resp = client.get(&format!("/v2/team/{}/plan", ws_id)).await?;
95 if cli.output == "json" {
96 println!("{}", serde_json::to_string_pretty(&resp).unwrap());
97 } else {
98 let plan_name = resp.get("plan_id")
99 .or_else(|| resp.get("plan_name"))
100 .and_then(|v| v.as_str())
101 .unwrap_or("-");
102 println!("Plan: {}", plan_name);
103 }
104 Ok(())
105 }
106 }
107}