gantt-board/scripts/project.sh

232 lines
5.3 KiB
Bash
Executable File

#!/bin/bash
# Project CLI for Gantt Board (API passthrough)
# Usage: ./project.sh [create|list|get|update|delete] [options]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./lib/api_client.sh
source "$SCRIPT_DIR/lib/api_client.sh"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}i${NC} $1"; }
log_success() { echo -e "${GREEN}ok${NC} $1"; }
log_warning() { echo -e "${YELLOW}warn${NC} $1"; }
log_error() { echo -e "${RED}error${NC} $1"; }
UUID_PATTERN='^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
check_dependencies() {
if ! command -v jq >/dev/null 2>&1; then
log_error "jq is required. Install with: brew install jq"
exit 1
fi
}
show_usage() {
cat << 'USAGE'
Project CLI for Gantt Board
USAGE:
./project.sh [COMMAND] [OPTIONS]
COMMANDS:
create Create a new project
list List all projects
get <id-or-name> Get a specific project
update <id-or-name> Update a project
delete <id-or-name> Delete a project
CREATE OPTIONS:
--name "Name" Project name (required)
--description "Description" Project description
--color "#hexcolor" Project color
LIST OPTIONS:
--json Output as JSON
UPDATE OPTIONS:
--name "Name"
--description "Description"
--color "#hexcolor"
USAGE
}
resolve_project_id() {
local identifier="$1"
if [[ "$identifier" =~ $UUID_PATTERN ]]; then
echo "$identifier"
return 0
fi
local response
response=$(api_call GET "/projects")
local project_id
project_id=$(echo "$response" | jq -r --arg q "$identifier" '
.projects
| map(select((.name // "") | ascii_downcase | contains($q | ascii_downcase)))
| .[0].id // empty
')
if [[ -n "$project_id" ]]; then
echo "$project_id"
return 0
fi
log_error "Project '$identifier' not found"
return 1
}
cmd_create() {
local name=""
local description=""
local color="#3b82f6"
while [[ $# -gt 0 ]]; do
case "${1:-}" in
--name) name="${2:-}"; shift 2 ;;
--description) description="${2:-}"; shift 2 ;;
--color) color="${2:-}"; shift 2 ;;
*) shift ;;
esac
done
if [[ -z "$name" ]]; then
log_error "Name is required (use --name)"
exit 1
fi
local payload
payload=$(jq -n --arg name "$name" --arg description "$description" --arg color "$color" '
{
name: $name,
description: (if $description == "" then null else $description end),
color: $color
}
')
log_info "Creating project..."
api_call POST "/projects" "$payload" | jq .
}
cmd_list() {
local output_json=false
while [[ $# -gt 0 ]]; do
case "${1:-}" in
--json) output_json=true; shift ;;
*) shift ;;
esac
done
local response
response=$(api_call GET "/projects")
if [[ "$output_json" == true ]]; then
echo "$response" | jq '.projects'
return
fi
local projects
projects=$(echo "$response" | jq '.projects')
local count
count=$(echo "$projects" | jq 'length')
log_success "Found $count project(s)"
printf "%-36s %-25s %-30s\n" "ID" "NAME" "DESCRIPTION"
printf "%-36s %-25s %-30s\n" "------------------------------------" "-------------------------" "------------------------------"
echo "$projects" | jq -r '.[] | [.id, (.name // "" | tostring | .[0:23]), (.description // "" | tostring | .[0:28])] | @tsv' \
| while IFS=$'\t' read -r id name desc; do
printf "%-36s %-25s %-30s\n" "$id" "$name" "$desc"
done
}
cmd_get() {
local identifier="${1:-}"
if [[ -z "$identifier" ]]; then
log_error "Project ID or name required"
exit 1
fi
local project_id
project_id=$(resolve_project_id "$identifier")
log_info "Fetching project $project_id..."
api_call GET "/projects/$project_id" | jq '.project'
}
cmd_update() {
local identifier="${1:-}"
shift || true
if [[ -z "$identifier" ]]; then
log_error "Project ID or name required"
exit 1
fi
local project_id
project_id=$(resolve_project_id "$identifier")
local updates='{}'
while [[ $# -gt 0 ]]; do
case "${1:-}" in
--name) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {name: $v}'); shift 2 ;;
--description) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {description: $v}'); shift 2 ;;
--color) updates=$(echo "$updates" | jq --arg v "${2:-}" '. + {color: $v}'); shift 2 ;;
*) shift ;;
esac
done
if [[ "$updates" == "{}" ]]; then
log_warning "No update fields specified"
exit 0
fi
local payload
payload=$(echo "$updates" | jq --arg id "$project_id" '. + {id: $id}')
log_info "Updating project $project_id..."
api_call PATCH "/projects" "$payload" | jq .
}
cmd_delete() {
local identifier="${1:-}"
if [[ -z "$identifier" ]]; then
log_error "Project ID or name required"
exit 1
fi
local project_id
project_id=$(resolve_project_id "$identifier")
log_info "Deleting project $project_id..."
api_call DELETE "/projects" "{\"id\": \"$project_id\"}" | jq .
}
check_dependencies
COMMAND="${1:-}"
shift || true
case "$COMMAND" in
create) cmd_create "$@" ;;
list) cmd_list "$@" ;;
get) cmd_get "$@" ;;
update) cmd_update "$@" ;;
delete) cmd_delete "$@" ;;
help|--help|-h|"") show_usage ;;
*)
log_error "Unknown command: $COMMAND"
show_usage
exit 1
;;
esac