feat(backend): design the cli to look professional
This commit is contained in:
@@ -6,22 +6,59 @@ from typing import Optional
|
||||
from datetime import datetime
|
||||
from backend.models.enums import ScheduleStatus
|
||||
|
||||
# ANSI color codes for table formatting
|
||||
class TableColors:
|
||||
RESET = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
DIM = '\033[2m'
|
||||
BLUE = '\033[94m'
|
||||
GREEN = '\033[92m'
|
||||
YELLOW = '\033[93m'
|
||||
RED = '\033[91m'
|
||||
CYAN = '\033[96m'
|
||||
WHITE = '\033[97m'
|
||||
GREY = '\033[90m'
|
||||
|
||||
# Table-specific colors
|
||||
HEADER = '\033[1m\033[96m' # Bold Cyan
|
||||
SUCCESS = '\033[1m\033[92m' # Bold Green
|
||||
ERROR = '\033[1m\033[91m' # Bold Red
|
||||
WARNING = '\033[1m\033[93m' # Bold Yellow
|
||||
BORDER = '\033[90m' # Grey
|
||||
|
||||
|
||||
def format_member_row(member, classification_name: Optional[str] = None) -> str:
|
||||
"""Format a member for table display."""
|
||||
active = "✓" if member.IsActive else "✗"
|
||||
"""Format a member for table display with colors."""
|
||||
# Color coding for active status
|
||||
if member.IsActive:
|
||||
active = f"{TableColors.SUCCESS}✓ Active{TableColors.RESET}"
|
||||
name_color = TableColors.BOLD
|
||||
else:
|
||||
active = f"{TableColors.DIM}✗ Inactive{TableColors.RESET}"
|
||||
name_color = TableColors.DIM
|
||||
|
||||
classification = classification_name or "N/A"
|
||||
return f"{member.MemberId:3d} | {member.FirstName:<12} | {member.LastName:<15} | {classification:<12} | {active:^6} | {member.Email or 'N/A'}"
|
||||
email = member.Email or f"{TableColors.DIM}N/A{TableColors.RESET}"
|
||||
|
||||
return (f"{TableColors.CYAN}{member.MemberId:3d}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{name_color}{member.FirstName:<12}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{name_color}{member.LastName:<15}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{TableColors.YELLOW}{classification:<12}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{active:<19} {TableColors.BORDER}│{TableColors.RESET} {email}")
|
||||
|
||||
|
||||
def format_schedule_row(schedule, member_name: str = "", service_info: str = "") -> str:
|
||||
"""Format a schedule for table display."""
|
||||
status_symbols = {
|
||||
ScheduleStatus.PENDING: "⏳",
|
||||
ScheduleStatus.ACCEPTED: "✅",
|
||||
ScheduleStatus.DECLINED: "❌"
|
||||
}
|
||||
status_symbol = status_symbols.get(ScheduleStatus.from_raw(schedule.Status), "❓")
|
||||
"""Format a schedule for table display with colors."""
|
||||
# Color-coded status formatting
|
||||
status_enum = ScheduleStatus.from_raw(schedule.Status)
|
||||
if status_enum == ScheduleStatus.PENDING:
|
||||
status_display = f"{TableColors.WARNING}⏳ Pending{TableColors.RESET}"
|
||||
elif status_enum == ScheduleStatus.ACCEPTED:
|
||||
status_display = f"{TableColors.SUCCESS}✅ Accepted{TableColors.RESET}"
|
||||
elif status_enum == ScheduleStatus.DECLINED:
|
||||
status_display = f"{TableColors.ERROR}❌ Declined{TableColors.RESET}"
|
||||
else:
|
||||
status_display = f"{TableColors.DIM}❓ Unknown{TableColors.RESET}"
|
||||
|
||||
# Handle ScheduledAt - could be datetime object or string from DB
|
||||
if schedule.ScheduledAt:
|
||||
@@ -36,6 +73,50 @@ def format_schedule_row(schedule, member_name: str = "", service_info: str = "")
|
||||
# If it's already a datetime object
|
||||
scheduled_date = schedule.ScheduledAt.strftime("%Y-%m-%d %H:%M")
|
||||
else:
|
||||
scheduled_date = "N/A"
|
||||
scheduled_date = f"{TableColors.DIM}N/A{TableColors.RESET}"
|
||||
|
||||
return f"{schedule.ScheduleId:3d} | {status_symbol} {schedule.Status:<8} | {member_name:<20} | {service_info:<15} | {scheduled_date}"
|
||||
member_display = member_name if member_name else f"{TableColors.DIM}Unknown{TableColors.RESET}"
|
||||
service_display = service_info if service_info else f"{TableColors.DIM}Unknown{TableColors.RESET}"
|
||||
|
||||
return (f"{TableColors.CYAN}{schedule.ScheduleId:3d}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{status_display:<20} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{TableColors.BOLD}{member_display:<20}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{TableColors.BLUE}{service_display:<20}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} {scheduled_date}")
|
||||
|
||||
|
||||
def create_table_header(*columns) -> str:
|
||||
"""Create a styled table header."""
|
||||
header_parts = []
|
||||
for i, column in enumerate(columns):
|
||||
if i == 0:
|
||||
header_parts.append(f"{TableColors.HEADER}{column}{TableColors.RESET}")
|
||||
else:
|
||||
header_parts.append(f" {TableColors.BORDER}│{TableColors.RESET} {TableColors.HEADER}{column}{TableColors.RESET}")
|
||||
return "".join(header_parts)
|
||||
|
||||
|
||||
def create_table_separator(total_width: int) -> str:
|
||||
"""Create a styled table separator line."""
|
||||
return f"{TableColors.BORDER}{'─' * total_width}{TableColors.RESET}"
|
||||
|
||||
|
||||
def format_service_row(service, type_name: str, counts: dict) -> str:
|
||||
"""Format a service for table display with colors."""
|
||||
# Format date properly
|
||||
date_str = str(service.ServiceDate) if service.ServiceDate else f"{TableColors.DIM}N/A{TableColors.RESET}"
|
||||
|
||||
# Color-code the counts
|
||||
total_display = f"{TableColors.BOLD}{counts['total']}{TableColors.RESET}" if counts['total'] > 0 else f"{TableColors.DIM}0{TableColors.RESET}"
|
||||
|
||||
pending_display = f"{TableColors.WARNING}{counts['pending']}{TableColors.RESET}" if counts['pending'] > 0 else f"{TableColors.DIM}0{TableColors.RESET}"
|
||||
|
||||
accepted_display = f"{TableColors.SUCCESS}{counts['accepted']}{TableColors.RESET}" if counts['accepted'] > 0 else f"{TableColors.DIM}0{TableColors.RESET}"
|
||||
|
||||
declined_display = f"{TableColors.ERROR}{counts['declined']}{TableColors.RESET}" if counts['declined'] > 0 else f"{TableColors.DIM}0{TableColors.RESET}"
|
||||
|
||||
return (f"{TableColors.CYAN}{service.ServiceId:<3}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{TableColors.BOLD}{date_str:<12}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{TableColors.YELLOW}{type_name:<12}{TableColors.RESET} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{total_display:<5} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{pending_display:<7} {TableColors.BORDER}│{TableColors.RESET} "
|
||||
f"{accepted_display:<8} {TableColors.BORDER}│{TableColors.RESET} {declined_display}")
|
||||
Reference in New Issue
Block a user