buddy-workbench 0.1.27 → 0.1.29
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/package.json +10 -1
- package/server/routes/file-organizer.js +2 -2
- package/server/routes/jira-filters.js +71 -8
- package/ui/dist/assets/index-CLQIJIsd.js +534 -0
- package/ui/dist/assets/index-Ch-WIRyY.css +1 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-B84Jc7QD.css +0 -1
- package/ui/dist/assets/index-DrY4_2l_.js +0 -530
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "buddy-workbench",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,15 @@
|
|
|
24
24
|
"version": "node -e \"const v=process.env.npm_package_version; const fs=require('fs'); ['ui/package.json', 'ui/package-lock.json'].forEach(p=>{if(fs.existsSync(p)){const j=JSON.parse(fs.readFileSync(p)); j.version=v; if(j.packages&&j.packages['']){j.packages[''].version=v;} fs.writeFileSync(p, JSON.stringify(j,null,2)+'\\n');}});\" && git add ui/package.json ui/package-lock.json"
|
|
25
25
|
},
|
|
26
26
|
"devbuddyChangelog": [
|
|
27
|
+
{
|
|
28
|
+
"version": "0.1.28",
|
|
29
|
+
"name": "Presentation planning and presenter tools",
|
|
30
|
+
"notes": [
|
|
31
|
+
"Added a Presentations library and Presentation Studio for planning, timing, and delivering presentations.",
|
|
32
|
+
"Added a configurable audience view with Markdown takeaways, themes, time boxes, branding, and full-screen support.",
|
|
33
|
+
"Added step-linked quick captures and Markdown export with clipboard copy and .md downloads."
|
|
34
|
+
]
|
|
35
|
+
},
|
|
27
36
|
{
|
|
28
37
|
"version": "0.1.27",
|
|
29
38
|
"name": "Data backups and settings refinements",
|
|
@@ -16,8 +16,8 @@ router.post('/scan', async (req, res, next) => {
|
|
|
16
16
|
const result = await scanFolder({
|
|
17
17
|
sourcePath,
|
|
18
18
|
targetPath,
|
|
19
|
-
includeSubfolders:
|
|
20
|
-
skipOrganized: skipOrganized !== false
|
|
19
|
+
includeSubfolders: includeSubfolders === true || includeSubfolders === 'true',
|
|
20
|
+
skipOrganized: skipOrganized !== false && skipOrganized !== 'false'
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
res.json(result);
|
|
@@ -112,6 +112,10 @@ router.get('/:id/issues', async (req, res) => {
|
|
|
112
112
|
return res.status(404).json({ error: 'Filter not found.' });
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
if (filter.mockIssues && Array.isArray(filter.mockIssues)) {
|
|
116
|
+
return res.json({ filter, issues: filter.mockIssues });
|
|
117
|
+
}
|
|
118
|
+
|
|
115
119
|
const jiraHost = getJiraHost();
|
|
116
120
|
if (!jiraHost) {
|
|
117
121
|
return res.status(400).json({ error: 'Jira domain is not configured. Please set Domain in Settings.' });
|
|
@@ -121,7 +125,7 @@ router.get('/:id/issues', async (req, res) => {
|
|
|
121
125
|
const token = settings.jiraAccessToken;
|
|
122
126
|
|
|
123
127
|
try {
|
|
124
|
-
const url = `https://${jiraHost}/rest/api/2/search?jql=filter%3D${encodeURIComponent(filter.filterId)}&maxResults=200&fields=summary,priority,duedate,status`;
|
|
128
|
+
const url = `https://${jiraHost}/rest/api/2/search?jql=filter%3D${encodeURIComponent(filter.filterId)}&maxResults=200&fields=summary,priority,duedate,status,issuetype`;
|
|
125
129
|
const headers = {};
|
|
126
130
|
if (token) {
|
|
127
131
|
headers.Authorization = token.startsWith('Bearer ') ? token : `Bearer ${token}`;
|
|
@@ -144,6 +148,7 @@ router.get('/:id/issues', async (req, res) => {
|
|
|
144
148
|
priority: fields.priority || null,
|
|
145
149
|
dueDate: fields.duedate || null,
|
|
146
150
|
status: fields.status?.name || null,
|
|
151
|
+
issueType: fields.issuetype?.name || null,
|
|
147
152
|
url: `https://${jiraHost}/browse/${key}`
|
|
148
153
|
};
|
|
149
154
|
});
|
|
@@ -153,14 +158,14 @@ router.get('/:id/issues', async (req, res) => {
|
|
|
153
158
|
|
|
154
159
|
res.json({ filter, issues });
|
|
155
160
|
} catch (error) {
|
|
156
|
-
const targetUrl = `https://${jiraHost}/rest/api/2/search?jql=filter%3D${encodeURIComponent(filter.filterId)}&maxResults=200&fields=summary,priority,duedate,status`;
|
|
161
|
+
const targetUrl = `https://${jiraHost}/rest/api/2/search?jql=filter%3D${encodeURIComponent(filter.filterId)}&maxResults=200&fields=summary,priority,duedate,status,issuetype`;
|
|
157
162
|
res.status(500).json({ error: error.message || 'Failed to fetch Jira issues.', targetUrl });
|
|
158
163
|
}
|
|
159
164
|
});
|
|
160
165
|
|
|
161
166
|
// Clone an issue
|
|
162
167
|
router.post('/issues/clone', async (req, res) => {
|
|
163
|
-
const { issueKey, summary } = req.body || {};
|
|
168
|
+
const { issueKey, summary, issueType } = req.body || {};
|
|
164
169
|
if (!summary || typeof summary !== 'string' || !summary.trim()) {
|
|
165
170
|
return res.status(400).json({ error: 'Summary is required for clone.' });
|
|
166
171
|
}
|
|
@@ -194,20 +199,78 @@ router.post('/issues/clone', async (req, res) => {
|
|
|
194
199
|
// Ignore if fetching current user fails
|
|
195
200
|
}
|
|
196
201
|
|
|
197
|
-
|
|
202
|
+
// Fetch original issue details to preserve issue type, project, description, priority
|
|
203
|
+
let origIssueType = null;
|
|
204
|
+
let origProjectKey = null;
|
|
205
|
+
let origDescription = null;
|
|
206
|
+
let origPriority = null;
|
|
207
|
+
|
|
208
|
+
if (issueKey) {
|
|
209
|
+
try {
|
|
210
|
+
const issueUrl = `https://${jiraHost}/rest/api/2/issue/${encodeURIComponent(issueKey)}`;
|
|
211
|
+
const origRes = await httpClient.get(issueUrl, { headers });
|
|
212
|
+
if (origRes.status === 200 && origRes.data?.fields) {
|
|
213
|
+
const fields = origRes.data.fields;
|
|
214
|
+
if (fields.issuetype) {
|
|
215
|
+
if (fields.issuetype.id) {
|
|
216
|
+
origIssueType = { id: fields.issuetype.id };
|
|
217
|
+
} else if (fields.issuetype.name) {
|
|
218
|
+
origIssueType = { name: fields.issuetype.name };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (fields.project?.key) {
|
|
222
|
+
origProjectKey = fields.project.key;
|
|
223
|
+
}
|
|
224
|
+
if (fields.description) {
|
|
225
|
+
origDescription = fields.description;
|
|
226
|
+
}
|
|
227
|
+
if (fields.priority) {
|
|
228
|
+
if (fields.priority.id) {
|
|
229
|
+
origPriority = { id: fields.priority.id };
|
|
230
|
+
} else if (fields.priority.name) {
|
|
231
|
+
origPriority = { name: fields.priority.name };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} catch {
|
|
236
|
+
// Ignore if fetching original issue fails
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Determine issue type fallback: fetched original issueType -> passed issueType -> Task
|
|
241
|
+
const targetIssueType = origIssueType || (issueType ? { name: issueType } : { name: 'Task' });
|
|
242
|
+
const projectKey = origProjectKey || (issueKey ? issueKey.split('-')[0] : '');
|
|
198
243
|
const url = `https://${jiraHost}/rest/api/2/issue`;
|
|
199
244
|
const payload = {
|
|
200
245
|
fields: {
|
|
201
246
|
summary: summary.trim(),
|
|
202
247
|
...(projectKey ? { project: { key: projectKey } } : {}),
|
|
203
|
-
issuetype:
|
|
204
|
-
...(assigneeField ? { assignee: assigneeField } : {})
|
|
248
|
+
issuetype: targetIssueType,
|
|
249
|
+
...(assigneeField ? { assignee: assigneeField } : {}),
|
|
250
|
+
...(origDescription ? { description: origDescription } : {}),
|
|
251
|
+
...(origPriority ? { priority: origPriority } : {})
|
|
205
252
|
}
|
|
206
253
|
};
|
|
207
254
|
|
|
208
|
-
|
|
255
|
+
let response = await httpClient.post(url, payload, { headers });
|
|
256
|
+
// Fallback if optional fields like description or priority caused issue creation rejection
|
|
257
|
+
if (response.status !== 201 && response.status !== 200 && (origPriority || origDescription)) {
|
|
258
|
+
const fallbackPayload = {
|
|
259
|
+
fields: {
|
|
260
|
+
summary: summary.trim(),
|
|
261
|
+
...(projectKey ? { project: { key: projectKey } } : {}),
|
|
262
|
+
issuetype: targetIssueType,
|
|
263
|
+
...(assigneeField ? { assignee: assigneeField } : {})
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
response = await httpClient.post(url, fallbackPayload, { headers });
|
|
267
|
+
}
|
|
268
|
+
|
|
209
269
|
if (response.status !== 201 && response.status !== 200) {
|
|
210
|
-
const errMsg = response.data?.errorMessages?.[0] ||
|
|
270
|
+
const errMsg = response.data?.errorMessages?.[0] ||
|
|
271
|
+
(response.data?.errors ? Object.values(response.data.errors).join(', ') : null) ||
|
|
272
|
+
response.data?.message ||
|
|
273
|
+
`Jira API returned status ${response.status}`;
|
|
211
274
|
return res.status(response.status).json({ error: errMsg });
|
|
212
275
|
}
|
|
213
276
|
|