diff --git a/src/shared/python/shared/blocks.py b/src/shared/python/shared/blocks.py index 704fa1c..7d35abe 100644 --- a/src/shared/python/shared/blocks.py +++ b/src/shared/python/shared/blocks.py @@ -8,6 +8,12 @@ EASTERN = ZoneInfo("America/New_York") WEEKEND_DAYS = {"Saturday", "Sunday"} +SHIFT_LABELS = { + "day": "Day (8am–5pm)", + "night": "Night (5pm–8am)", +} + + def _format_shift_line( date: datetime, ext: str, @@ -17,8 +23,8 @@ def _format_shift_line( shift_type: str = "night", ) -> str: day_label = date.strftime("%a %b %-d") - if shift_type == "day": - day_label += " (Day 8a–5p)" + if shift_type in SHIFT_LABELS: + day_label += f" {SHIFT_LABELS[shift_type]}" if is_today: day_label = f"*{day_label} (today)*" @@ -56,17 +62,19 @@ def build_week_schedule(schedule, start_date: datetime | None = None) -> list[di day_name = date.strftime("%A") is_today = date_str == today_str + is_past = date_str < today_str + # Weekend day shift (8am–5pm) if day_name in WEEKEND_DAYS: ext, name, source = schedule.resolve_shift(date_str, day_name, "day") lines.append(_format_shift_line(date, ext, name, source, is_today, "day")) - if source == "available": + if source == "available" and not is_past: open_shifts.append((date_str, "day")) # After-hours (night) shift ext, name, source = schedule.resolve_shift(date_str, day_name) lines.append(_format_shift_line(date, ext, name, source, is_today)) - if source == "available": + if source == "available" and not is_past: open_shifts.append((date_str, "night")) blocks = [ @@ -89,14 +97,14 @@ def build_week_schedule(schedule, start_date: datetime | None = None) -> list[di elements = [] for date_str, shift_type in open_shifts: dt = datetime.strptime(date_str, "%Y-%m-%d") - suffix = " (Day)" if shift_type == "day" else "" + label = "Day" if shift_type == "day" else "Night" action_suffix = "_day" if shift_type == "day" else "" elements.append( { "type": "button", "text": { "type": "plain_text", - "text": f"Pick up {dt.strftime('%a %b %-d')}{suffix}", + "text": f"Pick up {dt.strftime('%a %b %-d')} {label}", }, "action_id": f"pickup_{date_str}{action_suffix}", "style": "primary", @@ -112,20 +120,26 @@ def build_week_schedule(schedule, start_date: datetime | None = None) -> list[di def build_shift_change_message( - user_id: str, date_str: str, action: str, ext: str, name: str + user_id: str, + date_str: str, + action: str, + ext: str, + name: str, + shift_type: str = "night", ) -> list[dict]: """Build a channel notification for a shift change.""" dt = datetime.strptime(date_str, "%Y-%m-%d") day_label = dt.strftime("%A, %b %-d") + type_label = f" ({SHIFT_LABELS.get(shift_type, shift_type)})" if shift_type == "day" else "" if action == "picked_up": - text = f":white_check_mark: <@{user_id}> picked up the shift for *{day_label}* (Ext {ext})" + text = f":white_check_mark: <@{user_id}> picked up the *{day_label}*{type_label} shift (Ext {ext})" elif action == "dropped": - text = f":warning: <@{user_id}> dropped the shift for *{day_label}* — it's now *Available*" + text = f":warning: <@{user_id}> dropped the *{day_label}*{type_label} shift — it's now *Available*" elif action == "swapped": - text = f":arrows_counterclockwise: <@{user_id}> swapped into the shift for *{day_label}* (Ext {ext})" + text = f":arrows_counterclockwise: <@{user_id}> swapped into the *{day_label}*{type_label} shift (Ext {ext})" else: - text = f"Shift updated for *{day_label}*: {name} (Ext {ext})" + text = f"Shift updated for *{day_label}*{type_label}: {name} (Ext {ext})" return [{"type": "section", "text": {"type": "mrkdwn", "text": text}}] diff --git a/src/slack-bot/app.py b/src/slack-bot/app.py index 7a7bd25..f254b54 100644 --- a/src/slack-bot/app.py +++ b/src/slack-bot/app.py @@ -153,6 +153,15 @@ def create_app( user_id = body["user"]["id"] channel_id = body["channel"]["id"] + today_str = datetime.now(EASTERN).strftime("%Y-%m-%d") + if date_str < today_str: + client.chat_postEphemeral( + channel=channel_id, + user=user_id, + text="That shift has already passed and can't be picked up.", + ) + return + employee = schedule.get_employee_by_slack_id(user_id) if not employee: client.chat_postEphemeral( @@ -177,7 +186,8 @@ def create_app( _update_3cx_routing(employee["extension"]) blocks = build_shift_change_message( - user_id, date_str, "picked_up", employee["extension"], employee["name"] + user_id, date_str, "picked_up", employee["extension"], employee["name"], + shift_type=shift_type, ) respond( response_type="in_channel", @@ -345,6 +355,10 @@ def create_app( return date_str = date.strftime("%Y-%m-%d") + if date_str < datetime.now(EASTERN).strftime("%Y-%m-%d"): + respond(text="You can't pick up a shift in the past.") + return + day_name = date.strftime("%A") ext, name, source = schedule.resolve_shift(date_str, day_name) @@ -396,6 +410,10 @@ def create_app( return date_str = date.strftime("%Y-%m-%d") + if date_str < datetime.now(EASTERN).strftime("%Y-%m-%d"): + respond(text="You can't drop a shift in the past.") + return + day_name = date.strftime("%A") ext, name, source = schedule.resolve_shift(date_str, day_name)