github-issue-tower-defence-management 1.180.1 → 1.181.0
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.
- package/CHANGELOG.md +13 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/index-BUFcVUVD.js +86 -0
- package/bin/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/bin/adapter/repositories/GoogleSpreadsheetRepository.js +41 -12
- package/bin/adapter/repositories/GoogleSpreadsheetRepository.js.map +1 -1
- package/bin/domain/usecases/extractFencedJsonBlocks.js +18 -0
- package/bin/domain/usecases/extractFencedJsonBlocks.js.map +1 -0
- package/bin/domain/usecases/extractNextStepAgent.js +14 -23
- package/bin/domain/usecases/extractNextStepAgent.js.map +1 -1
- package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js +10 -17
- package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +1 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOkAndAwaitingWorkspaceActions.stories.tsx +25 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOkAndAwaitingWorkspaceActions.test.tsx +50 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOkAndAwaitingWorkspaceActions.tsx +41 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.stories.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.tsx +5 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts +70 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +29 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.test.ts +14 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.ts +5 -1
- package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +7 -0
- package/src/adapter/entry-points/console/ui-dist/assets/index-BUFcVUVD.js +86 -0
- package/src/adapter/entry-points/console/ui-dist/index.html +1 -1
- package/src/adapter/repositories/GoogleSpreadsheetRepository.test.ts +47 -0
- package/src/adapter/repositories/GoogleSpreadsheetRepository.ts +76 -36
- package/src/domain/usecases/extractFencedJsonBlocks.test.ts +40 -0
- package/src/domain/usecases/extractFencedJsonBlocks.ts +21 -0
- package/src/domain/usecases/extractNextStepAgent.test.ts +19 -0
- package/src/domain/usecases/extractNextStepAgent.ts +15 -27
- package/src/domain/usecases/isPullRequestDeclaredUnnecessary.test.ts +33 -0
- package/src/domain/usecases/isPullRequestDeclaredUnnecessary.ts +13 -21
- package/types/adapter/repositories/GoogleSpreadsheetRepository.d.ts +3 -1
- package/types/adapter/repositories/GoogleSpreadsheetRepository.d.ts.map +1 -1
- package/types/domain/usecases/extractFencedJsonBlocks.d.ts +2 -0
- package/types/domain/usecases/extractFencedJsonBlocks.d.ts.map +1 -0
- package/types/domain/usecases/extractNextStepAgent.d.ts.map +1 -1
- package/types/domain/usecases/isPullRequestDeclaredUnnecessary.d.ts.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-I_2Yu8fM.js +0 -86
- package/src/adapter/entry-points/console/ui-dist/assets/index-I_2Yu8fM.js +0 -86
|
@@ -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-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BUFcVUVD.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-DeZcPNCr.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -9,9 +9,37 @@ const dotenv_1 = __importDefault(require("dotenv"));
|
|
|
9
9
|
dotenv_1.default.config();
|
|
10
10
|
class GoogleSpreadsheetRepository {
|
|
11
11
|
constructor(localStorageRepository, serviceAccountKey = process.env.GOOGLE_SERVICE_ACCOUNT_KEY ||
|
|
12
|
-
'dummy', sheetsClientFactory) {
|
|
12
|
+
'dummy', sheetsClientFactory, sleepFn = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) {
|
|
13
13
|
this.localStorageRepository = localStorageRepository;
|
|
14
14
|
this.keyFile = './tmp/service-account-key.json';
|
|
15
|
+
this.callWithRetry = async (fn) => {
|
|
16
|
+
const backoffMs = [1000, 2000, 4000];
|
|
17
|
+
let lastError;
|
|
18
|
+
for (let attempt = 0; attempt <= backoffMs.length; attempt++) {
|
|
19
|
+
try {
|
|
20
|
+
return await fn();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
lastError = error;
|
|
24
|
+
const isQuotaError = (() => {
|
|
25
|
+
if (!(error instanceof Error))
|
|
26
|
+
return false;
|
|
27
|
+
if (error.message.includes('Quota exceeded'))
|
|
28
|
+
return true;
|
|
29
|
+
if ('status' in error && error.status === 429)
|
|
30
|
+
return true;
|
|
31
|
+
if ('code' in error && error.code === 429)
|
|
32
|
+
return true;
|
|
33
|
+
return false;
|
|
34
|
+
})();
|
|
35
|
+
if (!isQuotaError || attempt === backoffMs.length) {
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
await this.sleepFn(backoffMs[attempt]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
throw lastError;
|
|
42
|
+
};
|
|
15
43
|
this.getSpreadsheetId = (spreadsheetUrl) => {
|
|
16
44
|
const url = new URL(spreadsheetUrl);
|
|
17
45
|
return url.pathname.split('/')[3];
|
|
@@ -19,9 +47,9 @@ class GoogleSpreadsheetRepository {
|
|
|
19
47
|
this.getSheet = async (spreadsheetUrl, sheetName) => {
|
|
20
48
|
const sheets = this.sheetsClient;
|
|
21
49
|
const spreadsheetId = this.getSpreadsheetId(spreadsheetUrl);
|
|
22
|
-
const responseSheet = await sheets.spreadsheets.get({
|
|
50
|
+
const responseSheet = await this.callWithRetry(() => sheets.spreadsheets.get({
|
|
23
51
|
spreadsheetId,
|
|
24
|
-
});
|
|
52
|
+
}));
|
|
25
53
|
if (responseSheet.status !== 200) {
|
|
26
54
|
throw new Error(`Failed to get sheet: ${responseSheet.status}. ${JSON.stringify(responseSheet.data)}`);
|
|
27
55
|
}
|
|
@@ -29,10 +57,10 @@ class GoogleSpreadsheetRepository {
|
|
|
29
57
|
if (!sheet) {
|
|
30
58
|
return null;
|
|
31
59
|
}
|
|
32
|
-
const response = await sheets.spreadsheets.values.get({
|
|
60
|
+
const response = await this.callWithRetry(() => sheets.spreadsheets.values.get({
|
|
33
61
|
spreadsheetId,
|
|
34
62
|
range: sheetName,
|
|
35
|
-
});
|
|
63
|
+
}));
|
|
36
64
|
if (response.status !== 200) {
|
|
37
65
|
throw new Error(`Failed to get sheet: ${response.status}. ${JSON.stringify(response.data)}`);
|
|
38
66
|
}
|
|
@@ -45,14 +73,14 @@ class GoogleSpreadsheetRepository {
|
|
|
45
73
|
const sheets = this.sheetsClient;
|
|
46
74
|
const spreadsheetId = this.getSpreadsheetId(spreadsheetUrl);
|
|
47
75
|
await this.createNewSheetIfNotExists(spreadsheetUrl, sheetName);
|
|
48
|
-
const response = await sheets.spreadsheets.values.update({
|
|
76
|
+
const response = await this.callWithRetry(() => sheets.spreadsheets.values.update({
|
|
49
77
|
spreadsheetId,
|
|
50
78
|
range: `${sheetName}!${String.fromCharCode(65 + column)}${row + 1}`,
|
|
51
79
|
valueInputOption: 'RAW',
|
|
52
80
|
requestBody: {
|
|
53
81
|
values: [[value]],
|
|
54
82
|
},
|
|
55
|
-
});
|
|
83
|
+
}));
|
|
56
84
|
if (response.status !== 200) {
|
|
57
85
|
throw new Error(`Failed to update cell: ${response.status}. ${JSON.stringify(response.data)}`);
|
|
58
86
|
}
|
|
@@ -60,7 +88,7 @@ class GoogleSpreadsheetRepository {
|
|
|
60
88
|
this.sheetExists = async (spreadsheetUrl, sheetName) => {
|
|
61
89
|
const sheets = this.sheetsClient;
|
|
62
90
|
const spreadsheetId = this.getSpreadsheetId(spreadsheetUrl);
|
|
63
|
-
const response = await sheets.spreadsheets.get({ spreadsheetId });
|
|
91
|
+
const response = await this.callWithRetry(() => sheets.spreadsheets.get({ spreadsheetId }));
|
|
64
92
|
if (response.status !== 200) {
|
|
65
93
|
throw new Error(`Failed to get sheet: ${response.status}. ${JSON.stringify(response.data)}`);
|
|
66
94
|
}
|
|
@@ -72,7 +100,7 @@ class GoogleSpreadsheetRepository {
|
|
|
72
100
|
if (await this.sheetExists(spreadsheetUrl, sheetName)) {
|
|
73
101
|
return;
|
|
74
102
|
}
|
|
75
|
-
const response = await sheets.spreadsheets.batchUpdate({
|
|
103
|
+
const response = await this.callWithRetry(() => sheets.spreadsheets.batchUpdate({
|
|
76
104
|
spreadsheetId,
|
|
77
105
|
requestBody: {
|
|
78
106
|
requests: [
|
|
@@ -85,7 +113,7 @@ class GoogleSpreadsheetRepository {
|
|
|
85
113
|
},
|
|
86
114
|
],
|
|
87
115
|
},
|
|
88
|
-
});
|
|
116
|
+
}));
|
|
89
117
|
if (response.status !== 200) {
|
|
90
118
|
throw new Error(`Failed to create sheet: ${response.status}. ${JSON.stringify(response.data)}`);
|
|
91
119
|
}
|
|
@@ -96,18 +124,19 @@ class GoogleSpreadsheetRepository {
|
|
|
96
124
|
await this.createNewSheetIfNotExists(spreadsheetUrl, sheetName);
|
|
97
125
|
const sheet = await this.getSheet(spreadsheetUrl, sheetName);
|
|
98
126
|
const range = `${sheetName}!A${sheet ? sheet.length + 1 : 1}:A`;
|
|
99
|
-
const response = await sheets.spreadsheets.values.append({
|
|
127
|
+
const response = await this.callWithRetry(() => sheets.spreadsheets.values.append({
|
|
100
128
|
spreadsheetId,
|
|
101
129
|
range: range,
|
|
102
130
|
valueInputOption: 'RAW',
|
|
103
131
|
requestBody: {
|
|
104
132
|
values,
|
|
105
133
|
},
|
|
106
|
-
});
|
|
134
|
+
}));
|
|
107
135
|
if (response.status !== 200) {
|
|
108
136
|
throw new Error(`Failed to append values: ${response.status}. ${JSON.stringify(response.data)}`);
|
|
109
137
|
}
|
|
110
138
|
};
|
|
139
|
+
this.sleepFn = sleepFn;
|
|
111
140
|
this.localStorageRepository.write(this.keyFile, serviceAccountKey);
|
|
112
141
|
this.sheetsClient = sheetsClientFactory
|
|
113
142
|
? sheetsClientFactory()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GoogleSpreadsheetRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/GoogleSpreadsheetRepository.ts"],"names":[],"mappings":";;;;;;AACA,2CAAoC;AAEpC,oDAA4B;AAC5B,gBAAM,CAAC,MAAM,EAAE,CAAC;AAuChB,MAAa,2BAA2B;
|
|
1
|
+
{"version":3,"file":"GoogleSpreadsheetRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/GoogleSpreadsheetRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractFencedJsonBlocks = void 0;
|
|
4
|
+
const normalizeReportBody_1 = require("./normalizeReportBody");
|
|
5
|
+
const extractFencedJsonBlocks = (body, context) => {
|
|
6
|
+
const blocks = [];
|
|
7
|
+
for (const match of (0, normalizeReportBody_1.normalizeReportBody)(body).matchAll(/```json\n([\s\S]*?)\n```/g)) {
|
|
8
|
+
try {
|
|
9
|
+
blocks.push(JSON.parse(match[1]));
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
console.warn(`Invalid JSON in report body while checking ${context}:`, error);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return blocks;
|
|
16
|
+
};
|
|
17
|
+
exports.extractFencedJsonBlocks = extractFencedJsonBlocks;
|
|
18
|
+
//# sourceMappingURL=extractFencedJsonBlocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractFencedJsonBlocks.js","sourceRoot":"","sources":["../../../src/domain/usecases/extractFencedJsonBlocks.ts"],"names":[],"mappings":";;;AAAA,+DAA4D;AAErD,MAAM,uBAAuB,GAAG,CACrC,IAAY,EACZ,OAAe,EACJ,EAAE;IACb,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,IAAA,yCAAmB,EAAC,IAAI,CAAC,CAAC,QAAQ,CACpD,2BAA2B,CAC5B,EAAE,CAAC;QACF,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CACV,8CAA8C,OAAO,GAAG,EACxD,KAAK,CACN,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAlBW,QAAA,uBAAuB,2BAkBlC"}
|
|
@@ -1,31 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.extractNextStepAgent = void 0;
|
|
4
|
-
const
|
|
4
|
+
const extractFencedJsonBlocks_1 = require("./extractFencedJsonBlocks");
|
|
5
5
|
const extractNextStepAgent = (body) => {
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
for (const block of (0, extractFencedJsonBlocks_1.extractFencedJsonBlocks)(body, 'nextStepAgent')) {
|
|
7
|
+
if (typeof block !== 'object' || block === null) {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
if (!('nextStepAgent' in block)) {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
const value = Reflect.get(block, 'nextStepAgent');
|
|
14
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
return value.trim();
|
|
9
18
|
}
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
reportJson = JSON.parse(reportMatch[1]);
|
|
13
|
-
}
|
|
14
|
-
catch (error) {
|
|
15
|
-
console.warn('Invalid JSON in report body while checking nextStepAgent:', error);
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
if (typeof reportJson !== 'object' || reportJson === null) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
if (!('nextStepAgent' in reportJson)) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
const value = Reflect.get(reportJson, 'nextStepAgent');
|
|
25
|
-
if (typeof value !== 'string' || value.trim() === '') {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
return value.trim();
|
|
19
|
+
return null;
|
|
29
20
|
};
|
|
30
21
|
exports.extractNextStepAgent = extractNextStepAgent;
|
|
31
22
|
//# sourceMappingURL=extractNextStepAgent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractNextStepAgent.js","sourceRoot":"","sources":["../../../src/domain/usecases/extractNextStepAgent.ts"],"names":[],"mappings":";;;AAAA
|
|
1
|
+
{"version":3,"file":"extractNextStepAgent.js","sourceRoot":"","sources":["../../../src/domain/usecases/extractNextStepAgent.ts"],"names":[],"mappings":";;;AAAA,uEAAoE;AAE7D,MAAM,oBAAoB,GAAG,CAAC,IAAY,EAAiB,EAAE;IAClE,KAAK,MAAM,KAAK,IAAI,IAAA,iDAAuB,EAAC,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC;QACnE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAChD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,CAAC,eAAe,IAAI,KAAK,CAAC,EAAE,CAAC;YAChC,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QAClD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YACrD,SAAS;QACX,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;IACtB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAfW,QAAA,oBAAoB,wBAe/B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isPullRequestDeclaredUnnecessary = void 0;
|
|
4
|
+
const extractFencedJsonBlocks_1 = require("./extractFencedJsonBlocks");
|
|
4
5
|
const isAgentReportBody_1 = require("./isAgentReportBody");
|
|
5
|
-
const normalizeReportBody_1 = require("./normalizeReportBody");
|
|
6
6
|
const isPullRequestDeclaredUnnecessary = (comments, isTrustedAuthor) => {
|
|
7
7
|
const lastComment = comments[comments.length - 1];
|
|
8
8
|
if (!lastComment ||
|
|
@@ -10,23 +10,16 @@ const isPullRequestDeclaredUnnecessary = (comments, isTrustedAuthor) => {
|
|
|
10
10
|
!(0, isAgentReportBody_1.isAgentReportBody)(lastComment.content)) {
|
|
11
11
|
return false;
|
|
12
12
|
}
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
catch (error) {
|
|
22
|
-
console.warn('Invalid JSON in report body while checking pullRequestRequired:', error);
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
if (typeof reportJson !== 'object' || reportJson === null) {
|
|
26
|
-
return false;
|
|
13
|
+
for (const block of (0, extractFencedJsonBlocks_1.extractFencedJsonBlocks)(lastComment.content, 'pullRequestRequired')) {
|
|
14
|
+
if (typeof block !== 'object' || block === null) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const report = { ...block };
|
|
18
|
+
if (report.pullRequestRequired === false) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
27
21
|
}
|
|
28
|
-
|
|
29
|
-
return report.pullRequestRequired === false;
|
|
22
|
+
return false;
|
|
30
23
|
};
|
|
31
24
|
exports.isPullRequestDeclaredUnnecessary = isPullRequestDeclaredUnnecessary;
|
|
32
25
|
//# sourceMappingURL=isPullRequestDeclaredUnnecessary.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"isPullRequestDeclaredUnnecessary.js","sourceRoot":"","sources":["../../../src/domain/usecases/isPullRequestDeclaredUnnecessary.ts"],"names":[],"mappings":";;;AAAA,2DAAwD;
|
|
1
|
+
{"version":3,"file":"isPullRequestDeclaredUnnecessary.js","sourceRoot":"","sources":["../../../src/domain/usecases/isPullRequestDeclaredUnnecessary.ts"],"names":[],"mappings":";;;AAAA,uEAAoE;AACpE,2DAAwD;AAEjD,MAAM,gCAAgC,GAAG,CAC9C,QAA+C,EAC/C,eAA4C,EACnC,EAAE;IACX,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClD,IACE,CAAC,WAAW;QACZ,CAAC,eAAe,CAAC,WAAW,CAAC,MAAM,CAAC;QACpC,CAAC,IAAA,qCAAiB,EAAC,WAAW,CAAC,OAAO,CAAC,EACvC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,IAAA,iDAAuB,EACzC,WAAW,CAAC,OAAO,EACnB,qBAAqB,CACtB,EAAE,CAAC;QACF,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAChD,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAA4B,EAAE,GAAG,KAAK,EAAE,CAAC;QACrD,IAAI,MAAM,CAAC,mBAAmB,KAAK,KAAK,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAzBW,QAAA,gCAAgC,oCAyB3C"}
|
package/package.json
CHANGED
|
@@ -112,7 +112,7 @@ test('renders the Workflow Blocker tab leftmost and shows its detail operations'
|
|
|
112
112
|
).click();
|
|
113
113
|
|
|
114
114
|
await expect(
|
|
115
|
-
page.locator('.console-op-button', { hasText:
|
|
115
|
+
page.locator('.console-op-button', { hasText: /^Awaiting Workspace$/ }),
|
|
116
116
|
).toBeVisible();
|
|
117
117
|
await expect(
|
|
118
118
|
page.locator('.console-op-button', { hasText: 'Close as not planned' }),
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { consoleStatusOptionsFixture } from '../../testing/fixtures';
|
|
3
|
+
import { ConsoleOkAndAwaitingWorkspaceActions } from './ConsoleOkAndAwaitingWorkspaceActions';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof ConsoleOkAndAwaitingWorkspaceActions> = {
|
|
6
|
+
title: 'Console/ConsoleOkAndAwaitingWorkspaceActions',
|
|
7
|
+
component: ConsoleOkAndAwaitingWorkspaceActions,
|
|
8
|
+
args: { onOkAndAwaitingWorkspace: () => {} },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof ConsoleOkAndAwaitingWorkspaceActions>;
|
|
14
|
+
|
|
15
|
+
export const WithAwaitingWorkspaceOption: Story = {
|
|
16
|
+
args: { statusOptions: consoleStatusOptionsFixture },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const WithoutAwaitingWorkspaceOption: Story = {
|
|
20
|
+
args: {
|
|
21
|
+
statusOptions: consoleStatusOptionsFixture.filter(
|
|
22
|
+
(o) => o.name.toLowerCase() !== 'awaiting workspace',
|
|
23
|
+
),
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
2
|
+
import { consoleStatusOptionsFixture } from '../../testing/fixtures';
|
|
3
|
+
import { ConsoleOkAndAwaitingWorkspaceActions } from './ConsoleOkAndAwaitingWorkspaceActions';
|
|
4
|
+
|
|
5
|
+
const [awaitingWorkspaceOption] = consoleStatusOptionsFixture.filter(
|
|
6
|
+
(o) => o.name.toLowerCase() === 'awaiting workspace',
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
const statusOptionsWithoutAwaitingWorkspace =
|
|
10
|
+
consoleStatusOptionsFixture.filter(
|
|
11
|
+
(o) => o.name.toLowerCase() !== 'awaiting workspace',
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
describe('ConsoleOkAndAwaitingWorkspaceActions', () => {
|
|
15
|
+
it('renders the button when the awaiting workspace option is present', () => {
|
|
16
|
+
const { getByRole } = render(
|
|
17
|
+
<ConsoleOkAndAwaitingWorkspaceActions
|
|
18
|
+
statusOptions={consoleStatusOptionsFixture}
|
|
19
|
+
onOkAndAwaitingWorkspace={() => {}}
|
|
20
|
+
/>,
|
|
21
|
+
);
|
|
22
|
+
expect(
|
|
23
|
+
getByRole('button', { name: 'ok & Awaiting Workspace' }),
|
|
24
|
+
).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns null when the awaiting workspace option is absent', () => {
|
|
28
|
+
const { container } = render(
|
|
29
|
+
<ConsoleOkAndAwaitingWorkspaceActions
|
|
30
|
+
statusOptions={statusOptionsWithoutAwaitingWorkspace}
|
|
31
|
+
onOkAndAwaitingWorkspace={() => {}}
|
|
32
|
+
/>,
|
|
33
|
+
);
|
|
34
|
+
expect(container.firstChild).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('calls the callback with the awaiting workspace option on click', () => {
|
|
38
|
+
const onOkAndAwaitingWorkspace = jest.fn();
|
|
39
|
+
const { getByRole } = render(
|
|
40
|
+
<ConsoleOkAndAwaitingWorkspaceActions
|
|
41
|
+
statusOptions={consoleStatusOptionsFixture}
|
|
42
|
+
onOkAndAwaitingWorkspace={onOkAndAwaitingWorkspace}
|
|
43
|
+
/>,
|
|
44
|
+
);
|
|
45
|
+
fireEvent.click(getByRole('button', { name: 'ok & Awaiting Workspace' }));
|
|
46
|
+
expect(onOkAndAwaitingWorkspace).toHaveBeenCalledWith(
|
|
47
|
+
awaitingWorkspaceOption,
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { colorFromEnum } from '../../logic/colors';
|
|
2
|
+
import type { ConsoleFieldOption } from '../../logic/types';
|
|
3
|
+
|
|
4
|
+
export type ConsoleOkAndAwaitingWorkspaceActionsProps = {
|
|
5
|
+
statusOptions: ConsoleFieldOption[];
|
|
6
|
+
onOkAndAwaitingWorkspace: (option: ConsoleFieldOption) => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const AWAITING_WORKSPACE_NAME = 'awaiting workspace';
|
|
10
|
+
|
|
11
|
+
export const ConsoleOkAndAwaitingWorkspaceActions = ({
|
|
12
|
+
statusOptions,
|
|
13
|
+
onOkAndAwaitingWorkspace,
|
|
14
|
+
}: ConsoleOkAndAwaitingWorkspaceActionsProps) => {
|
|
15
|
+
const option = statusOptions.find(
|
|
16
|
+
(o) => o.name.toLowerCase() === AWAITING_WORKSPACE_NAME,
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (option === undefined) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const palette = colorFromEnum(option.color);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="console-op-group">
|
|
27
|
+
<button
|
|
28
|
+
type="button"
|
|
29
|
+
className="console-op-button"
|
|
30
|
+
style={{
|
|
31
|
+
color: palette.fg,
|
|
32
|
+
borderColor: palette.border,
|
|
33
|
+
backgroundColor: palette.bg,
|
|
34
|
+
}}
|
|
35
|
+
onClick={() => onOkAndAwaitingWorkspace(option)}
|
|
36
|
+
>
|
|
37
|
+
ok & Awaiting Workspace
|
|
38
|
+
</button>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
} from '../../logic/types';
|
|
10
10
|
import { ConsoleCloseActions } from './ConsoleCloseActions';
|
|
11
11
|
import { ConsoleNextActionDateActions } from './ConsoleNextActionDateActions';
|
|
12
|
+
import { ConsoleOkAndAwaitingWorkspaceActions } from './ConsoleOkAndAwaitingWorkspaceActions';
|
|
12
13
|
import { ConsolePullRequestReviewActions } from './ConsolePullRequestReviewActions';
|
|
13
14
|
import { ConsoleStatusActions } from './ConsoleStatusActions';
|
|
14
15
|
import { ConsoleStoryActions } from './ConsoleStoryActions';
|
|
@@ -34,6 +35,10 @@ export const ConsoleOperationMenu = ({
|
|
|
34
35
|
const showStory = tab === 'triage';
|
|
35
36
|
return (
|
|
36
37
|
<div className="console-operation-bar">
|
|
38
|
+
<ConsoleOkAndAwaitingWorkspaceActions
|
|
39
|
+
statusOptions={statusOptions}
|
|
40
|
+
onOkAndAwaitingWorkspace={handlers.onOkAndAwaitingWorkspace}
|
|
41
|
+
/>
|
|
37
42
|
{hasPullRequest && (
|
|
38
43
|
<ConsolePullRequestReviewActions
|
|
39
44
|
onReview={handlers.onReview}
|
package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts
CHANGED
|
@@ -415,4 +415,74 @@ describe('useConsoleOperations', () => {
|
|
|
415
415
|
).rejects.toThrow('No project specified in the URL path.');
|
|
416
416
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
417
417
|
});
|
|
418
|
+
|
|
419
|
+
it('posts an ok comment then sets status to awaiting workspace in order', async () => {
|
|
420
|
+
let callCount = 0;
|
|
421
|
+
const calls: Array<[string, unknown]> = [];
|
|
422
|
+
global.fetch = jest.fn(async (url: unknown, opts: unknown) => {
|
|
423
|
+
calls.push([url as string, opts]);
|
|
424
|
+
callCount += 1;
|
|
425
|
+
if (callCount === 1) {
|
|
426
|
+
return {
|
|
427
|
+
ok: true,
|
|
428
|
+
status: 200,
|
|
429
|
+
json: async () => ({
|
|
430
|
+
comment: {
|
|
431
|
+
author: 'bot',
|
|
432
|
+
body: 'ok',
|
|
433
|
+
createdAt: '2026-01-01T00:00:00Z',
|
|
434
|
+
},
|
|
435
|
+
}),
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
return { ok: true, status: 200, json: async () => ({ ok: true }) };
|
|
439
|
+
}) as unknown as typeof fetch;
|
|
440
|
+
const { result } = setup();
|
|
441
|
+
const [option] = consoleStatusOptionsFixture.filter(
|
|
442
|
+
(o) => o.name.toLowerCase() === 'awaiting workspace',
|
|
443
|
+
);
|
|
444
|
+
await act(async () => {
|
|
445
|
+
await result.current.operations.okAndMoveToAwaitingWorkspace(
|
|
446
|
+
issueItem,
|
|
447
|
+
option,
|
|
448
|
+
);
|
|
449
|
+
});
|
|
450
|
+
expect(calls[0][0]).toBe('/api/comment');
|
|
451
|
+
expect(JSON.parse((calls[0][1] as { body: string }).body)).toMatchObject({
|
|
452
|
+
pjcode: 'acme',
|
|
453
|
+
url: issueItem.url,
|
|
454
|
+
body: 'ok',
|
|
455
|
+
});
|
|
456
|
+
expect(calls[1][0]).toBe('/api/triage');
|
|
457
|
+
expect(JSON.parse((calls[1][1] as { body: string }).body)).toMatchObject({
|
|
458
|
+
pjcode: 'acme',
|
|
459
|
+
action: 'set_status',
|
|
460
|
+
issueUrl: issueItem.url,
|
|
461
|
+
statusName: option.name,
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('does not call the status endpoint when the comment post fails', async () => {
|
|
466
|
+
let callCount = 0;
|
|
467
|
+
global.fetch = jest.fn(async () => {
|
|
468
|
+
callCount += 1;
|
|
469
|
+
if (callCount === 1) {
|
|
470
|
+
return { ok: false, status: 500, text: async () => 'error' };
|
|
471
|
+
}
|
|
472
|
+
return { ok: true, status: 200, json: async () => ({ ok: true }) };
|
|
473
|
+
}) as unknown as typeof fetch;
|
|
474
|
+
const { result } = setup();
|
|
475
|
+
const [option] = consoleStatusOptionsFixture.filter(
|
|
476
|
+
(o) => o.name.toLowerCase() === 'awaiting workspace',
|
|
477
|
+
);
|
|
478
|
+
await expect(
|
|
479
|
+
act(async () => {
|
|
480
|
+
await result.current.operations.okAndMoveToAwaitingWorkspace(
|
|
481
|
+
issueItem,
|
|
482
|
+
option,
|
|
483
|
+
);
|
|
484
|
+
}),
|
|
485
|
+
).rejects.toThrow();
|
|
486
|
+
expect(callCount).toBe(1);
|
|
487
|
+
});
|
|
418
488
|
});
|
package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts
CHANGED
|
@@ -61,6 +61,10 @@ export type ConsoleOperationsApi = {
|
|
|
61
61
|
item: ConsoleListItem,
|
|
62
62
|
action: ConsoleCloseAction,
|
|
63
63
|
) => Promise<void>;
|
|
64
|
+
okAndMoveToAwaitingWorkspace: (
|
|
65
|
+
item: ConsoleListItem,
|
|
66
|
+
option: ConsoleFieldOption,
|
|
67
|
+
) => Promise<void>;
|
|
64
68
|
addComment: (item: ConsoleListItem, body: string) => Promise<ConsoleComment>;
|
|
65
69
|
uploadAttachment: (item: ConsoleListItem, file: File) => Promise<string>;
|
|
66
70
|
addInlineReviewComment: (
|
|
@@ -276,6 +280,30 @@ export const useConsoleOperations = (
|
|
|
276
280
|
[pjcode, markDone],
|
|
277
281
|
);
|
|
278
282
|
|
|
283
|
+
const okAndMoveToAwaitingWorkspace = useCallback(
|
|
284
|
+
async (item: ConsoleListItem, option: ConsoleFieldOption) => {
|
|
285
|
+
if (pjcode === null) {
|
|
286
|
+
throw missingPjcodeError();
|
|
287
|
+
}
|
|
288
|
+
await postConsoleComment({ pjcode, url: item.url, body: 'ok' });
|
|
289
|
+
const request: ConsoleTriageRequest = {
|
|
290
|
+
pjcode,
|
|
291
|
+
action: 'set_status',
|
|
292
|
+
issueUrl: item.url,
|
|
293
|
+
projectItemId: item.projectItemId,
|
|
294
|
+
statusName: option.name,
|
|
295
|
+
};
|
|
296
|
+
await postConsoleOperation(TRIAGE_OPERATION_PATH, request);
|
|
297
|
+
invalidateItemContent(item);
|
|
298
|
+
patchOverlay(
|
|
299
|
+
overlayKeyForItem(item),
|
|
300
|
+
{ done: true, status: { name: option.name, color: option.color } },
|
|
301
|
+
mode,
|
|
302
|
+
);
|
|
303
|
+
},
|
|
304
|
+
[pjcode, invalidateItemContent, patchOverlay, mode],
|
|
305
|
+
);
|
|
306
|
+
|
|
279
307
|
const addComment = useCallback(
|
|
280
308
|
async (item: ConsoleListItem, body: string) => {
|
|
281
309
|
if (pjcode === null) {
|
|
@@ -340,6 +368,7 @@ export const useConsoleOperations = (
|
|
|
340
368
|
setStatus,
|
|
341
369
|
setInTmuxByHuman,
|
|
342
370
|
closeIssue,
|
|
371
|
+
okAndMoveToAwaitingWorkspace,
|
|
343
372
|
addComment,
|
|
344
373
|
uploadAttachment,
|
|
345
374
|
addInlineReviewComment,
|
|
@@ -127,6 +127,17 @@ describe('actionToastColor', () => {
|
|
|
127
127
|
actionToastColor({ type: 'next_action_date', action: 'snooze_1day' }),
|
|
128
128
|
).toBe('amber');
|
|
129
129
|
expect(actionToastColor({ type: 'close', action: 'close' })).toBe('red');
|
|
130
|
+
expect(actionToastColor({ type: 'ok_and_awaiting_workspace' })).toBe(
|
|
131
|
+
'blue',
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('actionToastMessage for ok_and_awaiting_workspace', () => {
|
|
137
|
+
it('returns the composite label', () => {
|
|
138
|
+
expect(
|
|
139
|
+
actionToastMessage({ type: 'ok_and_awaiting_workspace' }, 'prs'),
|
|
140
|
+
).toBe('ok → Awaiting Workspace');
|
|
130
141
|
});
|
|
131
142
|
});
|
|
132
143
|
|
|
@@ -138,6 +149,9 @@ describe('actionAdvances', () => {
|
|
|
138
149
|
expect(actionAdvances({ type: 'set_status', optionName: 'x' }, 'prs')).toBe(
|
|
139
150
|
true,
|
|
140
151
|
);
|
|
152
|
+
expect(actionAdvances({ type: 'ok_and_awaiting_workspace' }, 'prs')).toBe(
|
|
153
|
+
true,
|
|
154
|
+
);
|
|
141
155
|
});
|
|
142
156
|
|
|
143
157
|
it('does not advance a snooze except on the manual triage tabs', () => {
|