From edfc6c0bb02a4a0644ab8c95150122ff74718ccc Mon Sep 17 00:00:00 2001 From: amoussa1229 <166072409+amoussa1229@users.noreply.github.com> Date: Fri, 26 Jun 2026 18:57:33 +0000 Subject: [PATCH] Refine night-row labeling and drop notifications Weekday rows are night-only, so the moon glyph alone marks the after-hours shift; the verbose time-range label is kept only on weekend rows where day and night shifts coexist. Channel shift-change notifications now label the shift on every day for parity. Thread the caller's user_id through the regular-drop path instead of re-reading it off the employee record, and document the user-facing changes. Refs: #134 --- CHANGELOG.md | 17 ++++++++++++++++ src/shared/shared/blocks.py | 39 ++++++++++++++++++++++++++----------- src/slack-bot/CHANGELOG.md | 17 ++++++++++++++++ src/slack-bot/app.py | 6 +++--- tests/shared/test_blocks.py | 28 ++++++++++++++++++++------ 5 files changed, 87 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index accfbf3..df3339c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,23 @@ fine and still supported. --- +## v1.12.0 — June 26, 2026 + +**Clearer shift drops and an easier-to-read schedule.** Two small quality-of-life +improvements: + +- **`/oncall drop` can now say which shift to let go.** If you hold more than one + shift on the same day — say a holiday day slot *and* that night's after-hours + shift, or a weekend day *and* night shift — dropping no longer guesses for you. + The bot lists what you hold and asks you to add `day`, `night`, or `holiday` + (for example `/oncall drop saturday night`), the same way picking one up + already works. Asking to drop a shift you don't actually hold is turned down + instead of quietly dropping the wrong one. +- **Night shifts stand out on the two-week post.** Day and night rows now carry + their own icons — a sun for the daytime shift and a moon for the after-hours + (night) shift — so the on-call row is easy to spot at a glance. Weekend rows, + which have both a day and a night shift, still spell out the full time range. + ## v1.11.1 — June 17, 2026 **Fix holiday call routing to 3CX.** The holiday router was calling the wrong 3CX diff --git a/src/shared/shared/blocks.py b/src/shared/shared/blocks.py index dda207a..6ff7987 100644 --- a/src/shared/shared/blocks.py +++ b/src/shared/shared/blocks.py @@ -70,8 +70,9 @@ SHIFT_LABELS = { "night": "Night (5pm–8am)", } -# Per-shift glyphs so day vs. night rows are distinguishable at a glance — even -# on weekdays, where only the night (after-hours) shift exists. +# Per-shift glyphs so day vs. night rows are distinguishable at a glance. On +# weekdays only the night (after-hours) shift exists, so the moon glyph alone +# differentiates the row; the verbose time-range label is reserved for weekends. SHIFT_GLYPHS = { "day": ":sunny:", "night": ":crescent_moon:", @@ -93,10 +94,15 @@ def _format_shift_line( source: str, is_today: bool, shift_type: str = "night", + with_label: bool = True, ) -> str: day_label = date.strftime("%a %b %-d") - if shift_type in SHIFT_LABELS: - day_label += f" {SHIFT_GLYPHS[shift_type]} {SHIFT_LABELS[shift_type]}" + glyph = SHIFT_GLYPHS.get(shift_type) + if glyph: + if with_label and shift_type in SHIFT_LABELS: + day_label += f" {glyph} {SHIFT_LABELS[shift_type]}" + else: + day_label += f" {glyph}" if is_today: day_label = f"*{day_label} (today)*" @@ -182,9 +188,21 @@ def build_week_schedule(schedule, start_date: datetime | None = None) -> list[di if source == "available" and not is_past: open_shifts.append((date_str, "day")) - # After-hours (night) shift + # After-hours (night) shift. Weekdays are night-only, so the moon glyph + # carries the meaning; only weekends (where both shifts coexist) repeat + # the full time-range label. 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, + "night", + with_label=day_name in WEEKEND_DAYS, + ) + ) if source == "available" and not is_past: open_shifts.append((date_str, "night")) @@ -252,11 +270,10 @@ def build_shift_change_message( """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 dt.strftime("%A") in WEEKEND_DAYS - else "" - ) + # Label the shift on every day, not just weekends: a single-line notification + # has room to spell out the after-hours (night) shift the static post conveys + # with the moon glyph. + type_label = f" ({SHIFT_LABELS.get(shift_type, shift_type)})" if action == "picked_up": text = f":white_check_mark: <@{user_id}> picked up the *{day_label}*{type_label} shift (Ext {ext})" diff --git a/src/slack-bot/CHANGELOG.md b/src/slack-bot/CHANGELOG.md index accfbf3..df3339c 100644 --- a/src/slack-bot/CHANGELOG.md +++ b/src/slack-bot/CHANGELOG.md @@ -10,6 +10,23 @@ fine and still supported. --- +## v1.12.0 — June 26, 2026 + +**Clearer shift drops and an easier-to-read schedule.** Two small quality-of-life +improvements: + +- **`/oncall drop` can now say which shift to let go.** If you hold more than one + shift on the same day — say a holiday day slot *and* that night's after-hours + shift, or a weekend day *and* night shift — dropping no longer guesses for you. + The bot lists what you hold and asks you to add `day`, `night`, or `holiday` + (for example `/oncall drop saturday night`), the same way picking one up + already works. Asking to drop a shift you don't actually hold is turned down + instead of quietly dropping the wrong one. +- **Night shifts stand out on the two-week post.** Day and night rows now carry + their own icons — a sun for the daytime shift and a moon for the after-hours + (night) shift — so the on-call row is easy to spot at a glance. Weekend rows, + which have both a day and a night shift, still spell out the full time range. + ## v1.11.1 — June 17, 2026 **Fix holiday call routing to 3CX.** The holiday router was calling the wrong 3CX diff --git a/src/slack-bot/app.py b/src/slack-bot/app.py index 2a537b5..a4ef6ed 100644 --- a/src/slack-bot/app.py +++ b/src/slack-bot/app.py @@ -1038,7 +1038,7 @@ def _handle_drop( _drop_regular( respond, schedule, - employee, + user_id, date, date_str, day_name, @@ -1052,7 +1052,7 @@ def _handle_drop( def _drop_regular( respond, schedule, - employee, + user_id, date, date_str, day_name, @@ -1088,7 +1088,7 @@ def _drop_regular( ) blocks = build_shift_change_message( - employee.get("slack_user_id", ""), + user_id, date_str, "dropped", ext, diff --git a/tests/shared/test_blocks.py b/tests/shared/test_blocks.py index 6e8a8b4..c3f9b17 100644 --- a/tests/shared/test_blocks.py +++ b/tests/shared/test_blocks.py @@ -52,12 +52,28 @@ class TestBuildWeekSchedule: assert "Alice (Ext 114)" in blocks[1]["text"]["text"] @freeze_time("2026-06-01 12:00:00") - def test_weekday_night_row_shows_night_glyph_and_label(self, schedule, seed): - # A weekday (after-hours) night row is now unmistakable: moon glyph + label. + def test_weekday_night_row_shows_glyph_without_time_label(self, schedule, seed): + # Weekdays are night-only, so the moon glyph alone differentiates the row; + # the verbose time-range label is reserved for weekends where both shifts + # coexist (and would otherwise repeat on ~10 weekday rows). seed.weekly("Wednesday", "114", "Alice") text = build_week_schedule(schedule)[1]["text"]["text"] - assert ":crescent_moon:" in text - assert "Night (5pm" in text + wednesday_line = next( + line for line in text.splitlines() if "Alice (Ext 114)" in line + ) + assert ":crescent_moon:" in wednesday_line + assert "Night (5pm" not in wednesday_line + + @freeze_time("2026-06-01 12:00:00") + def test_weekend_night_row_keeps_glyph_and_time_label(self, schedule, seed): + # Weekends carry both shifts, so the night row keeps the full label. + seed.weekly("Saturday", "200", "Alice") + text = build_week_schedule(schedule)[1]["text"]["text"] + saturday_line = next( + line for line in text.splitlines() if "Alice (Ext 200)" in line + ) + assert ":crescent_moon:" in saturday_line + assert "Night (5pm" in saturday_line @freeze_time("2026-06-01 12:00:00") def test_weekend_day_row_shows_day_glyph_and_label(self, schedule, seed): @@ -123,8 +139,8 @@ class TestBuildShiftChangeMessage: ) text = blocks[0]["text"]["text"] assert "<@U1>" in text and "picked up" in text and "Ext 114" in text - # Weekday → no (Day/Night) label - assert "(Night" not in text + # Weekday is night-only → the notification still labels the after-hours shift + assert "Night (5pm" in text def test_dropped_shows_available(self): blocks = build_shift_change_message(