github-issue-tower-defence-management 1.197.0 → 1.197.1

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-CYwLv9pf.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-lT5OKx7e.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-CLHvrafO.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.197.0",
3
+ "version": "1.197.1",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -111,12 +111,12 @@ describe('buildConsoleDataResponse', () => {
111
111
  expect(response.statusCode).toBe(404);
112
112
  });
113
113
 
114
- it('serves a list file and applies the done exclusion', () => {
114
+ it('serves a list file without filtering items that have done records', () => {
115
115
  writeJson('acme/prs/list.json', {
116
116
  pjcode: 'acme',
117
117
  items: [
118
118
  { projectItemId: 'PVTI_1', title: 'keep' },
119
- { projectItemId: 'PVTI_2', title: 'drop' },
119
+ { projectItemId: 'PVTI_2', title: 'also keep despite done record' },
120
120
  ],
121
121
  });
122
122
  recordDoneProjectItemId(baseDir, 'acme', 'prs', 'PVTI_2');
@@ -129,16 +129,19 @@ describe('buildConsoleDataResponse', () => {
129
129
  const parsed: unknown = JSON.parse(response.body);
130
130
  expect(parsed).toEqual({
131
131
  pjcode: 'acme',
132
- items: [{ projectItemId: 'PVTI_1', title: 'keep' }],
132
+ items: [
133
+ { projectItemId: 'PVTI_1', title: 'keep' },
134
+ { projectItemId: 'PVTI_2', title: 'also keep despite done record' },
135
+ ],
133
136
  });
134
137
  });
135
138
 
136
- it('applies the done exclusion to the workflow-blocker list', () => {
139
+ it('serves the workflow-blocker list without filtering items that have done records', () => {
137
140
  writeJson('acme/workflow-blocker/list.json', {
138
141
  pjcode: 'acme',
139
142
  items: [
140
143
  { projectItemId: 'PVTI_1', title: 'keep' },
141
- { projectItemId: 'PVTI_2', title: 'processed blocker' },
144
+ { projectItemId: 'PVTI_2', title: 'also keep despite done record' },
142
145
  ],
143
146
  });
144
147
  recordDoneProjectItemId(baseDir, 'acme', 'workflow-blocker', 'PVTI_2');
@@ -151,7 +154,10 @@ describe('buildConsoleDataResponse', () => {
151
154
  const parsed: unknown = JSON.parse(response.body);
152
155
  expect(parsed).toEqual({
153
156
  pjcode: 'acme',
154
- items: [{ projectItemId: 'PVTI_1', title: 'keep' }],
157
+ items: [
158
+ { projectItemId: 'PVTI_1', title: 'keep' },
159
+ { projectItemId: 'PVTI_2', title: 'also keep despite done record' },
160
+ ],
155
161
  });
156
162
  });
157
163
 
@@ -1,6 +1,5 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
- import { readDoneProjectItemIds } from './consoleDoneStore';
4
3
  import { CONSOLE_LIST_TAB_NAMES } from './consoleTabNames';
5
4
 
6
5
  export { CONSOLE_LIST_TAB_NAMES };
@@ -55,9 +54,6 @@ export const parseConsoleDataRoute = (
55
54
  return null;
56
55
  };
57
56
 
58
- const isRecord = (value: unknown): value is Record<string, unknown> =>
59
- value !== null && typeof value === 'object' && !Array.isArray(value);
60
-
61
57
  type ReadJsonResult = { found: false } | { found: true; data: unknown };
62
58
 
63
59
  const readJsonFile = (filePath: string): ReadJsonResult => {
@@ -75,26 +71,6 @@ const readJsonFile = (filePath: string): ReadJsonResult => {
75
71
  return { found: true, data };
76
72
  };
77
73
 
78
- const isExcludedItem = (item: unknown, doneSet: Set<string>): boolean => {
79
- if (!isRecord(item)) {
80
- return false;
81
- }
82
- const projectItemId = item.projectItemId;
83
- return typeof projectItemId === 'string' && doneSet.has(projectItemId);
84
- };
85
-
86
- const applyDoneExclusion = (
87
- listData: unknown,
88
- doneProjectItemIds: string[],
89
- ): unknown => {
90
- if (!isRecord(listData) || !Array.isArray(listData.items)) {
91
- return listData;
92
- }
93
- const doneSet = new Set(doneProjectItemIds);
94
- const items = listData.items.filter((item) => !isExcludedItem(item, doneSet));
95
- return { ...listData, items };
96
- };
97
-
98
74
  export type ConsoleDataResponse = {
99
75
  statusCode: number;
100
76
  contentType: string;
@@ -116,13 +92,7 @@ export const buildConsoleDataResponse = (
116
92
  if (!listResult.found) {
117
93
  return notFoundJson();
118
94
  }
119
- const doneProjectItemIds = readDoneProjectItemIds(
120
- consoleDataOutputDir,
121
- route.pjcode,
122
- route.tab,
123
- );
124
- const filtered = applyDoneExclusion(listResult.data, doneProjectItemIds);
125
- return okJson(filtered);
95
+ return okJson(listResult.data);
126
96
  }
127
97
  if (route.kind === 'detail') {
128
98
  const filePath = path.join(
@@ -81,33 +81,33 @@ describe('counts driven to zero do not revive on tab switch', () => {
81
81
  const overlay: ConsoleOverlay = {
82
82
  PVTI_1: { ts: 500, mode: 'prs', done: true },
83
83
  };
84
- expect(countPendingItems([item(1)], overlay)).toBe(0);
84
+ expect(countPendingItems([item(1)], overlay, 'prs')).toBe(0);
85
85
  });
86
86
 
87
- it('keeps a done item subtracted from every tab regardless of its mode', () => {
87
+ it('does not subtract a done item from a tab other than the one it was processed in', () => {
88
88
  const overlay: ConsoleOverlay = {
89
89
  PVTI_1: { ts: 100, mode: 'triage', done: true },
90
90
  };
91
- expect(countPendingItems([item(1)], overlay)).toBe(0);
91
+ expect(countPendingItems([item(1)], overlay, 'prs')).toBe(1);
92
92
  });
93
93
 
94
94
  it('does not revive a done item even when an entry was processed before the snapshot it still appears in', () => {
95
95
  const overlay: ConsoleOverlay = {
96
96
  PVTI_1: { ts: 100, mode: 'prs', done: true },
97
97
  };
98
- expect(countPendingItems([item(1)], overlay)).toBe(0);
98
+ expect(countPendingItems([item(1)], overlay, 'prs')).toBe(0);
99
99
  });
100
100
 
101
- it('does not revive a done item when it appears in a tab other than the one it was processed in', () => {
101
+ it('shows a done item in a tab other than the one it was processed in', () => {
102
102
  const overlay: ConsoleOverlay = {
103
103
  PVTI_1: { ts: 100, mode: 'prs', done: true },
104
104
  };
105
- expect(countPendingItems([item(1)], overlay)).toBe(0);
106
- expect(filterPendingItems([item(1)], overlay)).toEqual([]);
105
+ expect(countPendingItems([item(1)], overlay, 'triage')).toBe(1);
106
+ expect(filterPendingItems([item(1)], overlay, 'triage')).toEqual([item(1)]);
107
107
  });
108
108
 
109
109
  it('counts an item that has no done entry', () => {
110
- expect(countPendingItems([item(1)], {})).toBe(1);
110
+ expect(countPendingItems([item(1)], {}, 'prs')).toBe(1);
111
111
  });
112
112
  });
113
113
 
@@ -116,7 +116,7 @@ describe('filterPendingItems', () => {
116
116
  const overlay: ConsoleOverlay = {
117
117
  PVTI_1: { ts: 500, mode: 'prs', done: true },
118
118
  };
119
- const result = filterPendingItems([item(1), item(2)], overlay);
119
+ const result = filterPendingItems([item(1), item(2)], overlay, 'prs');
120
120
  expect(result.map((entry) => entry.number)).toEqual([2]);
121
121
  });
122
122
 
@@ -125,8 +125,8 @@ describe('filterPendingItems', () => {
125
125
  PVTI_1: { ts: 500, mode: 'prs', done: true },
126
126
  };
127
127
  const items = [item(1), item(2)];
128
- expect(countPendingItems(items, overlay)).toBe(
129
- filterPendingItems(items, overlay).length,
128
+ expect(countPendingItems(items, overlay, 'prs')).toBe(
129
+ filterPendingItems(items, overlay, 'prs').length,
130
130
  );
131
131
  });
132
132
  });
@@ -136,14 +136,20 @@ describe('workflow-blocker items are filtered by the done overlay like every oth
136
136
  const overlay: ConsoleOverlay = {
137
137
  PVTI_1: { ts: 100, mode: 'workflow-blocker', done: true },
138
138
  };
139
- expect(countPendingItems([item(1), item(2)], overlay)).toBe(1);
139
+ expect(
140
+ countPendingItems([item(1), item(2)], overlay, 'workflow-blocker'),
141
+ ).toBe(1);
140
142
  });
141
143
 
142
144
  it('removes a processed workflow-blocker item from the list', () => {
143
145
  const overlay: ConsoleOverlay = {
144
146
  PVTI_1: { ts: 100, mode: 'workflow-blocker', done: true },
145
147
  };
146
- const result = filterPendingItems([item(1), item(2)], overlay);
148
+ const result = filterPendingItems(
149
+ [item(1), item(2)],
150
+ overlay,
151
+ 'workflow-blocker',
152
+ );
147
153
  expect(result.map((entry) => entry.number)).toEqual([2]);
148
154
  });
149
155
 
@@ -152,8 +158,12 @@ describe('workflow-blocker items are filtered by the done overlay like every oth
152
158
  PVTI_1: { ts: 100, mode: 'workflow-blocker', done: true },
153
159
  PVTI_2: { ts: 100, mode: 'workflow-blocker', done: true },
154
160
  };
155
- expect(countPendingItems([item(1), item(2)], overlay)).toBe(0);
156
- expect(filterPendingItems([item(1), item(2)], overlay)).toEqual([]);
161
+ expect(
162
+ countPendingItems([item(1), item(2)], overlay, 'workflow-blocker'),
163
+ ).toBe(0);
164
+ expect(
165
+ filterPendingItems([item(1), item(2)], overlay, 'workflow-blocker'),
166
+ ).toEqual([]);
157
167
  });
158
168
  });
159
169
 
@@ -171,8 +181,10 @@ describe('overlayEntriesActedSinceSnapshot', () => {
171
181
  '2026-08-04T00:26:06Z',
172
182
  );
173
183
  expect(acted).toEqual({});
174
- expect(countPendingItems([item(1)], acted)).toBe(1);
175
- expect(filterPendingItems([item(1)], acted)).toEqual([item(1)]);
184
+ expect(countPendingItems([item(1)], acted, 'todo-by-human')).toBe(1);
185
+ expect(filterPendingItems([item(1)], acted, 'todo-by-human')).toEqual([
186
+ item(1),
187
+ ]);
176
188
  });
177
189
 
178
190
  it('keeps an entry written after the snapshot was generated so the item stays hidden', () => {
@@ -187,7 +199,7 @@ describe('overlayEntriesActedSinceSnapshot', () => {
187
199
  overlay,
188
200
  '2026-08-04T00:26:06Z',
189
201
  );
190
- expect(countPendingItems([item(1)], acted)).toBe(0);
202
+ expect(countPendingItems([item(1)], acted, 'todo-by-human')).toBe(0);
191
203
  });
192
204
 
193
205
  it('keeps an entry written at the exact snapshot generation time', () => {
@@ -16,19 +16,28 @@ export const isOverlayEntryActed = (
16
16
  entry: ConsoleOverlayEntry | undefined,
17
17
  ): boolean => entry !== undefined && entry.done === true;
18
18
 
19
+ const isActedInTab = (
20
+ entry: ConsoleOverlayEntry | undefined,
21
+ currentTab: ConsoleTabName,
22
+ ): boolean =>
23
+ entry !== undefined && entry.done === true && entry.mode === currentTab;
24
+
19
25
  export const countPendingItems = (
20
26
  items: ConsoleListItem[],
21
27
  overlay: ConsoleOverlay,
28
+ currentTab: ConsoleTabName,
22
29
  ): number =>
23
- items.filter((item) => !isOverlayEntryActed(overlay[overlayKeyForItem(item)]))
24
- .length;
30
+ items.filter(
31
+ (item) => !isActedInTab(overlay[overlayKeyForItem(item)], currentTab),
32
+ ).length;
25
33
 
26
34
  export const filterPendingItems = (
27
35
  items: ConsoleListItem[],
28
36
  overlay: ConsoleOverlay,
37
+ currentTab: ConsoleTabName,
29
38
  ): ConsoleListItem[] =>
30
39
  items.filter(
31
- (item) => !isOverlayEntryActed(overlay[overlayKeyForItem(item)]),
40
+ (item) => !isActedInTab(overlay[overlayKeyForItem(item)], currentTab),
32
41
  );
33
42
 
34
43
  export const overlayEntriesActedSinceSnapshot = (
@@ -107,6 +107,7 @@ export const ConsolePage = () => {
107
107
  overlayState.overlay,
108
108
  snapshot.generatedAt,
109
109
  ),
110
+ tab.name,
110
111
  );
111
112
  }
112
113
  }
@@ -151,8 +152,9 @@ export const ConsolePage = () => {
151
152
  overlayState.overlay,
152
153
  activeSnapshot.generatedAt,
153
154
  ),
155
+ activeTab,
154
156
  );
155
- }, [activeSnapshot, overlayState.overlay]);
157
+ }, [activeSnapshot, overlayState.overlay, activeTab]);
156
158
 
157
159
  const orderedPendingKeys = useMemo(
158
160
  () => pendingItems.map((item) => overlayKeyForItem(item)),