mirror of
https://github.com/Sea-Haven-Industries/afterhours-shift-manager.git
synced 2026-10-04 19:32:06 +00:00
- Reject /oncall pick and /oncall drop for past dates - Show ephemeral error when stale pickup buttons are clicked - Hide pickup buttons for dates in the past - Add explicit "Day (8am-5pm)" and "Night (5pm-8am)" labels to schedule lines, pickup buttons, and shift change notifications
This commit is contained in:
parent
9dd7cbd0f8
commit
e9b55d357b
2 changed files with 44 additions and 12 deletions
|
|
@ -8,6 +8,12 @@ EASTERN = ZoneInfo("America/New_York")
|
||||||
WEEKEND_DAYS = {"Saturday", "Sunday"}
|
WEEKEND_DAYS = {"Saturday", "Sunday"}
|
||||||
|
|
||||||
|
|
||||||
|
SHIFT_LABELS = {
|
||||||
|
"day": "Day (8am–5pm)",
|
||||||
|
"night": "Night (5pm–8am)",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _format_shift_line(
|
def _format_shift_line(
|
||||||
date: datetime,
|
date: datetime,
|
||||||
ext: str,
|
ext: str,
|
||||||
|
|
@ -17,8 +23,8 @@ def _format_shift_line(
|
||||||
shift_type: str = "night",
|
shift_type: str = "night",
|
||||||
) -> str:
|
) -> str:
|
||||||
day_label = date.strftime("%a %b %-d")
|
day_label = date.strftime("%a %b %-d")
|
||||||
if shift_type == "day":
|
if shift_type in SHIFT_LABELS:
|
||||||
day_label += " (Day 8a–5p)"
|
day_label += f" {SHIFT_LABELS[shift_type]}"
|
||||||
if is_today:
|
if is_today:
|
||||||
day_label = f"*{day_label} (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")
|
day_name = date.strftime("%A")
|
||||||
is_today = date_str == today_str
|
is_today = date_str == today_str
|
||||||
|
|
||||||
|
is_past = date_str < today_str
|
||||||
|
|
||||||
# Weekend day shift (8am–5pm)
|
# Weekend day shift (8am–5pm)
|
||||||
if day_name in WEEKEND_DAYS:
|
if day_name in WEEKEND_DAYS:
|
||||||
ext, name, source = schedule.resolve_shift(date_str, day_name, "day")
|
ext, name, source = schedule.resolve_shift(date_str, day_name, "day")
|
||||||
lines.append(_format_shift_line(date, ext, name, source, is_today, "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"))
|
open_shifts.append((date_str, "day"))
|
||||||
|
|
||||||
# After-hours (night) shift
|
# After-hours (night) shift
|
||||||
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
||||||
lines.append(_format_shift_line(date, ext, name, source, is_today))
|
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"))
|
open_shifts.append((date_str, "night"))
|
||||||
|
|
||||||
blocks = [
|
blocks = [
|
||||||
|
|
@ -89,14 +97,14 @@ def build_week_schedule(schedule, start_date: datetime | None = None) -> list[di
|
||||||
elements = []
|
elements = []
|
||||||
for date_str, shift_type in open_shifts:
|
for date_str, shift_type in open_shifts:
|
||||||
dt = datetime.strptime(date_str, "%Y-%m-%d")
|
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 ""
|
action_suffix = "_day" if shift_type == "day" else ""
|
||||||
elements.append(
|
elements.append(
|
||||||
{
|
{
|
||||||
"type": "button",
|
"type": "button",
|
||||||
"text": {
|
"text": {
|
||||||
"type": "plain_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}",
|
"action_id": f"pickup_{date_str}{action_suffix}",
|
||||||
"style": "primary",
|
"style": "primary",
|
||||||
|
|
@ -112,20 +120,26 @@ def build_week_schedule(schedule, start_date: datetime | None = None) -> list[di
|
||||||
|
|
||||||
|
|
||||||
def build_shift_change_message(
|
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]:
|
) -> list[dict]:
|
||||||
"""Build a channel notification for a shift change."""
|
"""Build a channel notification for a shift change."""
|
||||||
dt = datetime.strptime(date_str, "%Y-%m-%d")
|
dt = datetime.strptime(date_str, "%Y-%m-%d")
|
||||||
day_label = dt.strftime("%A, %b %-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":
|
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":
|
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":
|
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:
|
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}}]
|
return [{"type": "section", "text": {"type": "mrkdwn", "text": text}}]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,15 @@ def create_app(
|
||||||
user_id = body["user"]["id"]
|
user_id = body["user"]["id"]
|
||||||
channel_id = body["channel"]["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)
|
employee = schedule.get_employee_by_slack_id(user_id)
|
||||||
if not employee:
|
if not employee:
|
||||||
client.chat_postEphemeral(
|
client.chat_postEphemeral(
|
||||||
|
|
@ -177,7 +186,8 @@ def create_app(
|
||||||
_update_3cx_routing(employee["extension"])
|
_update_3cx_routing(employee["extension"])
|
||||||
|
|
||||||
blocks = build_shift_change_message(
|
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(
|
respond(
|
||||||
response_type="in_channel",
|
response_type="in_channel",
|
||||||
|
|
@ -345,6 +355,10 @@ def create_app(
|
||||||
return
|
return
|
||||||
|
|
||||||
date_str = date.strftime("%Y-%m-%d")
|
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")
|
day_name = date.strftime("%A")
|
||||||
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
||||||
|
|
||||||
|
|
@ -396,6 +410,10 @@ def create_app(
|
||||||
return
|
return
|
||||||
|
|
||||||
date_str = date.strftime("%Y-%m-%d")
|
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")
|
day_name = date.strftime("%A")
|
||||||
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
ext, name, source = schedule.resolve_shift(date_str, day_name)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue