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