issue-pane 2.6.1 → 3.0.0-a58a4367
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/README.md +13 -0
- package/dist/board.js +1 -0
- package/dist/csvButton.js +1 -0
- package/dist/issue.js +1 -0
- package/dist/issuePane.js +1 -0
- package/dist/newIssue.js +1 -0
- package/dist/newTracker.js +1 -0
- package/package.json +31 -27
- package/Documentation/Configuration.html +0 -282
- package/Documentation/Configuration.md +0 -245
- package/Documentation/FooTracker/chat.ttl +0 -0
- package/Documentation/FooTracker/index.ttl +0 -84
- package/Documentation/FooTracker/state.ttl +0 -0
- package/Documentation/Makefile +0 -11
- package/Documentation/github-markdown.css +0 -964
- package/Makefile +0 -34
- package/babel.config.js +0 -5
- package/board.js +0 -97
- package/csvButton.js +0 -135
- package/dev/context.js +0 -18
- package/dev/index.html +0 -47
- package/dev/index.js +0 -42
- package/dist/big-tracker.ttl +0 -238
- package/dist/ecosystem-roadmap.ttl +0 -78
- package/dist/state-big-tracker.ttl +0 -64
- package/eslint.config.mjs +0 -32
- package/issue.js +0 -506
- package/issuePane.js +0 -594
- package/newIssue.js +0 -118
- package/newTracker.js +0 -132
- package/tbl-bug-22.png +0 -0
- package/test/big-tracker.ttl +0 -237
- package/test/ecosystem-roadmap.ttl +0 -78
- package/trackerSettingsForm.js +0 -170
- package/trackerSettingsForm.ttl +0 -168
- package/ui.js +0 -310
- package/ui.ttl +0 -308
- package/webpack.dev.config.js +0 -36
- package/wf.js +0 -370
- package/wf.ttl +0 -368
package/issuePane.js
DELETED
|
@@ -1,594 +0,0 @@
|
|
|
1
|
-
/* Issue Tracker Pane
|
|
2
|
-
**
|
|
3
|
-
** This solid view allows a user to interact with an issue tracker, or individual issue,
|
|
4
|
-
** to change its state according to an ontology, comment on it, etc.
|
|
5
|
-
**
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { create, login, ns, icons, rdf, tabs, table, utils, widgets } from 'solid-ui'
|
|
9
|
-
import { store, authn } from 'solid-logic'
|
|
10
|
-
import { board } from './board' // @@ will later be in solid-UI
|
|
11
|
-
import { renderIssue, renderIssueCard, getState, exposeOverlay } from './issue'
|
|
12
|
-
import { newTrackerButton } from './newTracker'
|
|
13
|
-
import { newIssueForm } from './newIssue'
|
|
14
|
-
import { csvButton } from './csvButton'
|
|
15
|
-
import { trackerSettingsFormText } from './trackerSettingsForm.js'
|
|
16
|
-
// import { trackerInstancesFormText } from './trackerInstancesForm.js'
|
|
17
|
-
|
|
18
|
-
const $rdf = rdf
|
|
19
|
-
const kb = store
|
|
20
|
-
|
|
21
|
-
// const MY_TRACKERS_ICON = UI.icons.iconBase + 'noun_Document_998605.svg'
|
|
22
|
-
// const TRACKER_ICON = UI.icons.iconBase + 'noun_list_638112'
|
|
23
|
-
// const TASK_ICON = UI.icons.iconBase + 'noun_17020.svg'
|
|
24
|
-
|
|
25
|
-
const OVERFLOW_STYLE = 'position: fixed; z-index: 100; top: 1.51em; right: 2em; left: 2em; bottom:1.5em; border: 0.1em grey; overflow: scroll;'
|
|
26
|
-
export default {
|
|
27
|
-
icon: icons.iconBase + 'noun_122196.svg', // was: js/panes/issue/tbl-bug-22.png
|
|
28
|
-
// noun_list_638112 is a checklist document
|
|
29
|
-
// noun_Document_998605.svg is a stack of twpo checklists
|
|
30
|
-
// noun_97839.svg is a ladybug
|
|
31
|
-
// noun_122196.svg is a clipboard with a check list on it
|
|
32
|
-
// noun_17020.svg is a single check box
|
|
33
|
-
name: 'issue',
|
|
34
|
-
|
|
35
|
-
audience: [], // Anyone. was [ns.solid('PowerUser')]
|
|
36
|
-
|
|
37
|
-
// Does the subject deserve an issue pane?
|
|
38
|
-
label: function (subject, _context) {
|
|
39
|
-
const t = kb.findTypeURIs(subject)
|
|
40
|
-
if (
|
|
41
|
-
t['http://www.w3.org/2005/01/wf/flow#Task'] ||
|
|
42
|
-
kb.holds(subject, ns.wf('tracker'))
|
|
43
|
-
) { return 'issue' } // in case ontology not available
|
|
44
|
-
if (t['http://www.w3.org/2005/01/wf/flow#Tracker']) return 'tracker'
|
|
45
|
-
// Later: Person. For a list of things assigned to them,
|
|
46
|
-
// open bugs on projects they are developer on, etc
|
|
47
|
-
return null // No under other circumstances (while testing at least!)
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
mintClass: ns.wf('Tracker'),
|
|
51
|
-
|
|
52
|
-
mintNew: async function (context, options) {
|
|
53
|
-
/** Perform updates on more than one document @@ Move to rdflib!
|
|
54
|
-
*/
|
|
55
|
-
async function updateMany (deletions, insertions) {
|
|
56
|
-
const docs = deletions.concat(insertions).map(st => st.why)
|
|
57
|
-
const uniqueDocs = Array.from(new Set(docs))
|
|
58
|
-
const updates = uniqueDocs.map(doc =>
|
|
59
|
-
kb.updater.update(deletions.filter(st => st.why.sameTerm(doc)),
|
|
60
|
-
insertions.filter(st => st.why.sameTerm(doc))))
|
|
61
|
-
return Promise.all(updates)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const kb = context.session.store
|
|
65
|
-
let stateStore
|
|
66
|
-
if (options.newInstance) {
|
|
67
|
-
stateStore = kb.sym(options.newInstance.doc().uri + '_state.ttl')
|
|
68
|
-
} else {
|
|
69
|
-
options.newInstance = kb.sym(options.newBase + 'index.ttl#this')
|
|
70
|
-
stateStore = kb.sym(options.newBase + 'state.ttl')
|
|
71
|
-
}
|
|
72
|
-
const tracker = options.newInstance
|
|
73
|
-
const appDoc = tracker.doc()
|
|
74
|
-
|
|
75
|
-
const me = authn.currentUser()
|
|
76
|
-
if (me) {
|
|
77
|
-
kb.add(tracker, ns.dc('author'), me, appDoc)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
kb.add(tracker, ns.rdf('type'), ns.wf('Tracker'), appDoc)
|
|
81
|
-
kb.add(tracker, ns.dc('created'), new Date(), appDoc)
|
|
82
|
-
|
|
83
|
-
// @@ to do --- adk user what sort of tracker they want
|
|
84
|
-
|
|
85
|
-
kb.add(tracker, ns.wf('issueClass'), ns.wf('Task'), appDoc) // @@ ask user
|
|
86
|
-
kb.add(tracker, ns.wf('initialState'), ns.wf('Open'), appDoc)
|
|
87
|
-
kb.add(tracker, ns.wf('stateStore'), stateStore, appDoc)
|
|
88
|
-
kb.add(tracker, ns.wf('assigneeClass'), ns.foaf('Person'), appDoc) // @@ set to people in the meeting?
|
|
89
|
-
|
|
90
|
-
kb.add(tracker, ns.wf('stateStore'), stateStore, stateStore) // Back Link
|
|
91
|
-
|
|
92
|
-
const ins = kb.statementsMatching(undefined, undefined, undefined, appDoc).concat(kb.statementsMatching(undefined, undefined, undefined, stateStore))
|
|
93
|
-
try {
|
|
94
|
-
await updateMany([], ins)
|
|
95
|
-
} catch (err) {
|
|
96
|
-
return widgets.complain(context, 'Error writing tracker configuration: ' + err)
|
|
97
|
-
}
|
|
98
|
-
/*
|
|
99
|
-
try {
|
|
100
|
-
await kb.updater.updateMany([], kb.statementsMatching(undefined, undefined, undefined, stateStore))
|
|
101
|
-
} catch (err) {
|
|
102
|
-
return widgets.complain(context, 'Error writing tracker state file: ' + err)
|
|
103
|
-
}
|
|
104
|
-
*/
|
|
105
|
-
const dom = context.dom
|
|
106
|
-
const div = options.div
|
|
107
|
-
|
|
108
|
-
const notice = div.appendChild(dom.createElement('div'))
|
|
109
|
-
notice.innerHTML = `<h4>Success</h4>
|
|
110
|
-
<p>Your <a href="${tracker.uri}">new tracker</a> has been made.
|
|
111
|
-
Use the settings tab to configure it.
|
|
112
|
-
</p>
|
|
113
|
-
`
|
|
114
|
-
// console.log('New tracker created ' + tracker)
|
|
115
|
-
// alert('New tracker created')
|
|
116
|
-
return options
|
|
117
|
-
},
|
|
118
|
-
|
|
119
|
-
render: function (subject, context) {
|
|
120
|
-
const dom = context.dom
|
|
121
|
-
|
|
122
|
-
const paneDiv = dom.createElement('div')
|
|
123
|
-
context.paneDiv = paneDiv
|
|
124
|
-
paneDiv.setAttribute('class', 'issuePane')
|
|
125
|
-
|
|
126
|
-
function complain (message) {
|
|
127
|
-
console.warn(message)
|
|
128
|
-
paneDiv.appendChild(widgets.errorMessageBlock(dom, message))
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function complainIfBad (ok, message) {
|
|
132
|
-
if (!ok) {
|
|
133
|
-
complain(message)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/** Infer subclass from disjoint Union
|
|
138
|
-
**
|
|
139
|
-
** This is would not be needed if our quey language
|
|
140
|
-
** allowed is to query ardf Collection membership.
|
|
141
|
-
*/
|
|
142
|
-
async function fixSubClasses (kb, tracker) { // 20220228
|
|
143
|
-
async function checkOneSuperclass (klass) {
|
|
144
|
-
const collection = kb.any(klass, ns.owl('disjointUnionOf'), null, doc)
|
|
145
|
-
if (!collection) throw new Error(`Classification ${klass} has no disjointUnionOf`)
|
|
146
|
-
if (!collection.elements) throw new Error(`Classification ${klass} has no array`)
|
|
147
|
-
const needed = new Set(collection.elements.map(x => x.uri))
|
|
148
|
-
const existing = new Set(kb.each(null, ns.rdfs('subClassOf'), klass, doc).map(x => x.uri))
|
|
149
|
-
|
|
150
|
-
const superfluous = [...existing].filter(sub => !needed.has(sub))
|
|
151
|
-
const deleteActions = superfluous.map(sub => { return { action: 'delete', st: $rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, doc) } })
|
|
152
|
-
|
|
153
|
-
const missing = [...needed].filter(sub => !existing.has(sub))
|
|
154
|
-
const insertActions = missing.map(sub => { return { action: 'insert', st: $rdf.st(kb.sym(sub), ns.rdfs('subClassOf'), klass, doc) } })
|
|
155
|
-
return deleteActions.concat(insertActions)
|
|
156
|
-
}
|
|
157
|
-
const doc = tracker.doc()
|
|
158
|
-
const states = kb.any(tracker, ns.wf('issueClass'))
|
|
159
|
-
const cats = kb.each(tracker, ns.wf('issueCategory')).concat([states])
|
|
160
|
-
let damage = [] // to make totally functionaly need to deal with map over async.
|
|
161
|
-
for (const klass of cats) {
|
|
162
|
-
damage = damage.concat(await checkOneSuperclass(klass))
|
|
163
|
-
}
|
|
164
|
-
if (damage.length) {
|
|
165
|
-
const insertables = damage.filter(fix => fix.action === 'insert').map(fix => fix.st)
|
|
166
|
-
const deletables = damage.filter(fix => fix.action === 'delete').map(fix => fix.st)
|
|
167
|
-
// alert(`Internal error: s${damage} subclasses inconsistences!`)
|
|
168
|
-
console.log('Damage:', damage)
|
|
169
|
-
if (confirm(`Fix ${damage} inconsistent subclasses in tracker config?`)) {
|
|
170
|
-
await kb.updater.update(deletables, insertables)
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/** /////////////////////////// Board
|
|
176
|
-
*/
|
|
177
|
-
function renderBoard (tracker, klass) {
|
|
178
|
-
const states = kb.any(tracker, ns.wf('issueClass'))
|
|
179
|
-
klass = klass || states // default to states
|
|
180
|
-
const doingStates = klass.sameTerm(states)
|
|
181
|
-
|
|
182
|
-
// These are states we will show by default: the open issues.
|
|
183
|
-
const stateArray = kb.any(klass, ns.owl('disjointUnionOf'))
|
|
184
|
-
if (!stateArray) {
|
|
185
|
-
return complain(`Configuration error: state ${states} does not have substates`)
|
|
186
|
-
}
|
|
187
|
-
let columnValues = stateArray.elements
|
|
188
|
-
if (doingStates && columnValues.length > 2 // and there are more than two
|
|
189
|
-
) { // strip out closed states
|
|
190
|
-
columnValues = columnValues.filter(state => kb.holds(state, ns.rdfs('subClassOf'), ns.wf('Open')) || state.sameTerm(ns.wf('Open')))
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async function columnDropHandler (issue, newState) {
|
|
194
|
-
const currentState = getState(issue, klass)
|
|
195
|
-
const tracker = kb.the(issue, ns.wf('tracker'), null, issue.doc())
|
|
196
|
-
const stateStore = kb.any(tracker, ns.wf('stateStore'))
|
|
197
|
-
|
|
198
|
-
if (newState.sameTerm(currentState)) {
|
|
199
|
-
// alert('Same state ' + utils.label(currentState)) // @@ remove
|
|
200
|
-
return
|
|
201
|
-
}
|
|
202
|
-
try {
|
|
203
|
-
await kb.updater.update(
|
|
204
|
-
[$rdf.st(issue, ns.rdf('type'), currentState, stateStore)],
|
|
205
|
-
[$rdf.st(issue, ns.rdf('type'), newState, stateStore)])
|
|
206
|
-
} catch (err) {
|
|
207
|
-
widgets.complain(context, 'Unable to change issue state: ' + err)
|
|
208
|
-
}
|
|
209
|
-
boardDiv.refresh() // reorganize board to match the new reality
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function isOpen (issue) {
|
|
213
|
-
const types = kb.findTypeURIs(issue)
|
|
214
|
-
return !!types[ns.wf('Open').uri]
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const options = { columnDropHandler, filter: doingStates ? null : isOpen }
|
|
218
|
-
options.sortBy = ns.dct('created')
|
|
219
|
-
options.sortReverse = true
|
|
220
|
-
function localRenderIssueCard (issue) {
|
|
221
|
-
return renderIssueCard(issue, context)
|
|
222
|
-
}
|
|
223
|
-
// const columnValues = states // @@ optionally selected states would work
|
|
224
|
-
const boardDiv = board(dom, columnValues, localRenderIssueCard, options)
|
|
225
|
-
return boardDiv
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/** ////////////// Table
|
|
229
|
-
*/
|
|
230
|
-
function tableRefreshButton (stateStore, tableDiv) {
|
|
231
|
-
const refreshButton = widgets.button(dom, icons.iconBase + 'noun_479395.svg',
|
|
232
|
-
'refresh table', async _event => {
|
|
233
|
-
try {
|
|
234
|
-
await kb.fetcher.load(stateStore, { force: true, clearPreviousData: true })
|
|
235
|
-
} catch (err) {
|
|
236
|
-
alert(err)
|
|
237
|
-
return
|
|
238
|
-
}
|
|
239
|
-
widgets.refreshTree(tableDiv)
|
|
240
|
-
})
|
|
241
|
-
return refreshButton
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function renderTable (tracker) {
|
|
245
|
-
function newOptionalClause () {
|
|
246
|
-
const clause = new $rdf.IndexedFormula()
|
|
247
|
-
query.pat.optional.push(clause)
|
|
248
|
-
return clause
|
|
249
|
-
}
|
|
250
|
-
const states = kb.any(subject, ns.wf('issueClass'))
|
|
251
|
-
const cats = kb.each(tracker, ns.wf('issueCategory')) // zero or more
|
|
252
|
-
const vars = ['issue', 'state', 'created']
|
|
253
|
-
const query = new $rdf.Query(utils.label(subject))
|
|
254
|
-
for (let i = 0; i < cats.length; i++) {
|
|
255
|
-
vars.push('_cat_' + i)
|
|
256
|
-
}
|
|
257
|
-
const v = {} // The RDF variable objects for each variable name
|
|
258
|
-
vars.forEach(function (x) {
|
|
259
|
-
query.vars.push((v[x] = $rdf.variable(x)))
|
|
260
|
-
})
|
|
261
|
-
query.pat.add(v.issue, ns.wf('tracker'), tracker)
|
|
262
|
-
// query.pat.add(v['issue'], ns.dc('title'), v['title'])
|
|
263
|
-
query.pat.add(v.issue, ns.dct('created'), v.created)
|
|
264
|
-
query.pat.add(v.issue, ns.rdf('type'), v.state)
|
|
265
|
-
query.pat.add(v.state, ns.rdfs('subClassOf'), states)
|
|
266
|
-
|
|
267
|
-
query.pat.optional = []
|
|
268
|
-
|
|
269
|
-
for (let i = 0; i < cats.length; i++) {
|
|
270
|
-
const clause = newOptionalClause()
|
|
271
|
-
clause.add(v.issue, ns.rdf('type'), v['_cat_' + i])
|
|
272
|
-
clause.add(v['_cat_' + i], ns.rdfs('subClassOf'), cats[i])
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const propertyList = kb.any(tracker, ns.wf('propertyList')) // List of extra properties
|
|
276
|
-
if (propertyList) {
|
|
277
|
-
const properties = propertyList.elements
|
|
278
|
-
for (let p = 0; p < properties.length; p++) {
|
|
279
|
-
const prop = properties[p]
|
|
280
|
-
let vname = '_prop_' + p
|
|
281
|
-
if (prop.uri.indexOf('#') >= 0) {
|
|
282
|
-
vname = prop.uri.split('#')[1]
|
|
283
|
-
}
|
|
284
|
-
const oneOpt = newOptionalClause()
|
|
285
|
-
query.vars.push((v[vname] = $rdf.variable(vname)))
|
|
286
|
-
oneOpt.add(v.issue, prop, v[vname])
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const selectedStates = {}
|
|
291
|
-
const possible = kb.each(undefined, ns.rdfs('subClassOf'), states)
|
|
292
|
-
possible.forEach(function (s) {
|
|
293
|
-
if (
|
|
294
|
-
kb.holds(s, ns.rdfs('subClassOf'), ns.wf('Open')) ||
|
|
295
|
-
s.sameTerm(ns.wf('Open'))
|
|
296
|
-
) {
|
|
297
|
-
selectedStates[s.uri] = true
|
|
298
|
-
// console.log('on '+s.uri); // @@
|
|
299
|
-
}
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
function exposeThisOverlay (href) {
|
|
303
|
-
const subject = $rdf.sym(href)
|
|
304
|
-
exposeOverlay(subject, context)
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const tableDiv = table(dom, {
|
|
308
|
-
query: query,
|
|
309
|
-
keyVariable: '?issue', // Charactersic of row
|
|
310
|
-
sortBy: '?created', // By default, sort by date
|
|
311
|
-
sortReverse: true, // most recent at the top
|
|
312
|
-
hints: {
|
|
313
|
-
'?issue': { linkFunction: exposeThisOverlay, label: 'Title' },
|
|
314
|
-
'?created': { cellFormat: 'shortDate' },
|
|
315
|
-
'?state': { initialSelection: selectedStates, label: 'Status' }
|
|
316
|
-
}
|
|
317
|
-
})
|
|
318
|
-
const stateStore = kb.any(subject, ns.wf('stateStore'))
|
|
319
|
-
tableDiv.appendChild(tableRefreshButton(stateStore, tableDiv))
|
|
320
|
-
return tableDiv
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Allow user to create new things within the folder
|
|
324
|
-
function renderCreationControl (refreshTarget) {
|
|
325
|
-
const creationDiv = dom.createElement('div')
|
|
326
|
-
const me = authn.currentUser()
|
|
327
|
-
const creationContext = {
|
|
328
|
-
// folder: subject,
|
|
329
|
-
div: creationDiv,
|
|
330
|
-
dom: dom,
|
|
331
|
-
noun: 'tracker',
|
|
332
|
-
statusArea: creationDiv,
|
|
333
|
-
me: me,
|
|
334
|
-
refreshTarget: refreshTarget
|
|
335
|
-
}
|
|
336
|
-
const issuePane = context.session.paneRegistry.byName('issue')
|
|
337
|
-
const relevantPanes = [issuePane]
|
|
338
|
-
create.newThingUI(creationContext, context, relevantPanes) // Have to pass panes down newUI
|
|
339
|
-
return creationDiv
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function renderInstances (theClass) {
|
|
343
|
-
const instancesDiv = dom.createElement('div')
|
|
344
|
-
const context = { dom, div: instancesDiv, noun: 'tracker' }
|
|
345
|
-
login.registrationList(context, { public: true, private: true, type: theClass }).then(_context2 => {
|
|
346
|
-
instancesDiv.appendChild(renderCreationControl(instancesDiv))
|
|
347
|
-
/* // keep this code in case we need a form
|
|
348
|
-
const InstancesForm = ns.wf('TrackerInstancesForm')
|
|
349
|
-
const text = trackerInstancesFormText
|
|
350
|
-
$rdf.parse(text, kb, InstancesForm.doc().uri, 'text/turtle')
|
|
351
|
-
widgets.appendForm(dom, instancesDiv, {}, tracker, InstancesForm,
|
|
352
|
-
tracker.doc(), complainIfBad)
|
|
353
|
-
*/
|
|
354
|
-
})
|
|
355
|
-
return instancesDiv
|
|
356
|
-
}
|
|
357
|
-
function renderSettings (tracker) {
|
|
358
|
-
const settingsDiv = dom.createElement('div')
|
|
359
|
-
settingsDiv.appendChild(csvButton(dom, tracker)) // Button to copy the tracker as a CSV file
|
|
360
|
-
const states = kb.any(tracker, ns.wf('issueClass'))
|
|
361
|
-
const views = [tableView, states] // Possible default views
|
|
362
|
-
.concat(kb.each(tracker, ns.wf('issueCategory')))
|
|
363
|
-
const box = settingsDiv.appendChild(dom.createElement('div'))
|
|
364
|
-
const lhs = widgets.renderNameValuePair(dom, kb, box, null, 'Default view') // @@ use a predicate?
|
|
365
|
-
lhs.appendChild(widgets.makeSelectForOptions(dom, kb, tracker,
|
|
366
|
-
ns.wf('defaultView'),
|
|
367
|
-
views, {}, tracker.doc()))
|
|
368
|
-
|
|
369
|
-
// A registration control allows the to record this tracker in their type index
|
|
370
|
-
const context = { dom, div: settingsDiv, noun: 'tracker' }
|
|
371
|
-
login.registrationControl(context, tracker, ns.wf('Tracker')).then(_context2 => {
|
|
372
|
-
const settingsForm = ns.wf('TrackerSettingsForm')
|
|
373
|
-
const text = trackerSettingsFormText
|
|
374
|
-
$rdf.parse(text, kb, settingsForm.doc().uri, 'text/turtle')
|
|
375
|
-
widgets.appendForm(dom, settingsDiv, {}, tracker, settingsForm,
|
|
376
|
-
tracker.doc(), complainIfBad)
|
|
377
|
-
})
|
|
378
|
-
return settingsDiv
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
function renderTabsTableAndBoard () {
|
|
382
|
-
function renderMain (ele, object) {
|
|
383
|
-
ele.innerHTML = '' // Clear out "loading message"
|
|
384
|
-
if (object.sameTerm(boardView)) {
|
|
385
|
-
ele.appendChild(renderBoard(tracker))
|
|
386
|
-
} else if (object.sameTerm(tableView)) {
|
|
387
|
-
ele.appendChild(renderTable(tracker))
|
|
388
|
-
} else if (object.sameTerm(settingsView)) {
|
|
389
|
-
ele.appendChild(renderSettings(tracker))
|
|
390
|
-
} else if (object.sameTerm(instancesView)) {
|
|
391
|
-
ele.appendChild(renderInstances(ns.wf('Tracker')))
|
|
392
|
-
} else if ((kb.holds(tracker, ns.wf('issueCategory'), object)) ||
|
|
393
|
-
(kb.holds(tracker, ns.wf('issueClass'), object))) {
|
|
394
|
-
ele.appendChild(renderBoard(tracker, object))
|
|
395
|
-
} else {
|
|
396
|
-
throw new Error('Unexpected tab type: ' + object)
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
const states = kb.any(tracker, ns.wf('issueClass'))
|
|
400
|
-
const items = [instancesView, tableView, states]
|
|
401
|
-
.concat(kb.each(tracker, ns.wf('issueCategory')))
|
|
402
|
-
items.push(settingsView)
|
|
403
|
-
const selectedTab = kb.any(tracker, ns.wf('defaultView'), null, tracker.doc()) || tableView
|
|
404
|
-
const options = { renderMain, items, selectedTab }
|
|
405
|
-
|
|
406
|
-
// Add stuff to the ontologies which we believe but they don't say
|
|
407
|
-
const doc = instancesView.doc()
|
|
408
|
-
kb.add(instancesView, ns.rdfs('label'), 'My Trackers', doc) // @@ squatting on wf ns
|
|
409
|
-
kb.add(settingsView, ns.rdfs('label'), 'Settings', doc) // @@ squatting on wf ns
|
|
410
|
-
kb.add(states, ns.rdfs('label'), 'By State', doc) // @@ squatting on wf ns
|
|
411
|
-
|
|
412
|
-
const myTabs = tabs.tabWidget(options)
|
|
413
|
-
return myTabs
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
async function renderSingleIssue () {
|
|
417
|
-
tracker = kb.any(subject, ns.wf('tracker'))
|
|
418
|
-
if (!tracker) throw new Error('This issue ' + subject + 'has no tracker')
|
|
419
|
-
|
|
420
|
-
// Much data is in the tracker instance, so wait for the data from it
|
|
421
|
-
try {
|
|
422
|
-
const _xhrs = await context.session.store.fetcher.load(tracker.doc())
|
|
423
|
-
} catch (err) {
|
|
424
|
-
const msg = 'Failed to load tracker config ' + tracker.doc() + ': ' + err
|
|
425
|
-
return complain(msg)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const stateStore = kb.any(tracker, ns.wf('stateStore'))
|
|
429
|
-
if (!stateStore) {
|
|
430
|
-
return complain('Tracker has no state store: ' + tracker)
|
|
431
|
-
}
|
|
432
|
-
try {
|
|
433
|
-
await context.session.store.fetcher.load(subject)
|
|
434
|
-
} catch (err) {
|
|
435
|
-
return complain('Failed to load issue state ' + stateStore + ': ' + err)
|
|
436
|
-
}
|
|
437
|
-
paneDiv.appendChild(renderIssue(subject, context))
|
|
438
|
-
updater.addDownstreamChangeListener(stateStore, function () {
|
|
439
|
-
widgets.refreshTree(paneDiv)
|
|
440
|
-
}) // Live update
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
async function renderTracker () {
|
|
444
|
-
function showNewIssue (issue) {
|
|
445
|
-
widgets.refreshTree(paneDiv)
|
|
446
|
-
exposeOverlay(issue, context)
|
|
447
|
-
newIssueButton.disabled = false // https://stackoverflow.com/questions/41176582/enable-disable-a-button-in-pure-javascript
|
|
448
|
-
}
|
|
449
|
-
tracker = subject
|
|
450
|
-
|
|
451
|
-
try {
|
|
452
|
-
await fixSubClasses(kb, tracker)
|
|
453
|
-
} catch (err) {
|
|
454
|
-
console.log('@@@ Error fixing subclasses in config: ' + err)
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const states = kb.any(subject, ns.wf('issueClass'))
|
|
458
|
-
if (!states) throw new Error('This tracker has no issueClass')
|
|
459
|
-
const stateStore = kb.any(subject, ns.wf('stateStore'))
|
|
460
|
-
if (!stateStore) throw new Error('This tracker has no stateStore')
|
|
461
|
-
|
|
462
|
-
// const me = await authn.currentUser()
|
|
463
|
-
|
|
464
|
-
const h = dom.createElement('h2')
|
|
465
|
-
h.setAttribute('style', 'font-size: 150%')
|
|
466
|
-
paneDiv.appendChild(h)
|
|
467
|
-
const classLabel = utils.label(states)
|
|
468
|
-
h.appendChild(dom.createTextNode(classLabel + ' list')) // Use class label @@I18n
|
|
469
|
-
|
|
470
|
-
// New Issue button
|
|
471
|
-
const newIssueButton = dom.createElement('button')
|
|
472
|
-
const container = dom.createElement('div')
|
|
473
|
-
newIssueButton.setAttribute('type', 'button')
|
|
474
|
-
newIssueButton.setAttribute('style', 'padding: 0.3em; font-size: 100%; margin: 0.5em;')
|
|
475
|
-
container.appendChild(newIssueButton)
|
|
476
|
-
paneDiv.appendChild(container)
|
|
477
|
-
const img = dom.createElement('img')
|
|
478
|
-
img.setAttribute('src', icons.iconBase + 'noun_19460_green.svg')
|
|
479
|
-
img.setAttribute('style', 'width: 1em; height: 1em; margin: 0.2em;')
|
|
480
|
-
newIssueButton.appendChild(img)
|
|
481
|
-
const span = dom.createElement('span')
|
|
482
|
-
span.innerHTML = 'New ' + classLabel
|
|
483
|
-
newIssueButton.appendChild(span)
|
|
484
|
-
newIssueButton.addEventListener(
|
|
485
|
-
'click',
|
|
486
|
-
function (_event) {
|
|
487
|
-
newIssueButton.disabled = true
|
|
488
|
-
container.appendChild(newIssueForm(dom, kb, tracker, null, showNewIssue))
|
|
489
|
-
},
|
|
490
|
-
false
|
|
491
|
-
)
|
|
492
|
-
|
|
493
|
-
// Table of issues - when we have the main issue list
|
|
494
|
-
// We also need the ontology loaded
|
|
495
|
-
//
|
|
496
|
-
context.session.store.fetcher
|
|
497
|
-
.load([stateStore])
|
|
498
|
-
.then(function (_xhrs) {
|
|
499
|
-
const tableDiv = renderTabsTableAndBoard(tracker)
|
|
500
|
-
// const tableDiv = renderTable(tracker) // was
|
|
501
|
-
paneDiv.appendChild(tableDiv)
|
|
502
|
-
|
|
503
|
-
if (tableDiv.refresh) {
|
|
504
|
-
// Refresh function
|
|
505
|
-
} else {
|
|
506
|
-
console.log('No table refresh function?!')
|
|
507
|
-
}
|
|
508
|
-
paneDiv.appendChild(newTrackerButton(subject, context))
|
|
509
|
-
updater.addDownstreamChangeListener(stateStore, tableDiv.refresh) // Live update
|
|
510
|
-
})
|
|
511
|
-
.catch(function (err) {
|
|
512
|
-
return console.log('Cannot load state store: ' + err)
|
|
513
|
-
})
|
|
514
|
-
// end of Tracker instance
|
|
515
|
-
} // render tracker
|
|
516
|
-
|
|
517
|
-
/* Render tabs with both views
|
|
518
|
-
*/
|
|
519
|
-
const boardView = ns.wf('BoardView')
|
|
520
|
-
const tableView = ns.wf('TableView')
|
|
521
|
-
const settingsView = ns.wf('SettingsView')
|
|
522
|
-
const instancesView = ns.wf('InstancesView')
|
|
523
|
-
|
|
524
|
-
const updater = kb.updater
|
|
525
|
-
const t = kb.findTypeURIs(subject)
|
|
526
|
-
let tracker
|
|
527
|
-
|
|
528
|
-
// Whatever we are rendering, lets load the ontology
|
|
529
|
-
const flowOntology = ns.wf('').doc()
|
|
530
|
-
if (!kb.holds(undefined, undefined, undefined, flowOntology)) {
|
|
531
|
-
// If not loaded already
|
|
532
|
-
$rdf.parse(require('./wf.js'), kb, flowOntology.uri, 'text/turtle') // Load ontology directly
|
|
533
|
-
}
|
|
534
|
-
const userInterfaceOntology = ns.ui('').doc()
|
|
535
|
-
if (!kb.holds(undefined, undefined, undefined, userInterfaceOntology)) {
|
|
536
|
-
// If not loaded already
|
|
537
|
-
$rdf.parse(require('./ui.js'), kb, userInterfaceOntology.uri, 'text/turtle') // Load ontology directly
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Render a single issue
|
|
541
|
-
if (
|
|
542
|
-
t['http://www.w3.org/2005/01/wf/flow#Task'] ||
|
|
543
|
-
kb.holds(subject, ns.wf('tracker'))
|
|
544
|
-
) {
|
|
545
|
-
renderSingleIssue().then(() => console.log('Single issue rendered'))
|
|
546
|
-
} else if (t['http://www.w3.org/2005/01/wf/flow#Tracker']) {
|
|
547
|
-
// Render a Tracker instance
|
|
548
|
-
renderTracker().then(() => console.log('Tracker rendered'))
|
|
549
|
-
} else {
|
|
550
|
-
console.log(
|
|
551
|
-
'Error: Issue pane: No evidence that ' +
|
|
552
|
-
subject +
|
|
553
|
-
' is either a bug or a tracker.'
|
|
554
|
-
)
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
let loginOutButton
|
|
558
|
-
const overlay = paneDiv.appendChild(dom.createElement('div'))
|
|
559
|
-
context.overlay = overlay
|
|
560
|
-
overlay.style = OVERFLOW_STYLE
|
|
561
|
-
overlay.style.visibility = 'hidden'
|
|
562
|
-
|
|
563
|
-
authn.checkUser().then(webId => {
|
|
564
|
-
if (webId) {
|
|
565
|
-
console.log('Web ID set already: ' + webId)
|
|
566
|
-
context.me = webId
|
|
567
|
-
// @@ enable things
|
|
568
|
-
return
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
loginOutButton = login.loginStatusBox(dom, webIdUri => {
|
|
572
|
-
authn.log
|
|
573
|
-
if (webIdUri) {
|
|
574
|
-
context.me = kb.sym(webIdUri)
|
|
575
|
-
console.log('Web ID set from login button: ' + webIdUri)
|
|
576
|
-
paneDiv.removeChild(loginOutButton)
|
|
577
|
-
// enable things
|
|
578
|
-
} else {
|
|
579
|
-
context.me = null
|
|
580
|
-
}
|
|
581
|
-
})
|
|
582
|
-
|
|
583
|
-
loginOutButton.setAttribute('style', 'margin: 0.5em 1em;')
|
|
584
|
-
paneDiv.appendChild(loginOutButton)
|
|
585
|
-
if (!context.statusArea) {
|
|
586
|
-
context.statusArea = paneDiv.appendChild(dom.createElement('div'))
|
|
587
|
-
}
|
|
588
|
-
})
|
|
589
|
-
|
|
590
|
-
return paneDiv
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// ends
|