issue-pane 2.4.9 → 2.4.10-0fa14c64
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/.eslintrc +3 -2
- package/.github/workflows/ci.yml +31 -2
- package/.nvmrc +0 -0
- package/Documentation/Configuration.html +0 -0
- package/Documentation/Configuration.md +0 -0
- package/Documentation/FooTracker/chat.ttl +0 -0
- package/Documentation/FooTracker/index.ttl +0 -0
- package/Documentation/FooTracker/state.ttl +0 -0
- package/Documentation/Makefile +0 -0
- package/Documentation/github-markdown.css +0 -0
- package/LICENSE.md +0 -0
- package/Makefile +0 -0
- package/README.md +0 -0
- package/board.js +7 -7
- package/issue.js +124 -114
- package/issuePane.js +72 -66
- package/newIssue.js +15 -19
- package/newTracker.js +26 -25
- package/package.json +10 -9
- package/tbl-bug-22.png +0 -0
- package/trackerSettingsForm.js +0 -0
- package/trackerSettingsForm.ttl +0 -0
- package/ui.js +0 -0
- package/ui.ttl +0 -0
- package/wf.js +0 -0
- package/wf.ttl +0 -0
package/.eslintrc
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"root": true,
|
|
2
3
|
"env": {
|
|
3
4
|
"browser": true,
|
|
4
5
|
"es6": true,
|
|
5
6
|
"node": true
|
|
6
7
|
},
|
|
7
|
-
"extends": "standard",
|
|
8
8
|
"globals": {
|
|
9
9
|
"Atomics": "readonly",
|
|
10
10
|
"SharedArrayBuffer": "readonly"
|
|
@@ -14,5 +14,6 @@
|
|
|
14
14
|
"argsIgnorePattern": "^_",
|
|
15
15
|
"varsIgnorePattern": "^_"
|
|
16
16
|
}]
|
|
17
|
-
}
|
|
17
|
+
},
|
|
18
|
+
"extends": ["eslint:recommended", "plugin:import/recommended"]
|
|
18
19
|
}
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -24,7 +24,36 @@ jobs:
|
|
|
24
24
|
uses: actions/setup-node@v1
|
|
25
25
|
with:
|
|
26
26
|
node-version: ${{ matrix.node-version }}
|
|
27
|
-
- run: npm install -g npm # for issues with executables on npm 7
|
|
28
27
|
- run: npm ci
|
|
28
|
+
- run: npm run lint --if-present
|
|
29
29
|
- run: npm test
|
|
30
|
-
- run: npm run build
|
|
30
|
+
- run: npm run build --if-present
|
|
31
|
+
- name: Save build
|
|
32
|
+
if: matrix.node-version == '14.x'
|
|
33
|
+
uses: actions/upload-artifact@v2
|
|
34
|
+
with:
|
|
35
|
+
name: build
|
|
36
|
+
path: |
|
|
37
|
+
.
|
|
38
|
+
!node_modules
|
|
39
|
+
retention-days: 1
|
|
40
|
+
npm-publish-build:
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/download-artifact@v2
|
|
45
|
+
with:
|
|
46
|
+
name: build
|
|
47
|
+
- uses: actions/setup-node@v1
|
|
48
|
+
with:
|
|
49
|
+
node-version: 14.x
|
|
50
|
+
- uses: rlespinasse/github-slug-action@v3.x
|
|
51
|
+
- name: Append commit hash to package version
|
|
52
|
+
run: 'sed -i -E "s/(\"version\": *\"[^\"]+)/\1-${GITHUB_SHA_SHORT}/" package.json'
|
|
53
|
+
- name: Disable pre- and post-publish actions
|
|
54
|
+
run: 'sed -i -E "s/\"((pre|post)publish)/\"ignore:\1/" package.json'
|
|
55
|
+
- uses: JS-DevTools/npm-publish@v1
|
|
56
|
+
with:
|
|
57
|
+
token: ${{ secrets.NPM_TOKEN }}
|
|
58
|
+
tag: ${{ env.GITHUB_REF_SLUG }}
|
|
59
|
+
|
package/.nvmrc
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/Documentation/Makefile
CHANGED
|
File without changes
|
|
File without changes
|
package/LICENSE.md
CHANGED
|
File without changes
|
package/Makefile
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
File without changes
|
package/board.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import * as UI from 'solid-ui'
|
|
14
|
-
|
|
14
|
+
import { store } from 'solid-logic'
|
|
15
15
|
const ns = UI.ns
|
|
16
16
|
const $rdf = UI.rdf
|
|
17
17
|
|
|
@@ -38,7 +38,7 @@ export function board (dom, columnValues, renderItem, options) {
|
|
|
38
38
|
function droppedURIHandler (uris) {
|
|
39
39
|
uris.forEach(function (u) {
|
|
40
40
|
console.log('Dropped on column: ' + u)
|
|
41
|
-
const item =
|
|
41
|
+
const item = store.sym(u)
|
|
42
42
|
options.columnDropHandler(item, x)
|
|
43
43
|
})
|
|
44
44
|
}
|
|
@@ -54,19 +54,19 @@ export function board (dom, columnValues, renderItem, options) {
|
|
|
54
54
|
function defaultRenderItem (item, category) {
|
|
55
55
|
const card = dom.createElement('div')
|
|
56
56
|
const table = card.appendChild(dom.createElement('table'))
|
|
57
|
-
const classes =
|
|
58
|
-
const catColors = classes.map(cat =>
|
|
57
|
+
const classes = store.each(item, ns.rdf('type'))
|
|
58
|
+
const catColors = classes.map(cat => store.any(cat, ns.ui('backgroundColor'))).filter(c => c)
|
|
59
59
|
|
|
60
60
|
table.appendChild(UI.widgets.personTR(dom, null, item))
|
|
61
61
|
table.subject = item
|
|
62
62
|
table.style = 'margin: 1em;' // @@ use style.js
|
|
63
|
-
const backgroundColor = catColors[0] ||
|
|
63
|
+
const backgroundColor = catColors[0] || store.any(category, ns.ui('backgroundColor'))
|
|
64
64
|
card.style.backgroundColor = backgroundColor ? backgroundColor.value : '#fff'
|
|
65
65
|
return card
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
function sortedBy (values, predicate, defaultSortValue, reverse) {
|
|
69
|
-
const toBeSorted = values.map(x => [
|
|
69
|
+
const toBeSorted = values.map(x => [store.any(x, predicate) || defaultSortValue, x])
|
|
70
70
|
toBeSorted.sort()
|
|
71
71
|
if (reverse) toBeSorted.reverse() // @@ check
|
|
72
72
|
return toBeSorted.map(pair => pair[1])
|
|
@@ -82,7 +82,7 @@ export function board (dom, columnValues, renderItem, options) {
|
|
|
82
82
|
}
|
|
83
83
|
for (let col = mainRow.firstChild; col; col = col.nextSibling) {
|
|
84
84
|
const category = col.subject
|
|
85
|
-
let items =
|
|
85
|
+
let items = store.each(null, ns.rdf('type'), category)
|
|
86
86
|
const sortBy = options.sortBy || ns.dct('created')
|
|
87
87
|
if (options.filter) {
|
|
88
88
|
items = items.filter(options.filter)
|
package/issue.js
CHANGED
|
@@ -1,52 +1,65 @@
|
|
|
1
1
|
// All the UI for a single issue, without store load or listening for changes
|
|
2
2
|
//
|
|
3
|
-
import {
|
|
3
|
+
import { icons, messageArea, ns, rdf, style, utils, widgets } from 'solid-ui'
|
|
4
|
+
import { authn, store } from 'solid-logic'
|
|
4
5
|
import { newIssueForm } from './newIssue'
|
|
5
6
|
|
|
6
7
|
const $rdf = rdf
|
|
7
|
-
const kb = store
|
|
8
8
|
|
|
9
9
|
const SET_MODIFIED_DATES = false
|
|
10
10
|
|
|
11
|
+
export const TASK_ICON = icons.iconBase + 'noun_17020_gray-tick.svg'
|
|
12
|
+
export const OPEN_TASK_ICON = icons.iconBase + 'noun_17020_sans-tick.svg'
|
|
13
|
+
export const CLOSED_TASK_ICON = icons.iconBase + 'noun_17020.svg'
|
|
14
|
+
|
|
11
15
|
function complain (message, context) {
|
|
12
16
|
console.warn(message)
|
|
13
17
|
context.paneDiv.appendChild(widgets.errorMessageBlock(context.dom, message))
|
|
14
18
|
}
|
|
15
19
|
|
|
20
|
+
export function isOpen (issue) {
|
|
21
|
+
const types = store.findTypeURIs(issue)
|
|
22
|
+
return !!types[ns.wf('Open').uri]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function iconForIssue (issue) {
|
|
26
|
+
return isOpen(issue) ? TASK_ICON : CLOSED_TASK_ICON
|
|
27
|
+
}
|
|
16
28
|
export function getState (issue, classification) {
|
|
17
|
-
const tracker =
|
|
18
|
-
const states =
|
|
29
|
+
const tracker = store.the(issue, ns.wf('tracker'), null, issue.doc())
|
|
30
|
+
const states = store.any(tracker, ns.wf('issueClass'))
|
|
19
31
|
classification = classification || states
|
|
20
|
-
const types =
|
|
21
|
-
.filter(ty =>
|
|
32
|
+
const types = store.each(issue, ns.rdf('type'))
|
|
33
|
+
.filter(ty => store.holds(ty, ns.rdfs('subClassOf'), classification))
|
|
22
34
|
if (types.length !== 1) {
|
|
23
|
-
// const initialState =
|
|
35
|
+
// const initialState = store.any(tracker, ns.wf('initialState')) No do NOT default
|
|
24
36
|
// if (initialState) return initialState
|
|
25
37
|
throw new Error('Issue must have one type as state: ' + types.length)
|
|
26
38
|
}
|
|
27
39
|
return types[0]
|
|
28
40
|
}
|
|
29
41
|
|
|
30
|
-
export function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const catColors = classes.map(cat => kb.any(cat, ns.ui('backgroundColor'))).filter(c => !!c)
|
|
42
|
+
export function getBackgroundColorFromTypes (issue) {
|
|
43
|
+
const classes = store.each(issue, ns.rdf('type')) // @@ pick cats in order then state
|
|
44
|
+
const catColors = classes.map(cat => store.any(cat, ns.ui('backgroundColor'))).filter(c => !!c)
|
|
34
45
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
if (catColors.length) return catColors[0].value // pick first one
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function renderIssueCard (issue, context) {
|
|
38
51
|
function refresh () {
|
|
39
|
-
const backgroundColor =
|
|
52
|
+
const backgroundColor = getBackgroundColorFromTypes(issue) || 'white'
|
|
40
53
|
card.style.backgroundColor = backgroundColor
|
|
41
54
|
editButton.style.backgroundColor = backgroundColor // Override white from style sheet
|
|
42
55
|
}
|
|
43
56
|
const dom = context.dom
|
|
44
|
-
const uncategorized = !
|
|
57
|
+
const uncategorized = !getBackgroundColorFromTypes(issue) // This is a suspect issue. Prompt to delete it
|
|
45
58
|
|
|
46
59
|
const card = dom.createElement('div')
|
|
47
60
|
const table = card.appendChild(dom.createElement('table'))
|
|
48
61
|
table.style.width = '100%'
|
|
49
|
-
const options = { draggable: false } // Let the
|
|
62
|
+
const options = { draggable: false } // Let the board make the whole card draggable
|
|
50
63
|
table.appendChild(widgets.personTR(dom, null, issue, options))
|
|
51
64
|
table.subject = issue
|
|
52
65
|
card.style = 'border-radius: 0.4em; border: 0.05em solid grey; margin: 0.3em;'
|
|
@@ -66,7 +79,7 @@ export function renderIssueCard (issue, context) {
|
|
|
66
79
|
if (uncategorized) {
|
|
67
80
|
const deleteButton = widgets.deleteButtonWithCheck(dom, buttonsCell, 'issue', async function () { // noun?
|
|
68
81
|
try {
|
|
69
|
-
await
|
|
82
|
+
await store.updater.update(store.connectedStatements(issue))
|
|
70
83
|
} catch (err) {
|
|
71
84
|
complain(`Unable to delete issue: ${err}`, context)
|
|
72
85
|
}
|
|
@@ -100,18 +113,24 @@ export function exposeOverlay (subject, context) {
|
|
|
100
113
|
overlay.firstChild.style.overflow = 'auto' // was scroll
|
|
101
114
|
}
|
|
102
115
|
|
|
116
|
+
function renderSpacer (dom, backgroundColor) {
|
|
117
|
+
const spacer = dom.createElement('div')
|
|
118
|
+
spacer.setAttribute('style', 'height: 1em; margin: 0.5em;') // spacer and placeHolder
|
|
119
|
+
spacer.style.backgroundColor = backgroundColor // try that
|
|
120
|
+
return spacer
|
|
121
|
+
}
|
|
122
|
+
|
|
103
123
|
export function renderIssue (issue, context) {
|
|
104
124
|
// Don't bother changing the last modified dates of things: save time
|
|
105
|
-
function setModifiedDate (subj,
|
|
125
|
+
function setModifiedDate (subj, store, doc) {
|
|
106
126
|
if (SET_MODIFIED_DATES) {
|
|
107
127
|
if (!getOption(tracker, 'trackLastModified')) return
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
)
|
|
128
|
+
const deletions = store.statementsMatching(issue, ns.dct('modified'))
|
|
129
|
+
.concat(store.statementsMatching(issue, ns.wf('modifiedBy'))
|
|
130
|
+
)
|
|
112
131
|
const insertions = [$rdf.st(issue, ns.dct('modified'), new Date(), doc)]
|
|
113
132
|
if (me) insertions.push($rdf.st(issue, ns.wf('modifiedBy'), me, doc))
|
|
114
|
-
|
|
133
|
+
store.updater.update(deletions, insertions, function (_uri, _ok, _body) {})
|
|
115
134
|
}
|
|
116
135
|
}
|
|
117
136
|
|
|
@@ -123,7 +142,7 @@ export function renderIssue (issue, context) {
|
|
|
123
142
|
return pre
|
|
124
143
|
}
|
|
125
144
|
|
|
126
|
-
|
|
145
|
+
function timestring () {
|
|
127
146
|
const now = new Date()
|
|
128
147
|
return '' + now.getTime()
|
|
129
148
|
// http://www.w3schools.com/jsref/jsref_obj_date.asp
|
|
@@ -144,50 +163,50 @@ export function renderIssue (issue, context) {
|
|
|
144
163
|
}
|
|
145
164
|
function getOption (tracker, option) {
|
|
146
165
|
// eg 'allowSubIssues'
|
|
147
|
-
const opt =
|
|
166
|
+
const opt = store.any(tracker, ns.ui(option))
|
|
148
167
|
return !!(opt && opt.value)
|
|
149
168
|
}
|
|
150
169
|
|
|
151
170
|
function setPaneStyle () {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
for (const uri in types) {
|
|
156
|
-
backgroundColor = kb.any(
|
|
157
|
-
kb.sym(uri),
|
|
158
|
-
kb.sym('http://www.w3.org/ns/ui#backgroundColor')
|
|
159
|
-
)
|
|
160
|
-
if (backgroundColor) break
|
|
161
|
-
}
|
|
162
|
-
backgroundColor = backgroundColor ? backgroundColor.value : '#eee' // default grey
|
|
163
|
-
mystyle += 'background-color: ' + backgroundColor + '; '
|
|
171
|
+
const backgroundColor = getBackgroundColorFromTypes(issue) || '#eee' // default grey
|
|
172
|
+
const mystyle0 = 'padding: 0.5em 1.5em 1em 1.5em; border: 0.7em;'
|
|
173
|
+
const mystyle = mystyle0 + 'border-color: ' + backgroundColor + '; '
|
|
164
174
|
issueDiv.setAttribute('style', mystyle)
|
|
175
|
+
issueDiv.style.backgroundColor = 'white'
|
|
165
176
|
}
|
|
166
177
|
|
|
178
|
+
/// ////////////// Body of renderIssue
|
|
179
|
+
|
|
167
180
|
const dom = context.dom
|
|
168
|
-
|
|
181
|
+
// eslint-disable-next-line no-use-before-define
|
|
182
|
+
const tracker = store.the(issue, ns.wf('tracker'), null, issue.doc())
|
|
169
183
|
if (!tracker) throw new Error('No tracker')
|
|
170
|
-
|
|
184
|
+
// eslint-disable-next-line no-use-before-define
|
|
185
|
+
const stateStore = store.any(tracker, ns.wf('stateStore'))
|
|
171
186
|
const store = issue.doc()
|
|
172
187
|
|
|
173
188
|
const issueDiv = dom.createElement('div')
|
|
174
|
-
|
|
189
|
+
const me = authn.currentUser()
|
|
190
|
+
const backgroundColor = getBackgroundColorFromTypes(issue) || 'white'
|
|
175
191
|
|
|
176
192
|
setPaneStyle()
|
|
177
193
|
|
|
178
194
|
authn.checkUser() // kick off async operation
|
|
179
195
|
|
|
180
|
-
const
|
|
196
|
+
const iconButton = issueDiv.appendChild(widgets.button(dom, iconForIssue(issue)))
|
|
197
|
+
widgets.makeDraggable(iconButton, issue) // Drag me wherever you need to do stuff with this issue
|
|
198
|
+
|
|
199
|
+
const states = store.any(tracker, ns.wf('issueClass'))
|
|
181
200
|
if (!states) { throw new Error('This tracker ' + tracker + ' has no issueClass') }
|
|
182
201
|
const select = widgets.makeSelectForCategory(
|
|
183
202
|
dom,
|
|
184
|
-
|
|
203
|
+
store,
|
|
185
204
|
issue,
|
|
186
205
|
states,
|
|
187
206
|
stateStore,
|
|
188
207
|
function (ok, body) {
|
|
189
208
|
if (ok) {
|
|
190
|
-
setModifiedDate(store,
|
|
209
|
+
setModifiedDate(store, store, store)
|
|
191
210
|
widgets.refreshTree(issueDiv)
|
|
192
211
|
} else {
|
|
193
212
|
console.log('Failed to change state:\n' + body)
|
|
@@ -196,18 +215,18 @@ export function renderIssue (issue, context) {
|
|
|
196
215
|
)
|
|
197
216
|
issueDiv.appendChild(select)
|
|
198
217
|
|
|
199
|
-
const cats =
|
|
200
|
-
for (
|
|
218
|
+
const cats = store.each(tracker, ns.wf('issueCategory')) // zero or more
|
|
219
|
+
for (const cat of cats) {
|
|
201
220
|
issueDiv.appendChild(
|
|
202
221
|
widgets.makeSelectForCategory(
|
|
203
222
|
dom,
|
|
204
|
-
|
|
223
|
+
store,
|
|
205
224
|
issue,
|
|
206
|
-
|
|
225
|
+
cat,
|
|
207
226
|
stateStore,
|
|
208
227
|
function (ok, body) {
|
|
209
228
|
if (ok) {
|
|
210
|
-
setModifiedDate(store,
|
|
229
|
+
setModifiedDate(store, store, store)
|
|
211
230
|
widgets.refreshTree(issueDiv)
|
|
212
231
|
} else {
|
|
213
232
|
console.log('Failed to change category:\n' + body)
|
|
@@ -251,40 +270,26 @@ export function renderIssue (issue, context) {
|
|
|
251
270
|
wf:Task :creationForm core:coreIsueForm .
|
|
252
271
|
`
|
|
253
272
|
const CORE_ISSUE_FORM = ns.wf('coreIsueForm')
|
|
254
|
-
$rdf.parse(coreIssueFormText,
|
|
255
|
-
widgets.appendForm(
|
|
273
|
+
$rdf.parse(coreIssueFormText, store, CORE_ISSUE_FORM.doc().uri, 'text/turtle')
|
|
274
|
+
const form = widgets.appendForm(
|
|
256
275
|
dom,
|
|
257
|
-
|
|
276
|
+
null, // was: container
|
|
258
277
|
{},
|
|
259
278
|
issue,
|
|
260
279
|
CORE_ISSUE_FORM,
|
|
261
280
|
stateStore,
|
|
262
281
|
complainIfBad
|
|
263
282
|
)
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
/*
|
|
267
|
-
issueDiv.appendChild(
|
|
268
|
-
widgets.makeDescription(
|
|
269
|
-
dom,
|
|
270
|
-
kb,
|
|
271
|
-
issue,
|
|
272
|
-
ns.wf('description'),
|
|
273
|
-
store,
|
|
274
|
-
function (ok, body) {
|
|
275
|
-
if (ok) setModifiedDate(store, kb, store)
|
|
276
|
-
else console.log('Failed to change description:\n' + body)
|
|
277
|
-
}
|
|
278
|
-
)
|
|
279
|
-
) */
|
|
283
|
+
issueDiv.appendChild(form)
|
|
284
|
+
form.style.backgroundColor = backgroundColor
|
|
280
285
|
|
|
281
286
|
// Assigned to whom?
|
|
282
287
|
|
|
283
|
-
const assignments =
|
|
288
|
+
const assignments = store.statementsMatching(issue, ns.wf('assignee'))
|
|
284
289
|
if (assignments.length > 1) {
|
|
285
290
|
say('Weird, was assigned to more than one person. Fixing ..')
|
|
286
291
|
const deletions = assignments.slice(1)
|
|
287
|
-
|
|
292
|
+
store.updater.update(deletions, [], function (uri, ok, body) {
|
|
288
293
|
if (ok) {
|
|
289
294
|
say('Now fixed.')
|
|
290
295
|
} else {
|
|
@@ -297,20 +302,16 @@ export function renderIssue (issue, context) {
|
|
|
297
302
|
// Anyone assigned to any issue we know about
|
|
298
303
|
|
|
299
304
|
async function getPossibleAssignees () {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const group = devGroups[i]
|
|
304
|
-
await kb.fetcher.load()
|
|
305
|
-
devs = devs.concat(kb.each(group, ns.vcard('member')))
|
|
306
|
-
}
|
|
305
|
+
const devGroups = store.each(issue, ns.wf('assigneeGroup'))
|
|
306
|
+
await store.fetcher.load(devGroups) // Load them all
|
|
307
|
+
const groupDevs = devGroups.map(group => store.each(group, ns.vcard('member'), null, group.doc())).flat()
|
|
307
308
|
// Anyone who is a developer of any project which uses this tracker
|
|
308
|
-
const proj =
|
|
309
|
+
const proj = store.any(null, ns.doap('bug-database'), tracker) // What project?
|
|
309
310
|
if (proj) {
|
|
310
|
-
await
|
|
311
|
-
devs = devs.concat(kb.each(proj, ns.doap('developer')))
|
|
311
|
+
await store.fetcher.load(proj)
|
|
312
312
|
}
|
|
313
|
-
|
|
313
|
+
const projectDevs = proj ? store.each(proj, ns.doap('developer')) : []
|
|
314
|
+
return groupDevs.concat(projectDevs)
|
|
314
315
|
}
|
|
315
316
|
|
|
316
317
|
// Super issues first - like parent directories .. maybe use breadcrums from?? @@
|
|
@@ -322,7 +323,7 @@ export function renderIssue (issue, context) {
|
|
|
322
323
|
getPossibleAssignees().then(devs => {
|
|
323
324
|
if (devs.length) {
|
|
324
325
|
devs.forEach(function (person) {
|
|
325
|
-
|
|
326
|
+
store.fetcher.lookUpThing(person)
|
|
326
327
|
}) // best effort async for names etc
|
|
327
328
|
const opts = {
|
|
328
329
|
// 'mint': '** Add new person **',
|
|
@@ -337,14 +338,14 @@ export function renderIssue (issue, context) {
|
|
|
337
338
|
issueDiv.appendChild(
|
|
338
339
|
widgets.makeSelectForOptions(
|
|
339
340
|
dom,
|
|
340
|
-
|
|
341
|
+
store,
|
|
341
342
|
issue,
|
|
342
343
|
ns.wf('assignee'),
|
|
343
344
|
devs,
|
|
344
345
|
opts,
|
|
345
346
|
store,
|
|
346
347
|
function (ok, body) {
|
|
347
|
-
if (ok) setModifiedDate(store,
|
|
348
|
+
if (ok) setModifiedDate(store, store, store)
|
|
348
349
|
else console.log('Failed to change assignee:\n' + body)
|
|
349
350
|
}
|
|
350
351
|
)
|
|
@@ -352,27 +353,36 @@ export function renderIssue (issue, context) {
|
|
|
352
353
|
}
|
|
353
354
|
})
|
|
354
355
|
|
|
355
|
-
/* The trees of super
|
|
356
|
+
/* The trees of super-issues and sub-issues
|
|
356
357
|
*/
|
|
357
|
-
|
|
358
|
+
function supersOver (issue, stack) {
|
|
359
|
+
stack = stack || []
|
|
360
|
+
const sup = store.any(null, ns.wf('dependent'), issue, issue.doc())
|
|
361
|
+
if (sup) return supersOver(sup, [sup].concat(stack))
|
|
362
|
+
return stack
|
|
363
|
+
}
|
|
358
364
|
if (getOption(tracker, 'allowSubIssues')) {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
subIssuePanel.style = 'margin: 1em; padding: 1em;'
|
|
362
|
-
}
|
|
365
|
+
const subIssuePanel = issueDiv.appendChild(dom.createElement('div'))
|
|
366
|
+
subIssuePanel.style = 'margin: 1em; padding: 1em;'
|
|
363
367
|
|
|
364
368
|
subIssuePanel.appendChild(dom.createElement('h4')).textContent = 'Super Issues'
|
|
365
369
|
const listOfSupers = subIssuePanel.appendChild(dom.createElement('div'))
|
|
370
|
+
listOfSupers.style.display = 'flex'
|
|
366
371
|
listOfSupers.refresh = function () {
|
|
367
|
-
|
|
372
|
+
// const supers = store.each(null, ns.wf('dependent'), issue, issue.doc())
|
|
373
|
+
const supers = supersOver(issue)
|
|
374
|
+
utils.syncTableToArrayReOrdered(listOfSupers, supers, renderSubIssue)
|
|
368
375
|
}
|
|
369
376
|
listOfSupers.refresh()
|
|
370
377
|
|
|
371
378
|
// Sub issues
|
|
372
379
|
subIssuePanel.appendChild(dom.createElement('h4')).textContent = 'Sub Issues'
|
|
373
380
|
const listOfSubs = subIssuePanel.appendChild(dom.createElement('div'))
|
|
381
|
+
listOfSubs.style.display = 'flex'
|
|
382
|
+
listOfSubs.style.flexDirection = 'reverse' // Or center
|
|
374
383
|
listOfSubs.refresh = function () {
|
|
375
|
-
|
|
384
|
+
const subs = store.each(issue, ns.wf('dependent'), null, issue.doc())
|
|
385
|
+
utils.syncTableToArrayReOrdered(listOfSubs, subs, renderSubIssue)
|
|
376
386
|
}
|
|
377
387
|
listOfSubs.refresh()
|
|
378
388
|
|
|
@@ -385,7 +395,7 @@ export function renderIssue (issue, context) {
|
|
|
385
395
|
b.addEventListener(
|
|
386
396
|
'click',
|
|
387
397
|
function (_event) {
|
|
388
|
-
subIssuePanel.insertBefore(newIssueForm(dom,
|
|
398
|
+
subIssuePanel.insertBefore(newIssueForm(dom, store, tracker, issue, listOfSubs.refresh), b.nextSibling) // Pop form just after button
|
|
389
399
|
},
|
|
390
400
|
false
|
|
391
401
|
)
|
|
@@ -394,7 +404,7 @@ export function renderIssue (issue, context) {
|
|
|
394
404
|
issueDiv.appendChild(dom.createElement('br'))
|
|
395
405
|
|
|
396
406
|
// Extras are stored centrally to the tracker
|
|
397
|
-
const extrasForm =
|
|
407
|
+
const extrasForm = store.any(tracker, ns.wf('extrasEntryForm'))
|
|
398
408
|
if (extrasForm) {
|
|
399
409
|
widgets.appendForm(
|
|
400
410
|
dom,
|
|
@@ -405,40 +415,41 @@ export function renderIssue (issue, context) {
|
|
|
405
415
|
stateStore,
|
|
406
416
|
complainIfBad
|
|
407
417
|
)
|
|
418
|
+
// issueDiv.appendChild(renderSpacer(backgroundColor))
|
|
408
419
|
}
|
|
409
420
|
|
|
410
421
|
// Comment/discussion area
|
|
411
422
|
|
|
412
|
-
const spacer = issueDiv.appendChild(dom
|
|
413
|
-
spacer.setAttribute('style', 'height: 1em') // spacer and placeHolder
|
|
423
|
+
const spacer = issueDiv.appendChild(renderSpacer(dom, backgroundColor))
|
|
414
424
|
|
|
415
|
-
const template =
|
|
425
|
+
const template = store.anyValue(tracker, ns.wf('issueURITemplate'))
|
|
416
426
|
/*
|
|
417
|
-
var chatDocURITemplate =
|
|
427
|
+
var chatDocURITemplate = store.anyValue(tracker, ns.wf('chatDocURITemplate')) // relaive to issue
|
|
418
428
|
var chat
|
|
419
429
|
if (chatDocURITemplate) {
|
|
420
430
|
let template = $rdf.uri.join(chatDocURITemplate, issue.uri) // Template is relative to issue
|
|
421
|
-
chat =
|
|
431
|
+
chat = store.sym(expandTemplate(template))
|
|
422
432
|
} else
|
|
423
433
|
*/
|
|
424
434
|
let messageStore
|
|
425
435
|
if (template) {
|
|
426
436
|
messageStore = issue.doc() // for now. Could go deeper
|
|
427
437
|
} else {
|
|
428
|
-
messageStore =
|
|
429
|
-
if (!messageStore) messageStore =
|
|
430
|
-
|
|
438
|
+
messageStore = store.any(tracker, ns.wf('messageStore'))
|
|
439
|
+
if (!messageStore) messageStore = store.any(tracker, ns.wf('stateStore'))
|
|
440
|
+
store.sym(messageStore.uri + '#' + 'Chat' + timestring()) // var chat =
|
|
431
441
|
}
|
|
432
442
|
|
|
433
|
-
|
|
443
|
+
store.fetcher.nowOrWhenFetched(messageStore, function (ok, body, _xhr) {
|
|
434
444
|
if (!ok) {
|
|
435
445
|
const er = dom.createElement('p')
|
|
436
446
|
er.textContent = body // @@ use nice error message
|
|
437
447
|
issueDiv.insertBefore(er, spacer)
|
|
438
448
|
} else {
|
|
439
|
-
const discussion = messageArea(dom,
|
|
449
|
+
const discussion = messageArea(dom, store, issue, messageStore)
|
|
440
450
|
issueDiv.insertBefore(discussion, spacer)
|
|
441
|
-
|
|
451
|
+
issueDiv.insertBefore(renderSpacer(dom, backgroundColor), discussion)
|
|
452
|
+
} // Not sure why e stuck this in upwards rather than downwards
|
|
442
453
|
})
|
|
443
454
|
|
|
444
455
|
// Draggable attachment list
|
|
@@ -446,23 +457,22 @@ export function renderIssue (issue, context) {
|
|
|
446
457
|
attachmentHint.innerHTML = `<h4>Attachments</h4>
|
|
447
458
|
<p>Drag files, emails,
|
|
448
459
|
web pages onto the paper clip, or click the file upload button.</p>`
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}
|
|
460
|
+
const uploadFolderURI =
|
|
461
|
+
issue.uri.endsWith('/index.ttl#this') // This has a whole folder to itself
|
|
462
|
+
? issue.uri.slice(0, 14) + 'Files/' // back to slash
|
|
463
|
+
: issue.dir().uri + 'Files/' + issue.uri.split('#')[1] + '/' // New folder for issue in file with others
|
|
464
|
+
|
|
455
465
|
widgets.attachmentList(dom, issue, issueDiv, {
|
|
456
466
|
doc: stateStore,
|
|
457
467
|
promptIcon: icons.iconBase + 'noun_25830.svg',
|
|
458
|
-
uploadFolder:
|
|
468
|
+
uploadFolder: store.sym(uploadFolderURI), // Allow local files to be uploaded
|
|
459
469
|
predicate: ns.wf('attachment')
|
|
460
470
|
})
|
|
461
471
|
|
|
462
472
|
// Delete button to delete the issue
|
|
463
473
|
const deleteButton = widgets.deleteButtonWithCheck(dom, issueDiv, 'issue', async function () {
|
|
464
474
|
try {
|
|
465
|
-
await
|
|
475
|
+
await store.updater.update(store.connectedStatements(issue))
|
|
466
476
|
} catch (err) {
|
|
467
477
|
complain(`Unable to delete issue: ${err}`, context)
|
|
468
478
|
}
|
|
@@ -480,7 +490,7 @@ export function renderIssue (issue, context) {
|
|
|
480
490
|
'click',
|
|
481
491
|
async function (_event) {
|
|
482
492
|
try {
|
|
483
|
-
await
|
|
493
|
+
await store.fetcher.load(messageStore, { force: true, clearPreviousData: true })
|
|
484
494
|
} catch (err) {
|
|
485
495
|
alert(err)
|
|
486
496
|
return
|
package/issuePane.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import * as UI from 'solid-ui'
|
|
9
|
+
import { store, authn } from 'solid-logic'
|
|
9
10
|
import { board } from './board' // @@ will later be in solid-UI
|
|
10
11
|
import { renderIssue, renderIssueCard, getState, exposeOverlay } from './issue'
|
|
11
12
|
import { newTrackerButton } from './newTracker'
|
|
@@ -15,7 +16,6 @@ import { trackerSettingsFormText } from './trackerSettingsForm.js'
|
|
|
15
16
|
|
|
16
17
|
const $rdf = UI.rdf
|
|
17
18
|
const ns = UI.ns
|
|
18
|
-
const kb = UI.store
|
|
19
19
|
const widgets = UI.widgets
|
|
20
20
|
|
|
21
21
|
// const MY_TRACKERS_ICON = UI.icons.iconBase + 'noun_Document_998605.svg'
|
|
@@ -36,10 +36,10 @@ export default {
|
|
|
36
36
|
|
|
37
37
|
// Does the subject deserve an issue pane?
|
|
38
38
|
label: function (subject, _context) {
|
|
39
|
-
const t =
|
|
39
|
+
const t = store.findTypeURIs(subject)
|
|
40
40
|
if (
|
|
41
41
|
t['http://www.w3.org/2005/01/wf/flow#Task'] ||
|
|
42
|
-
|
|
42
|
+
store.holds(subject, UI.ns.wf('tracker'))
|
|
43
43
|
) { return 'issue' } // in case ontology not available
|
|
44
44
|
if (t['http://www.w3.org/2005/01/wf/flow#Tracker']) return 'tracker'
|
|
45
45
|
// Later: Person. For a list of things assigned to them,
|
|
@@ -61,7 +61,7 @@ export default {
|
|
|
61
61
|
return Promise.all(updates)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
const kb = context.session.store
|
|
65
65
|
const ns = UI.ns
|
|
66
66
|
let stateStore
|
|
67
67
|
if (options.newInstance) {
|
|
@@ -73,7 +73,7 @@ export default {
|
|
|
73
73
|
const tracker = options.newInstance
|
|
74
74
|
const appDoc = tracker.doc()
|
|
75
75
|
|
|
76
|
-
const me =
|
|
76
|
+
const me = authn.currentUser()
|
|
77
77
|
if (me) {
|
|
78
78
|
kb.add(tracker, ns.dc('author'), me, appDoc)
|
|
79
79
|
}
|
|
@@ -140,73 +140,81 @@ export default {
|
|
|
140
140
|
** This is would not be needed if our quey language
|
|
141
141
|
** allowed is to query ardf Collection membership.
|
|
142
142
|
*/
|
|
143
|
-
async function fixSubClasses (kb, tracker) {
|
|
143
|
+
async function fixSubClasses (kb, tracker) { // 20220228
|
|
144
144
|
async function checkOneSuperclass (klass) {
|
|
145
145
|
const collection = kb.any(klass, ns.owl('disjointUnionOf'), null, doc)
|
|
146
146
|
if (!collection) throw new Error(`Classification ${klass} has no disjointUnionOf`)
|
|
147
147
|
if (!collection.elements) throw new Error(`Classification ${klass} has no array`)
|
|
148
148
|
const needed = new Set(collection.elements.map(x => x.uri))
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
|
|
150
|
+
const existing = new Set(kb.each(null, ns.rdfs('subClassOf'), klass, doc).map(x => x.uri))
|
|
151
|
+
const superfluous = existing.filter(sub => !needed.has(sub))
|
|
152
|
+
const deleteActions = superfluous.map(sub => { return { action: 'delete', st: $rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, doc) } })
|
|
153
|
+
/*
|
|
151
154
|
for (const sub of existing) {
|
|
152
155
|
if (!needed.has(sub)) {
|
|
153
156
|
deletables.push($rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, doc))
|
|
154
157
|
}
|
|
155
158
|
}
|
|
159
|
+
*/
|
|
160
|
+
const missing = needed.filter(sub => !existing.has(sub))
|
|
161
|
+
const insertActions = missing.ma(sub => { return { action: 'insert', st: $rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, doc) } })
|
|
162
|
+
/*
|
|
156
163
|
for (const sub of needed) {
|
|
157
164
|
if (!existing.has(sub)) {
|
|
158
165
|
insertables.push($rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, doc))
|
|
159
166
|
}
|
|
160
167
|
}
|
|
168
|
+
*/
|
|
169
|
+
return deleteActions.concat(insertActions)
|
|
161
170
|
}
|
|
162
171
|
const doc = tracker.doc()
|
|
163
172
|
const states = kb.any(tracker, ns.wf('issueClass'))
|
|
164
|
-
const cats = kb.each(tracker, ns.wf('issueCategory'))
|
|
165
|
-
|
|
166
|
-
var deletables = []
|
|
167
|
-
cats.push(states)
|
|
173
|
+
const cats = kb.each(tracker, ns.wf('issueCategory')).concat([states])
|
|
174
|
+
let damage = [] // to make totally functionaly need to deal with map over async.
|
|
168
175
|
for (const klass of cats) {
|
|
169
|
-
await checkOneSuperclass(klass)
|
|
176
|
+
damage = damage.concat(await checkOneSuperclass(klass))
|
|
170
177
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
if (damage.length) {
|
|
179
|
+
const insertables = damage.filter(fix => fix.action === 'insert').map(fix => fix.st)
|
|
180
|
+
const deletables = damage.filter(fix => fix.action === 'delete').map(fix => fix.st)
|
|
181
|
+
// alert(`Internal error: s${damage} subclasses inconsistences!`)
|
|
182
|
+
console.log('Damage:', damage)
|
|
175
183
|
if (confirm(`Fix ${damage} inconsistent subclasses in tracker config?`)) {
|
|
176
184
|
await kb.updater.update(deletables, insertables)
|
|
177
|
-
|
|
185
|
+
}
|
|
178
186
|
}
|
|
179
187
|
}
|
|
180
188
|
|
|
181
189
|
/** /////////////////////////// Board
|
|
182
190
|
*/
|
|
183
191
|
function renderBoard (tracker, klass) {
|
|
184
|
-
const states =
|
|
192
|
+
const states = store.any(tracker, ns.wf('issueClass'))
|
|
185
193
|
klass = klass || states // default to states
|
|
186
194
|
const doingStates = klass.sameTerm(states)
|
|
187
195
|
|
|
188
196
|
// These are states we will show by default: the open issues.
|
|
189
|
-
const stateArray =
|
|
197
|
+
const stateArray = store.any(klass, ns.owl('disjointUnionOf'))
|
|
190
198
|
if (!stateArray) {
|
|
191
199
|
return complain(`Configuration error: state ${states} does not have substates`)
|
|
192
200
|
}
|
|
193
201
|
let columnValues = stateArray.elements
|
|
194
202
|
if (doingStates && columnValues.length > 2 // and there are more than two
|
|
195
203
|
) { // strip out closed states
|
|
196
|
-
columnValues = columnValues.filter(state =>
|
|
204
|
+
columnValues = columnValues.filter(state => store.holds(state, ns.rdfs('subClassOf'), ns.wf('Open')) || state.sameTerm(ns.wf('Open')))
|
|
197
205
|
}
|
|
198
206
|
|
|
199
207
|
async function columnDropHandler (issue, newState) {
|
|
200
208
|
const currentState = getState(issue, klass)
|
|
201
|
-
const tracker =
|
|
202
|
-
const stateStore =
|
|
209
|
+
const tracker = store.the(issue, ns.wf('tracker'), null, issue.doc())
|
|
210
|
+
const stateStore = store.any(tracker, ns.wf('stateStore'))
|
|
203
211
|
|
|
204
212
|
if (newState.sameTerm(currentState)) {
|
|
205
213
|
// alert('Same state ' + UI.utils.label(currentState)) // @@ remove
|
|
206
214
|
return
|
|
207
215
|
}
|
|
208
216
|
try {
|
|
209
|
-
await
|
|
217
|
+
await store.updater.update(
|
|
210
218
|
[$rdf.st(issue, ns.rdf('type'), currentState, stateStore)],
|
|
211
219
|
[$rdf.st(issue, ns.rdf('type'), newState, stateStore)])
|
|
212
220
|
} catch (err) {
|
|
@@ -216,7 +224,7 @@ export default {
|
|
|
216
224
|
}
|
|
217
225
|
|
|
218
226
|
function isOpen (issue) {
|
|
219
|
-
const types =
|
|
227
|
+
const types = store.findTypeURIs(issue)
|
|
220
228
|
return !!types[ns.wf('Open').uri]
|
|
221
229
|
}
|
|
222
230
|
|
|
@@ -237,7 +245,7 @@ export default {
|
|
|
237
245
|
const refreshButton = widgets.button(dom, UI.icons.iconBase + 'noun_479395.svg',
|
|
238
246
|
'refresh table', async _event => {
|
|
239
247
|
try {
|
|
240
|
-
await
|
|
248
|
+
await store.fetcher.load(stateStore, { force: true, clearPreviousData: true })
|
|
241
249
|
} catch (err) {
|
|
242
250
|
alert(err)
|
|
243
251
|
return
|
|
@@ -253,10 +261,10 @@ export default {
|
|
|
253
261
|
query.pat.optional.push(clause)
|
|
254
262
|
return clause
|
|
255
263
|
}
|
|
256
|
-
const states =
|
|
257
|
-
const cats =
|
|
264
|
+
const states = store.any(subject, ns.wf('issueClass'))
|
|
265
|
+
const cats = store.each(tracker, ns.wf('issueCategory')) // zero or more
|
|
258
266
|
const vars = ['issue', 'state', 'created']
|
|
259
|
-
|
|
267
|
+
const query = new $rdf.Query(UI.utils.label(subject))
|
|
260
268
|
for (let i = 0; i < cats.length; i++) {
|
|
261
269
|
vars.push('_cat_' + i)
|
|
262
270
|
}
|
|
@@ -278,7 +286,7 @@ export default {
|
|
|
278
286
|
clause.add(v['_cat_' + i], ns.rdfs('subClassOf'), cats[i])
|
|
279
287
|
}
|
|
280
288
|
|
|
281
|
-
const propertyList =
|
|
289
|
+
const propertyList = store.any(tracker, ns.wf('propertyList')) // List of extra properties
|
|
282
290
|
if (propertyList) {
|
|
283
291
|
const properties = propertyList.elements
|
|
284
292
|
for (let p = 0; p < properties.length; p++) {
|
|
@@ -294,10 +302,10 @@ export default {
|
|
|
294
302
|
}
|
|
295
303
|
|
|
296
304
|
const selectedStates = {}
|
|
297
|
-
const possible =
|
|
305
|
+
const possible = store.each(undefined, ns.rdfs('subClassOf'), states)
|
|
298
306
|
possible.forEach(function (s) {
|
|
299
307
|
if (
|
|
300
|
-
|
|
308
|
+
store.holds(s, ns.rdfs('subClassOf'), ns.wf('Open')) ||
|
|
301
309
|
s.sameTerm(ns.wf('Open'))
|
|
302
310
|
) {
|
|
303
311
|
selectedStates[s.uri] = true
|
|
@@ -321,7 +329,7 @@ export default {
|
|
|
321
329
|
'?state': { initialSelection: selectedStates, label: 'Status' }
|
|
322
330
|
}
|
|
323
331
|
})
|
|
324
|
-
const stateStore =
|
|
332
|
+
const stateStore = store.any(subject, ns.wf('stateStore'))
|
|
325
333
|
tableDiv.appendChild(tableRefreshButton(stateStore, tableDiv))
|
|
326
334
|
return tableDiv
|
|
327
335
|
}
|
|
@@ -329,7 +337,7 @@ export default {
|
|
|
329
337
|
// Allow user to create new things within the folder
|
|
330
338
|
function renderCreationControl (refreshTarget) {
|
|
331
339
|
const creationDiv = dom.createElement('div')
|
|
332
|
-
const me =
|
|
340
|
+
const me = authn.currentUser()
|
|
333
341
|
const creationContext = {
|
|
334
342
|
// folder: subject,
|
|
335
343
|
div: creationDiv,
|
|
@@ -348,7 +356,7 @@ export default {
|
|
|
348
356
|
function renderInstances (theClass) {
|
|
349
357
|
const instancesDiv = dom.createElement('div')
|
|
350
358
|
const context = { dom, div: instancesDiv, noun: 'tracker' }
|
|
351
|
-
UI.
|
|
359
|
+
UI.login.registrationList(context, { public: true, private: true, type: theClass }).then(_context2 => {
|
|
352
360
|
instancesDiv.appendChild(renderCreationControl(instancesDiv))
|
|
353
361
|
/* // keep this code in case we need a form
|
|
354
362
|
const InstancesForm = ns.wf('TrackerInstancesForm')
|
|
@@ -364,10 +372,10 @@ export default {
|
|
|
364
372
|
const settingsDiv = dom.createElement('div')
|
|
365
373
|
// A registration control allows the to record this tracker in their type index
|
|
366
374
|
const context = { dom, div: settingsDiv, noun: 'tracker' }
|
|
367
|
-
UI.
|
|
375
|
+
UI.login.registrationControl(context, tracker, ns.wf('Tracker')).then(_context2 => {
|
|
368
376
|
const settingsForm = ns.wf('TrackerSettingsForm')
|
|
369
377
|
const text = trackerSettingsFormText
|
|
370
|
-
$rdf.parse(text,
|
|
378
|
+
$rdf.parse(text, store, settingsForm.doc().uri, 'text/turtle')
|
|
371
379
|
widgets.appendForm(dom, settingsDiv, {}, tracker, settingsForm,
|
|
372
380
|
tracker.doc(), complainIfBad)
|
|
373
381
|
})
|
|
@@ -385,25 +393,25 @@ export default {
|
|
|
385
393
|
ele.appendChild(renderSettings(tracker))
|
|
386
394
|
} else if (object.sameTerm(instancesView)) {
|
|
387
395
|
ele.appendChild(renderInstances(ns.wf('Tracker')))
|
|
388
|
-
} else if ((
|
|
389
|
-
(
|
|
396
|
+
} else if ((store.holds(tracker, ns.wf('issueCategory'), object)) ||
|
|
397
|
+
(store.holds(tracker, ns.wf('issueClass'), object))) {
|
|
390
398
|
ele.appendChild(renderBoard(tracker, object))
|
|
391
399
|
} else {
|
|
392
400
|
throw new Error('Unexpected tab type: ' + object)
|
|
393
401
|
}
|
|
394
402
|
}
|
|
395
|
-
const states =
|
|
403
|
+
const states = store.any(tracker, ns.wf('issueClass'))
|
|
396
404
|
const items = [instancesView, tableView, states]
|
|
397
|
-
.concat(
|
|
405
|
+
.concat(store.each(tracker, ns.wf('issueCategory')))
|
|
398
406
|
items.push(settingsView)
|
|
399
|
-
const selectedTab = tableView
|
|
407
|
+
const selectedTab = tableView.uri
|
|
400
408
|
const options = { renderMain, items, selectedTab }
|
|
401
409
|
|
|
402
410
|
// Add stuff to the ontologies which we believe but they don't say
|
|
403
411
|
const doc = instancesView.doc()
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
412
|
+
store.add(instancesView, ns.rdfs('label'), 'My Trackers', doc) // @@ squatting on wf ns
|
|
413
|
+
store.add(settingsView, ns.rdfs('label'), 'Settings', doc) // @@ squatting on wf ns
|
|
414
|
+
store.add(states, ns.rdfs('label'), 'By State', doc) // @@ squatting on wf ns
|
|
407
415
|
|
|
408
416
|
const tabs = UI.tabs.tabWidget(options)
|
|
409
417
|
return tabs
|
|
@@ -418,17 +426,17 @@ export default {
|
|
|
418
426
|
tracker = subject
|
|
419
427
|
|
|
420
428
|
try {
|
|
421
|
-
await fixSubClasses(
|
|
429
|
+
await fixSubClasses(store, tracker)
|
|
422
430
|
} catch (err) {
|
|
423
431
|
console.log('@@@ Error fixing subclasses in config: ' + err)
|
|
424
432
|
}
|
|
425
433
|
|
|
426
|
-
const states =
|
|
434
|
+
const states = store.any(subject, ns.wf('issueClass'))
|
|
427
435
|
if (!states) throw new Error('This tracker has no issueClass')
|
|
428
|
-
const stateStore =
|
|
436
|
+
const stateStore = store.any(subject, ns.wf('stateStore'))
|
|
429
437
|
if (!stateStore) throw new Error('This tracker has no stateStore')
|
|
430
438
|
|
|
431
|
-
|
|
439
|
+
authn.checkUser() // kick off async operation
|
|
432
440
|
|
|
433
441
|
const h = dom.createElement('h2')
|
|
434
442
|
h.setAttribute('style', 'font-size: 150%')
|
|
@@ -437,10 +445,10 @@ export default {
|
|
|
437
445
|
h.appendChild(dom.createTextNode(classLabel + ' list')) // Use class label @@I18n
|
|
438
446
|
|
|
439
447
|
// New Issue button
|
|
440
|
-
|
|
448
|
+
const b = dom.createElement('button')
|
|
441
449
|
const container = dom.createElement('div')
|
|
442
450
|
b.setAttribute('type', 'button')
|
|
443
|
-
b.setAttribute('style', 'padding: 0.3em; font-size: 100%; margin: 0.5em;')
|
|
451
|
+
b.setAttribute('style', 'padding: 0.3em; font-size: 100%; margin: 0.5em; display: flex; align-items: center;')
|
|
444
452
|
container.appendChild(b)
|
|
445
453
|
paneDiv.appendChild(container)
|
|
446
454
|
const img = dom.createElement('img')
|
|
@@ -454,7 +462,7 @@ export default {
|
|
|
454
462
|
'click',
|
|
455
463
|
function (_event) {
|
|
456
464
|
b.disabled = true
|
|
457
|
-
container.appendChild(newIssueForm(dom,
|
|
465
|
+
container.appendChild(newIssueForm(dom, store, tracker, null, showNewIssue))
|
|
458
466
|
},
|
|
459
467
|
false
|
|
460
468
|
)
|
|
@@ -490,28 +498,28 @@ export default {
|
|
|
490
498
|
const settingsView = ns.wf('SettingsView')
|
|
491
499
|
const instancesView = ns.wf('InstancesView')
|
|
492
500
|
|
|
493
|
-
const updater =
|
|
494
|
-
const t =
|
|
501
|
+
const updater = store.updater
|
|
502
|
+
const t = store.findTypeURIs(subject)
|
|
495
503
|
let tracker
|
|
496
504
|
|
|
497
505
|
// Whatever we are rendering, lets load the ontology
|
|
498
506
|
const flowOntology = UI.ns.wf('').doc()
|
|
499
|
-
if (!
|
|
507
|
+
if (!store.holds(undefined, undefined, undefined, flowOntology)) {
|
|
500
508
|
// If not loaded already
|
|
501
|
-
$rdf.parse(require('./wf.js'),
|
|
509
|
+
$rdf.parse(require('./wf.js'), store, flowOntology.uri, 'text/turtle') // Load ontology directly
|
|
502
510
|
}
|
|
503
511
|
const userInterfaceOntology = UI.ns.ui('').doc()
|
|
504
|
-
if (!
|
|
512
|
+
if (!store.holds(undefined, undefined, undefined, userInterfaceOntology)) {
|
|
505
513
|
// If not loaded already
|
|
506
|
-
$rdf.parse(require('./ui.js'),
|
|
514
|
+
$rdf.parse(require('./ui.js'), store, userInterfaceOntology.uri, 'text/turtle') // Load ontology directly
|
|
507
515
|
}
|
|
508
516
|
|
|
509
517
|
// Render a single issue
|
|
510
518
|
if (
|
|
511
519
|
t['http://www.w3.org/2005/01/wf/flow#Task'] ||
|
|
512
|
-
|
|
520
|
+
store.holds(subject, UI.ns.wf('tracker'))
|
|
513
521
|
) {
|
|
514
|
-
tracker =
|
|
522
|
+
tracker = store.any(subject, ns.wf('tracker'))
|
|
515
523
|
if (!tracker) throw new Error('This issue ' + subject + 'has no tracker')
|
|
516
524
|
|
|
517
525
|
// Much data is in the tracker instance, so wait for the data from it
|
|
@@ -519,7 +527,7 @@ export default {
|
|
|
519
527
|
context.session.store.fetcher
|
|
520
528
|
.load(tracker.doc())
|
|
521
529
|
.then(function (_xhrs) {
|
|
522
|
-
const stateStore =
|
|
530
|
+
const stateStore = store.any(tracker, ns.wf('stateStore'))
|
|
523
531
|
context.session.store.fetcher.nowOrWhenFetched(
|
|
524
532
|
stateStore,
|
|
525
533
|
subject,
|
|
@@ -572,9 +580,7 @@ export default {
|
|
|
572
580
|
overlay.style = OVERFLOW_STYLE
|
|
573
581
|
overlay.style.visibility = 'hidden'
|
|
574
582
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
UI.authn.checkUser().then(webId => {
|
|
583
|
+
authn.checkUser().then(webId => {
|
|
578
584
|
if (webId) {
|
|
579
585
|
console.log('Web ID set already: ' + webId)
|
|
580
586
|
context.me = webId
|
|
@@ -582,9 +588,9 @@ export default {
|
|
|
582
588
|
return
|
|
583
589
|
}
|
|
584
590
|
|
|
585
|
-
loginOutButton = UI.
|
|
591
|
+
loginOutButton = UI.login.loginStatusBox(dom, webIdUri => {
|
|
586
592
|
if (webIdUri) {
|
|
587
|
-
context.me =
|
|
593
|
+
context.me = store.sym(webIdUri)
|
|
588
594
|
console.log('Web ID set from login button: ' + webIdUri)
|
|
589
595
|
paneDiv.removeChild(loginOutButton)
|
|
590
596
|
// enable things
|
package/newIssue.js
CHANGED
|
@@ -19,7 +19,6 @@ export function newIssueForm (dom, kb, tracker, superIssue, showNewIssue) {
|
|
|
19
19
|
titlefield.setAttribute('class', 'pendingedit')
|
|
20
20
|
titlefield.disabled = true
|
|
21
21
|
const sts = []
|
|
22
|
-
let issue
|
|
23
22
|
|
|
24
23
|
const expandTemplate = function (template) {
|
|
25
24
|
const now = new $rdf.Literal(new Date())
|
|
@@ -34,34 +33,30 @@ export function newIssueForm (dom, kb, tracker, superIssue, showNewIssue) {
|
|
|
34
33
|
.replace('{DD}', DD)
|
|
35
34
|
}
|
|
36
35
|
// Where to store the new issue?
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (template) {
|
|
36
|
+
const template = kb.anyValue(tracker, ns.wf('issueURITemplate'))
|
|
37
|
+
const issue = template
|
|
40
38
|
// Does each issue do in its own file?
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
// eslint-disable-next-line prefer-const
|
|
47
|
-
issueDoc = issue.doc()
|
|
39
|
+
? kb.sym(expandTemplate($rdf.uri.join(template, stateStore.uri)))
|
|
40
|
+
: kb.sym(stateStore.uri + '#' + 'Iss' + timestring())
|
|
41
|
+
|
|
42
|
+
const issueDoc = issue.doc()
|
|
48
43
|
|
|
49
44
|
// Basic 9 core predicates are stored in the main stateStore
|
|
50
45
|
|
|
51
46
|
const title = kb.literal(titlefield.value)
|
|
52
47
|
sts.push(new $rdf.Statement(issue, ns.wf('tracker'), tracker, stateStore))
|
|
53
48
|
sts.push(new $rdf.Statement(issue, ns.dc('title'), title, stateStore))
|
|
54
|
-
sts.push(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
for (
|
|
49
|
+
sts.push(new $rdf.Statement(issue, ns.dct('created'), new Date(), stateStore))
|
|
50
|
+
// Copy states from super issue as after all they are subtasks so initially same state same category
|
|
51
|
+
const initialStates = superIssue
|
|
52
|
+
? kb.each(superIssue, ns.rdf('type'), null, superIssue.doc())
|
|
53
|
+
: kb.each(tracker, ns.wf('initialState'))
|
|
54
|
+
for (const state of initialStates) {
|
|
60
55
|
sts.push(
|
|
61
56
|
new $rdf.Statement(
|
|
62
57
|
issue,
|
|
63
58
|
ns.rdf('type'),
|
|
64
|
-
|
|
59
|
+
state,
|
|
65
60
|
stateStore
|
|
66
61
|
)
|
|
67
62
|
)
|
|
@@ -100,7 +95,7 @@ export function newIssueForm (dom, kb, tracker, superIssue, showNewIssue) {
|
|
|
100
95
|
'</h2><p>Title of new ' +
|
|
101
96
|
classLabel +
|
|
102
97
|
':</p>'
|
|
103
|
-
|
|
98
|
+
const titlefield = dom.createElement('input')
|
|
104
99
|
titlefield.setAttribute('type', 'text')
|
|
105
100
|
titlefield.setAttribute(
|
|
106
101
|
'style',
|
|
@@ -119,5 +114,6 @@ export function newIssueForm (dom, kb, tracker, superIssue, showNewIssue) {
|
|
|
119
114
|
false
|
|
120
115
|
)
|
|
121
116
|
form.appendChild(titlefield)
|
|
117
|
+
titlefield.focus() // we want user cursor here
|
|
122
118
|
return form
|
|
123
119
|
}
|
package/newTracker.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as UI from 'solid-ui'
|
|
2
|
+
import { store } from 'solid-logic'
|
|
2
3
|
|
|
3
4
|
const $rdf = UI.rdf
|
|
4
5
|
const ns = UI.ns
|
|
5
|
-
const
|
|
6
|
-
const updater = kb.updater
|
|
6
|
+
const updater = store.updater
|
|
7
7
|
|
|
8
8
|
/* Button for making a whole new tracker
|
|
9
9
|
** This is the least tesetd part of the tracker system at the moment.
|
|
@@ -16,17 +16,30 @@ export function newTrackerButton (thisTracker, context) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// const dom = context.dom
|
|
19
|
-
const button = UI.
|
|
19
|
+
const button = UI.login.newAppInstance(context.dom, { noun: 'tracker' }, function (
|
|
20
20
|
ws,
|
|
21
21
|
base
|
|
22
22
|
) {
|
|
23
|
+
function morph (x) {
|
|
24
|
+
// Move any URIs in this space into that space
|
|
25
|
+
if (x.elements !== undefined) return x.elements.map(morph) // Morph within lists
|
|
26
|
+
if (x.uri === undefined) return x
|
|
27
|
+
let u = x.uri
|
|
28
|
+
if (u === stateStore.uri) return newStore // special case
|
|
29
|
+
if (u.slice(0, oldBase.length) === oldBase) {
|
|
30
|
+
u = base + u.slice(oldBase.length)
|
|
31
|
+
$rdf.log.debug(' Map ' + x.uri + ' to ' + u)
|
|
32
|
+
}
|
|
33
|
+
return store.sym(u)
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
const appPathSegment = 'issuetracker.w3.org' // how to allocate this string and connect to
|
|
24
37
|
// console.log("Ready to make new instance at "+ws)
|
|
25
38
|
const sp = UI.ns.space
|
|
26
|
-
const
|
|
39
|
+
const store = context.session.store
|
|
27
40
|
|
|
28
41
|
if (!base) {
|
|
29
|
-
base =
|
|
42
|
+
base = store.any(ws, sp('uriPrefix')).value
|
|
30
43
|
if (base.slice(-1) !== '/') {
|
|
31
44
|
$rdf.log.error(
|
|
32
45
|
appPathSegment + ': No / at end of uriPrefix ' + base
|
|
@@ -39,29 +52,17 @@ export function newTrackerButton (thisTracker, context) {
|
|
|
39
52
|
}
|
|
40
53
|
}
|
|
41
54
|
|
|
42
|
-
const stateStore =
|
|
43
|
-
const newStore =
|
|
55
|
+
const stateStore = store.any(thisTracker, ns.wf('stateStore'))
|
|
56
|
+
const newStore = store.sym(base + 'store.ttl')
|
|
44
57
|
|
|
45
58
|
const here = thisTracker.doc()
|
|
46
59
|
|
|
47
60
|
const oldBase = here.uri.slice(0, here.uri.lastIndexOf('/') + 1)
|
|
48
61
|
|
|
49
|
-
var morph = function (x) {
|
|
50
|
-
// Move any URIs in this space into that space
|
|
51
|
-
if (x.elements !== undefined) return x.elements.map(morph) // Morph within lists
|
|
52
|
-
if (x.uri === undefined) return x
|
|
53
|
-
let u = x.uri
|
|
54
|
-
if (u === stateStore.uri) return newStore // special case
|
|
55
|
-
if (u.slice(0, oldBase.length) === oldBase) {
|
|
56
|
-
u = base + u.slice(oldBase.length)
|
|
57
|
-
$rdf.log.debug(' Map ' + x.uri + ' to ' + u)
|
|
58
|
-
}
|
|
59
|
-
return kb.sym(u)
|
|
60
|
-
}
|
|
61
62
|
const there = morph(here)
|
|
62
63
|
const newTracker = morph(thisTracker)
|
|
63
64
|
|
|
64
|
-
const myConfig =
|
|
65
|
+
const myConfig = store.statementsMatching(
|
|
65
66
|
undefined,
|
|
66
67
|
undefined,
|
|
67
68
|
undefined,
|
|
@@ -69,7 +70,7 @@ export function newTrackerButton (thisTracker, context) {
|
|
|
69
70
|
)
|
|
70
71
|
for (let i = 0; i < myConfig.length; i++) {
|
|
71
72
|
const st = myConfig[i]
|
|
72
|
-
|
|
73
|
+
store.add(
|
|
73
74
|
morph(st.subject),
|
|
74
75
|
morph(st.predicate),
|
|
75
76
|
morph(st.object),
|
|
@@ -79,15 +80,15 @@ export function newTrackerButton (thisTracker, context) {
|
|
|
79
80
|
|
|
80
81
|
// Keep a paper trail @@ Revisit when we have non-public ones @@ Privacy
|
|
81
82
|
//
|
|
82
|
-
|
|
83
|
+
store.add(newTracker, UI.ns.space('inspiration'), thisTracker, stateStore)
|
|
83
84
|
|
|
84
|
-
|
|
85
|
+
store.add(newTracker, UI.ns.space('inspiration'), thisTracker, there)
|
|
85
86
|
|
|
86
|
-
// $rdf.log.debug("\n Ready to put " +
|
|
87
|
+
// $rdf.log.debug("\n Ready to put " + store.statementsMatching(undefined, undefined, undefined, there)); //@@
|
|
87
88
|
|
|
88
89
|
updater.put(
|
|
89
90
|
there,
|
|
90
|
-
|
|
91
|
+
store.statementsMatching(undefined, undefined, undefined, there),
|
|
91
92
|
'text/turtle',
|
|
92
93
|
function (uri2, ok, message) {
|
|
93
94
|
if (ok) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "issue-pane",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.10-0fa14c64",
|
|
4
4
|
"description": "Solid-compatible Panes: issue editor",
|
|
5
5
|
"main": "./issuePane.js",
|
|
6
6
|
"scripts": {
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"lint": "eslint '*.js'",
|
|
9
9
|
"lint-fix": "eslint '*.js' --fix",
|
|
10
10
|
"test": "npm run lint",
|
|
11
|
-
"prepublishOnly": "npm test",
|
|
12
|
-
"postpublish": "git push origin main --follow-tags"
|
|
11
|
+
"ignore:prepublishOnly": "npm test",
|
|
12
|
+
"ignore:postpublish": "git push origin main --follow-tags"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -34,15 +34,16 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://github.com/solid/issue-pane",
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"pane-registry": "^2.4.
|
|
38
|
-
"rdflib": "^2.2.
|
|
39
|
-
"solid-
|
|
37
|
+
"pane-registry": "^2.4.7",
|
|
38
|
+
"rdflib": "^2.2.18",
|
|
39
|
+
"solid-logic": "^1.3.14",
|
|
40
|
+
"solid-ui": "^2.4.19"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
|
-
"eslint": "^
|
|
43
|
+
"eslint": "^8.11.0",
|
|
44
|
+
"eslint-plugin-import": "^2.25.4",
|
|
43
45
|
"husky": "^7.0.4",
|
|
44
|
-
"lint-staged": "^
|
|
45
|
-
"standard": "^16.0.4"
|
|
46
|
+
"lint-staged": "^12.3.7"
|
|
46
47
|
},
|
|
47
48
|
"husky": {
|
|
48
49
|
"hooks": {
|
package/tbl-bug-22.png
CHANGED
|
File without changes
|
package/trackerSettingsForm.js
CHANGED
|
File without changes
|
package/trackerSettingsForm.ttl
CHANGED
|
File without changes
|
package/ui.js
CHANGED
|
File without changes
|
package/ui.ttl
CHANGED
|
File without changes
|
package/wf.js
CHANGED
|
File without changes
|
package/wf.ttl
CHANGED
|
File without changes
|