github-issue-tower-defence-management 1.168.1 → 1.168.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>TDPM Console</title>
7
- <script type="module" crossorigin src="/assets/index-DkAuY63u.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-DL4JeBCA.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-U8I8ESZj.css">
9
9
  </head>
10
10
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.168.1",
3
+ "version": "1.168.2",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -18,7 +18,7 @@ type Story = StoryObj<typeof ConsoleItemList>;
18
18
 
19
19
  export const WithStoryGroups: Story = {
20
20
  args: {
21
- rows: buildConsoleListRows(consoleListItemsFixture, {}),
21
+ rows: buildConsoleListRows(consoleListItemsFixture, {}, []),
22
22
  storyColors: consoleStoryColorsFixture,
23
23
  activeItemId: null,
24
24
  isLoading: false,
@@ -6,7 +6,7 @@ import {
6
6
  } from '../../testing/fixtures';
7
7
  import { ConsoleItemList } from './ConsoleItemList';
8
8
 
9
- const rows = buildConsoleListRows(consoleListItemsFixture, {});
9
+ const rows = buildConsoleListRows(consoleListItemsFixture, {}, []);
10
10
  const now = Date.parse('2026-06-19T12:00:00.000Z');
11
11
 
12
12
  describe('ConsoleItemList', () => {
@@ -225,6 +225,28 @@ describe('useConsoleTabData', () => {
225
225
  ).toBe(true);
226
226
  });
227
227
 
228
+ it('parses storyOrder from the snapshot payload', async () => {
229
+ const fetchMock = jest.fn(async (url: string) => ({
230
+ ok: true,
231
+ status: 200,
232
+ json: async () => ({
233
+ pjcode: 'acme',
234
+ generatedAt: '2026-06-19T00:00:00.000Z',
235
+ statusOptions: [],
236
+ storyColors: {},
237
+ storyOrder: url.includes('/prs/') ? ['Alpha', 'Beta'] : [],
238
+ items: [],
239
+ }),
240
+ }));
241
+ global.fetch = fetchMock as unknown as typeof fetch;
242
+
243
+ const { result } = renderHook(() => useConsoleTabData('acme'));
244
+ await waitFor(() => {
245
+ expect(result.current.isLoading).toBe(false);
246
+ });
247
+ expect(result.current.snapshots.prs?.storyOrder).toEqual(['Alpha', 'Beta']);
248
+ });
249
+
228
250
  it('reports an error and fetches nothing when no pjcode is in the URL', async () => {
229
251
  const fetchMock = jest.fn();
230
252
  global.fetch = fetchMock as unknown as typeof fetch;
@@ -17,6 +17,7 @@ export type ConsoleTabSnapshot = {
17
17
  stories: ConsoleStoryEntry[];
18
18
  defaultNameWithOwner: string | null;
19
19
  fromCache: boolean;
20
+ storyOrder: string[];
20
21
  };
21
22
 
22
23
  export type ConsoleTabDataState = {
@@ -81,6 +82,7 @@ const parseSnapshotData = (
81
82
  isRecord(payload) && typeof payload.defaultNameWithOwner === 'string'
82
83
  ? payload.defaultNameWithOwner
83
84
  : null,
85
+ storyOrder: isRecord(payload) ? parseStringArray(payload.storyOrder) : [],
84
86
  });
85
87
 
86
88
  const emptySnapshots = (): Record<
@@ -69,14 +69,58 @@ describe('resolveItemStory', () => {
69
69
  });
70
70
 
71
71
  describe('buildConsoleListRows', () => {
72
- it('inserts a group header whenever the story changes and keeps array order', () => {
72
+ it('sorts items by storyOrder before grouping', () => {
73
+ const items = [
74
+ item({ number: 1, story: 'Beta' }),
75
+ item({ number: 2, story: 'Alpha' }),
76
+ item({ number: 3, story: 'Beta' }),
77
+ ];
78
+ const rows = buildConsoleListRows(items, {}, ['Alpha', 'Beta']);
79
+ expect(rows.map((row) => row.kind)).toEqual([
80
+ 'group-header',
81
+ 'item',
82
+ 'group-header',
83
+ 'item',
84
+ 'item',
85
+ ]);
86
+ const firstHeader = rows[0];
87
+ expect(firstHeader.kind === 'group-header' && firstHeader.story).toBe(
88
+ 'Alpha',
89
+ );
90
+ const secondHeader = rows[2];
91
+ expect(secondHeader.kind === 'group-header' && secondHeader.story).toBe(
92
+ 'Beta',
93
+ );
94
+ expect(secondHeader.kind === 'group-header' && secondHeader.count).toBe(2);
95
+ });
96
+
97
+ it('sorts by overlay-resolved story when storyOrder is provided', () => {
98
+ const overlay: ConsoleOverlay = {
99
+ PVTI_1: {
100
+ ts: 1,
101
+ mode: 'triage',
102
+ story: { name: 'Alpha', color: 'BLUE' },
103
+ },
104
+ };
105
+ const items = [
106
+ item({ number: 1, story: 'Beta' }),
107
+ item({ number: 2, story: 'Beta' }),
108
+ ];
109
+ const rows = buildConsoleListRows(items, overlay, ['Alpha', 'Beta']);
110
+ const firstHeader = rows[0];
111
+ expect(firstHeader.kind === 'group-header' && firstHeader.story).toBe(
112
+ 'Alpha',
113
+ );
114
+ });
115
+
116
+ it('keeps original relative order when storyOrder is empty', () => {
73
117
  const items = [
74
118
  item({ number: 1, story: 'Alpha' }),
75
119
  item({ number: 2, story: 'Alpha' }),
76
120
  item({ number: 3, story: 'Beta' }),
77
121
  item({ number: 4, story: 'Alpha' }),
78
122
  ];
79
- const rows = buildConsoleListRows(items, {});
123
+ const rows = buildConsoleListRows(items, {}, []);
80
124
  expect(rows.map((row) => row.kind)).toEqual([
81
125
  'group-header',
82
126
  'item',
@@ -91,6 +135,6 @@ describe('buildConsoleListRows', () => {
91
135
  });
92
136
 
93
137
  it('returns no rows for an empty list', () => {
94
- expect(buildConsoleListRows([], {})).toEqual([]);
138
+ expect(buildConsoleListRows([], {}, [])).toEqual([]);
95
139
  });
96
140
  });
@@ -51,19 +51,34 @@ export type ConsoleItemSummary = {
51
51
 
52
52
  export type ConsoleListRow = ConsoleListGroupRow | ConsoleItemSummary;
53
53
 
54
+ const UNKNOWN_STORY_SORT_INDEX = 999999;
55
+
54
56
  export const buildConsoleListRows = (
55
57
  items: ConsoleListItem[],
56
58
  overlay: ConsoleOverlay,
59
+ storyOrder: string[],
57
60
  ): ConsoleListRow[] => {
61
+ const indexByStory = new Map(storyOrder.map((name, index) => [name, index]));
62
+ const sorted =
63
+ storyOrder.length === 0
64
+ ? items
65
+ : [...items].sort((a, b) => {
66
+ const storyA = resolveItemStory(a, overlay);
67
+ const storyB = resolveItemStory(b, overlay);
68
+ const indexA = indexByStory.get(storyA) ?? UNKNOWN_STORY_SORT_INDEX;
69
+ const indexB = indexByStory.get(storyB) ?? UNKNOWN_STORY_SORT_INDEX;
70
+ return indexA - indexB;
71
+ });
72
+
58
73
  const storyCounts = new Map<string, number>();
59
- for (const item of items) {
74
+ for (const item of sorted) {
60
75
  const story = resolveItemStory(item, overlay);
61
76
  storyCounts.set(story, (storyCounts.get(story) ?? 0) + 1);
62
77
  }
63
78
 
64
79
  const rows: ConsoleListRow[] = [];
65
80
  let previousStory: string | null = null;
66
- for (const item of items) {
81
+ for (const item of sorted) {
67
82
  const story = resolveItemStory(item, overlay);
68
83
  if (story !== previousStory) {
69
84
  rows.push({
@@ -117,9 +117,11 @@ export const ConsolePage = () => {
117
117
  [pendingItems],
118
118
  );
119
119
 
120
+ const storyOrder = activeSnapshot?.storyOrder ?? [];
121
+
120
122
  const rows = useMemo(
121
- () => buildConsoleListRows(pendingItems, overlayState.overlay),
122
- [pendingItems, overlayState.overlay],
123
+ () => buildConsoleListRows(pendingItems, overlayState.overlay, storyOrder),
124
+ [pendingItems, overlayState.overlay, storyOrder],
123
125
  );
124
126
 
125
127
  const storyColors = activeSnapshot?.storyColors ?? {};