1use crate::client::ClickUpClient;
2use crate::commands::auth::resolve_token;
3use crate::commands::workspace::resolve_workspace;
4use crate::error::CliError;
5use crate::output::OutputConfig;
6use crate::Cli;
7use clap::Subcommand;
8
9#[derive(Subcommand)]
10pub enum ViewCommands {
11 List {
13 #[arg(long = "workspace-level", conflicts_with_all = &["space", "folder", "list"])]
15 workspace_level: bool,
16 #[arg(long, conflicts_with_all = &["workspace", "folder", "list"])]
18 space: Option<String>,
19 #[arg(long, conflicts_with_all = &["workspace", "space", "list"])]
21 folder: Option<String>,
22 #[arg(long, conflicts_with_all = &["workspace", "space", "folder"])]
24 list: Option<String>,
25 },
26 Get {
28 id: String,
30 },
31 Create {
33 #[arg(long)]
35 name: String,
36 #[arg(long, name = "type")]
38 view_type: String,
39 #[arg(long = "workspace-level", conflicts_with_all = &["space", "folder", "list"])]
41 workspace_level: bool,
42 #[arg(long, conflicts_with_all = &["workspace", "folder", "list"])]
44 space: Option<String>,
45 #[arg(long, conflicts_with_all = &["workspace", "space", "list"])]
47 folder: Option<String>,
48 #[arg(long, conflicts_with_all = &["workspace", "space", "folder"])]
50 list: Option<String>,
51 },
52 Update {
54 id: String,
56 #[arg(long)]
58 name: Option<String>,
59 },
60 Delete {
62 id: String,
64 },
65 Tasks {
67 id: String,
69 #[arg(long, default_value = "0")]
71 page: u32,
72 },
73}
74
75const VIEW_FIELDS: &[&str] = &["id", "name", "type"];
76
77pub async fn execute(command: ViewCommands, cli: &Cli) -> Result<(), CliError> {
78 let token = resolve_token(cli)?;
79 let client = ClickUpClient::new(&token, cli.timeout)?;
80 let output = OutputConfig::from_cli(&cli.output, &cli.fields, cli.no_header, cli.quiet);
81
82 match command {
83 ViewCommands::List {
84 workspace_level: workspace,
85 space,
86 folder,
87 list,
88 } => {
89 let url = if workspace {
90 let ws_id = resolve_workspace(cli)?;
91 format!("/v2/team/{}/view", ws_id)
92 } else if let Some(id) = space {
93 format!("/v2/space/{}/view", id)
94 } else if let Some(id) = folder {
95 format!("/v2/folder/{}/view", id)
96 } else if let Some(id) = list {
97 format!("/v2/list/{}/view", id)
98 } else {
99 return Err(CliError::ClientError {
100 message: "Specify a scope: --workspace, --space ID, --folder ID, or --list ID"
101 .into(),
102 status: 0,
103 });
104 };
105 let resp = client.get(&url).await?;
106 let views = resp
107 .get("views")
108 .and_then(|v| v.as_array())
109 .cloned()
110 .unwrap_or_default();
111 output.print_items(&views, VIEW_FIELDS, "id");
112 Ok(())
113 }
114 ViewCommands::Get { id } => {
115 let resp = client.get(&format!("/v2/view/{}", id)).await?;
116 let view = resp.get("view").cloned().unwrap_or(resp);
117 output.print_single(&view, VIEW_FIELDS, "id");
118 Ok(())
119 }
120 ViewCommands::Create {
121 name,
122 view_type,
123 workspace_level: workspace,
124 space,
125 folder,
126 list,
127 } => {
128 let url = if workspace {
129 let ws_id = resolve_workspace(cli)?;
130 format!("/v2/team/{}/view", ws_id)
131 } else if let Some(id) = space {
132 format!("/v2/space/{}/view", id)
133 } else if let Some(id) = folder {
134 format!("/v2/folder/{}/view", id)
135 } else if let Some(id) = list {
136 format!("/v2/list/{}/view", id)
137 } else {
138 return Err(CliError::ClientError {
139 message: "Specify a scope: --workspace, --space ID, --folder ID, or --list ID"
140 .into(),
141 status: 0,
142 });
143 };
144 let body = serde_json::json!({
145 "name": name,
146 "type": view_type,
147 });
148 let resp = client.post(&url, &body).await?;
149 let view = resp.get("view").cloned().unwrap_or(resp);
150 output.print_single(&view, VIEW_FIELDS, "id");
151 Ok(())
152 }
153 ViewCommands::Update { id, name } => {
154 let mut body = serde_json::Map::new();
155 if let Some(n) = name {
156 body.insert("name".into(), serde_json::Value::String(n));
157 }
158 let resp = client
159 .put(
160 &format!("/v2/view/{}", id),
161 &serde_json::Value::Object(body),
162 )
163 .await?;
164 let view = resp.get("view").cloned().unwrap_or(resp);
165 output.print_single(&view, VIEW_FIELDS, "id");
166 Ok(())
167 }
168 ViewCommands::Delete { id } => {
169 client.delete(&format!("/v2/view/{}", id)).await?;
170 output.print_message(&format!("View {} deleted", id));
171 Ok(())
172 }
173 ViewCommands::Tasks { id, page } => {
174 let resp = client
175 .get(&format!("/v2/view/{}/task?page={}", id, page))
176 .await?;
177 let tasks = resp
178 .get("tasks")
179 .and_then(|t| t.as_array())
180 .cloned()
181 .unwrap_or_default();
182 output.print_items(&tasks, &["id", "name", "status", "assignees"], "id");
183 Ok(())
184 }
185 }
186}