feat(cli): improve member design and usability

This commit is contained in:
2025-08-28 16:21:39 -04:00
parent 954abb704e
commit 94900b19f7
7 changed files with 453 additions and 106 deletions

View File

@@ -12,7 +12,7 @@ if TYPE_CHECKING:
from .commands import (
cmd_members_list, cmd_members_show,
cmd_schedules_list, cmd_schedules_show, cmd_schedules_accept, cmd_schedules_decline, cmd_schedules_schedule,
cmd_schedules_list, cmd_schedules_show, cmd_schedules_accept, cmd_schedules_decline, cmd_schedules_remove, cmd_schedules_schedule,
cmd_services_list,
)
@@ -173,15 +173,12 @@ def display_schedules_menu():
print(f"\n{Colors.HEADER}Schedules{Colors.RESET}")
print(f"{Colors.GREY}" * 50 + f"{Colors.RESET}")
print()
print(f" {Colors.CYAN}1.{Colors.RESET} List all schedules")
print(f" {Colors.CYAN}2.{Colors.RESET} List pending schedules")
print(f" {Colors.CYAN}3.{Colors.RESET} List accepted schedules")
print(f" {Colors.CYAN}4.{Colors.RESET} List declined schedules")
print(f" {Colors.CYAN}5.{Colors.RESET} Show schedule details")
print(f" {Colors.CYAN}6.{Colors.RESET} {Colors.GREEN}Accept a schedule{Colors.RESET}")
print(f" {Colors.CYAN}7.{Colors.RESET} {Colors.RED}Decline a schedule{Colors.RESET}")
print(f" {Colors.CYAN}8.{Colors.RESET} {Colors.YELLOW}Schedule next member for service{Colors.RESET}")
print(f" {Colors.CYAN}9.{Colors.RESET} {Colors.DIM}Back to main menu{Colors.RESET}")
print(f" {Colors.CYAN}1.{Colors.RESET} Browse schedules")
print(f" {Colors.CYAN}2.{Colors.RESET} {Colors.GREEN}Accept a schedule{Colors.RESET}")
print(f" {Colors.CYAN}3.{Colors.RESET} {Colors.RED}Decline a schedule{Colors.RESET}")
print(f" {Colors.CYAN}4.{Colors.RESET} {Colors.ERROR}Remove scheduled members{Colors.RESET}")
print(f" {Colors.CYAN}5.{Colors.RESET} {Colors.YELLOW}Schedule next member for service{Colors.RESET}")
print(f" {Colors.CYAN}6.{Colors.RESET} {Colors.DIM}Back to main menu{Colors.RESET}")
print()
@@ -251,6 +248,25 @@ def get_date_input(prompt: str = "Enter date (YYYY-MM-DD)") -> str:
return ""
def get_date_input_optional(prompt: str = "Enter date (YYYY-MM-DD)") -> str:
"""Get optional date input from user (allows empty input)."""
while True:
try:
print(create_simple_input_box(prompt))
date_str = input(f"{Colors.INPUT_BOX}└─> {Colors.RESET}").strip()
if not date_str:
return "" # Allow empty input
# Basic date format validation
if len(date_str) == 10 and date_str.count('-') == 2:
parts = date_str.split('-')
if len(parts[0]) == 4 and len(parts[1]) == 2 and len(parts[2]) == 2:
return date_str
print(f"{Colors.ERROR}Please use format YYYY-MM-DD (e.g., 2025-09-07){Colors.RESET}")
except (KeyboardInterrupt, EOFError):
print(f"\n{Colors.WARNING}Operation cancelled{Colors.RESET}")
return ""
class MockArgs:
"""Mock args object for interactive commands."""
def __init__(self, **kwargs):
@@ -306,73 +322,105 @@ def handle_schedules_menu(cli: "NimbusFlowCLI"):
while True:
clear_screen()
display_schedules_menu()
choice = get_user_choice(9)
choice = get_user_choice(6)
if choice == 1: # List all schedules
clear_screen()
print(f"{Colors.SUCCESS}Listing all schedules...{Colors.RESET}\n")
cmd_schedules_list(cli, MockArgs(service_id=None, status=None))
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == 2: # List pending schedules
clear_screen()
print(f"{Colors.WARNING}Listing pending schedules...{Colors.RESET}\n")
cmd_schedules_list(cli, MockArgs(service_id=None, status="pending"))
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == 3: # List accepted schedules
clear_screen()
print(f"{Colors.SUCCESS}Listing accepted schedules...{Colors.RESET}\n")
cmd_schedules_list(cli, MockArgs(service_id=None, status="accepted"))
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == 4: # List declined schedules
clear_screen()
print(f"{Colors.ERROR}Listing declined schedules...{Colors.RESET}\n")
cmd_schedules_list(cli, MockArgs(service_id=None, status="declined"))
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == 5: # Show schedule details
clear_screen()
schedule_id = get_text_input("Enter schedule ID", True)
if schedule_id.isdigit():
# Get date filter
date = get_date_input_optional("Enter date to filter schedules (or press Enter to skip)")
if not date:
clear_screen()
print(f"{Colors.SUCCESS}Showing details for schedule {schedule_id}...{Colors.RESET}\n")
cmd_schedules_show(cli, MockArgs(schedule_id=int(schedule_id)))
cmd_schedules_list(cli, MockArgs(service_id=None, status=None))
else:
print(f"{Colors.ERROR}Invalid schedule ID{Colors.RESET}")
# Find services for the date
try:
from datetime import date as date_type
target_date = date_type.fromisoformat(date)
all_services = cli.service_repo.list_all()
services_on_date = [s for s in all_services if s.ServiceDate == target_date]
except ValueError:
clear_screen()
print(f"{Colors.ERROR}Invalid date format. Please use YYYY-MM-DD format.{Colors.RESET}")
services_on_date = []
if not services_on_date:
clear_screen()
print(f"{Colors.ERROR}No services found for {date}{Colors.RESET}")
else:
clear_screen()
# Show available services for selection
service_type_map = {st.ServiceTypeId: st.TypeName for st in cli.service_type_repo.list_all()}
print(f"\n{Colors.HEADER}Services available on {date}{Colors.RESET}")
print(f"{Colors.GREY}" * 50 + f"{Colors.RESET}")
print()
for i, service in enumerate(services_on_date, 1):
type_name = service_type_map.get(service.ServiceTypeId, "Unknown")
print(f" {Colors.CYAN}{i}.{Colors.RESET} {type_name}")
print()
# Get service selection
try:
print(create_simple_input_box(f"Select service (1-{len(services_on_date)}) or press Enter to show all"))
choice_input = input(f"{Colors.INPUT_BOX}└─> {Colors.RESET}").strip()
if not choice_input:
# Empty input - show all services for this date
clear_screen()
cmd_schedules_list(cli, MockArgs(service_id=None, status=None, date=date))
elif not choice_input.isdigit():
print(f"{Colors.ERROR}Invalid selection{Colors.RESET}")
else:
service_choice = int(choice_input)
if service_choice < 1 or service_choice > len(services_on_date):
print(f"{Colors.ERROR}Please enter a number between 1 and {len(services_on_date)}{Colors.RESET}")
else:
clear_screen()
selected_service = services_on_date[service_choice - 1]
cmd_schedules_list(cli, MockArgs(service_id=selected_service.ServiceId, status=None))
except (KeyboardInterrupt, EOFError):
print(f"\n{Colors.WARNING}Operation cancelled{Colors.RESET}")
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == 6: # Accept schedule
elif choice == 2: # Accept schedule
clear_screen()
date = get_date_input("Enter date for interactive accept")
if date:
clear_screen()
print(f"{Colors.SUCCESS}Starting interactive accept for {date}...{Colors.RESET}\n")
cmd_schedules_accept(cli, MockArgs(date=date, schedule_id=None))
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == 7: # Decline schedule
elif choice == 3: # Decline schedule
clear_screen()
date = get_date_input("Enter date for interactive decline")
if date:
clear_screen()
reason = get_text_input("Enter decline reason (optional)", False)
clear_screen()
print(f"{Colors.ERROR}Starting interactive decline for {date}...{Colors.RESET}\n")
cmd_schedules_decline(cli, MockArgs(date=date, schedule_id=None, reason=reason))
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == 8: # Schedule next member
elif choice == 4: # Remove scheduled members
clear_screen()
date = get_date_input("Enter date to remove schedules for")
if date:
clear_screen()
cmd_schedules_remove(cli, MockArgs(date=date, schedule_id=None))
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == 5: # Schedule next member
clear_screen()
date = get_date_input("Enter date to schedule for")
if date:
clear_screen()
print(f"{Colors.WARNING}Starting scheduling for {date}...{Colors.RESET}\n")
cmd_schedules_schedule(cli, MockArgs(service_id=None, date=date, classifications=None))
input(f"\n{Colors.DIM}Press Enter to continue...{Colors.RESET}")
elif choice == 9: # Back to main menu
elif choice == 6: # Back to main menu
break