diff --git a/src/modules/lolschedule/README.md b/src/modules/lolschedule/README.md
index 51b5692..80e6719 100644
--- a/src/modules/lolschedule/README.md
+++ b/src/modules/lolschedule/README.md
@@ -7,7 +7,7 @@ LoL esports match schedule via the **lolesports.com** esports-api (the data feed
| Command | Description |
|---|---|
| `/lolschedule_today` | Today's matches (ICT), grouped by league. Scores for played + live, times for upcoming. |
-| `/lolschedule_week` | Next 7 days, grouped by day → league. |
+| `/lolschedule_week` | Next 7 days, grouped by league → day. |
| `/lolschedule_subscribe` | Opt the current chat into the daily 08:00 ICT digest. |
| `/lolschedule_unsubscribe` | Stop receiving the digest. |
@@ -63,7 +63,7 @@ Cache-first with KV. Key is `matches:{fromIso}:{toIso}`.
## Grouping
- `/lolschedule_today` — one section per league (header + match lines).
-- `/lolschedule_week` — one section per ICT day; within each day, leagues are sub-grouped.
+- `/lolschedule_week` — one section per league; within each league, matches are sub-grouped by ICT day (italic date headers).
- League ordering follows `LEAGUE_ORDER` in `format.js` (worlds / msi / first_stand first, then LCK / LPL / LEC / LCS, then the rest).
## Subscribers
diff --git a/src/modules/lolschedule/format.js b/src/modules/lolschedule/format.js
index 5004d18..c6252cd 100644
--- a/src/modules/lolschedule/format.js
+++ b/src/modules/lolschedule/format.js
@@ -151,7 +151,9 @@ export function renderToday(events, day) {
}
/**
- * Render week reply — grouped by ICT day → league.
+ * Render week reply — grouped by league → ICT day. The league ordering is
+ * determined by LEAGUE_ORDER; within each league, days appear chronologically
+ * and each day section lists its matches.
*
* @param {ScheduleEvent[]} events
* @param {Date} from
@@ -164,24 +166,25 @@ export function renderWeek(events, from, to) {
const header = `LoL — ${fromLbl} → ${toLbl} (ICT)`;
if (events.length === 0) return `${header}\nNo matches this week.`;
- /** @type {Map} */
- const days = new Map();
- for (const event of events) {
- const d = new Date(event.startTime);
- const key = ictDayKey(d);
- let g = days.get(key);
- if (!g) {
- g = { label: formatIctDayLabel(d), events: [] };
- days.set(key, g);
+ const leagueBlocks = groupByLeague(events).map((league) => {
+ /** @type {Map} */
+ const daysInLeague = new Map();
+ for (const event of league.events) {
+ const d = new Date(event.startTime);
+ const key = ictDayKey(d);
+ let g = daysInLeague.get(key);
+ if (!g) {
+ g = { label: formatIctDayLabel(d), lines: [] };
+ daysInLeague.set(key, g);
+ }
+ g.lines.push(formatEventLine(event));
}
- g.events.push(event);
- }
+ const daySections = [...daysInLeague.keys()].sort().map((key) => {
+ const day = daysInLeague.get(key);
+ return `${escapeHtml(day.label)}\n${day.lines.join("\n")}`;
+ });
+ return `${escapeHtml(league.name)}\n${daySections.join("\n")}`;
+ });
- const dayBlocks = [];
- for (const key of [...days.keys()].sort()) {
- const day = days.get(key);
- const leagueSections = groupByLeague(day.events).map(renderLeagueSection);
- dayBlocks.push(`${escapeHtml(day.label)}\n${leagueSections.join("\n")}`);
- }
- return `${header}\n\n${dayBlocks.join("\n\n")}`;
+ return `${header}\n\n${leagueBlocks.join("\n\n")}`;
}
diff --git a/tests/modules/lolschedule/format.test.js b/tests/modules/lolschedule/format.test.js
index 89cec78..238a512 100644
--- a/tests/modules/lolschedule/format.test.js
+++ b/tests/modules/lolschedule/format.test.js
@@ -153,12 +153,8 @@ describe("renderWeek", () => {
).toContain("No matches this week.");
});
- it("nests leagues under each ICT day in chronological order", () => {
+ it("groups by league first then nests days chronologically", () => {
const events = [
- evt({
- startTime: "2026-04-21T09:00:00Z",
- league: { name: "LCK", slug: "lck" },
- }),
evt({
startTime: "2026-04-22T09:00:00Z",
league: { name: "LPL", slug: "lpl" },
@@ -167,15 +163,22 @@ describe("renderWeek", () => {
startTime: "2026-04-22T11:00:00Z",
league: { name: "LCK", slug: "lck" },
}),
+ evt({
+ startTime: "2026-04-21T09:00:00Z",
+ league: { name: "LCK", slug: "lck" },
+ }),
];
const out = renderWeek(
events,
new Date("2026-04-21T00:00:00Z"),
new Date("2026-04-28T00:00:00Z"),
);
- expect(out.indexOf("Apr 21")).toBeLessThan(out.indexOf("Apr 22"));
- // Apr 22: LCK section appears before LPL (per LEAGUE_ORDER)
- const apr22Block = out.split("Apr 22")[1] || "";
- expect(apr22Block.indexOf("LCK")).toBeLessThan(apr22Block.indexOf("LPL"));
+ // Top-level: LCK section appears before LPL (per LEAGUE_ORDER).
+ expect(out.indexOf("LCK")).toBeLessThan(out.indexOf("LPL"));
+ // Inside LCK: Apr 21 before Apr 22.
+ const lckBlock = out.split("LCK")[1].split("LPL")[0];
+ expect(lckBlock.indexOf("Apr 21")).toBeLessThan(lckBlock.indexOf("Apr 22"));
+ // Day labels use italic inside league sections (distinct from league bold).
+ expect(lckBlock).toContain("Tue Apr 21");
});
});