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
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
@prefix : <#>.
|
|
2
|
-
@prefix dc: <http://purl.org/dc/elements/1.1/>.
|
|
3
|
-
@prefix dct: <http://purl.org/dc/terms/>.
|
|
4
|
-
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
|
|
5
|
-
@prefix owl: <http://www.w3.org/2002/07/owl#>.
|
|
6
|
-
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
|
|
7
|
-
@prefix ui: <http://www.w3.org/ns/ui#>.
|
|
8
|
-
@prefix wf: <http://www.w3.org/2005/01/wf/flow#>.
|
|
9
|
-
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
|
|
10
|
-
@prefix c: </profile/card#>.
|
|
11
|
-
|
|
12
|
-
:Classification
|
|
13
|
-
rdfs:label "Category";
|
|
14
|
-
owl:disjointUnionOf
|
|
15
|
-
( :Specification :Tests :Servers :Clients :DeveloperTools :Vertical ).
|
|
16
|
-
:Clients
|
|
17
|
-
rdfs:comment "Client side frameworks, SDKs and operating systems";
|
|
18
|
-
rdfs:label "Clients";
|
|
19
|
-
rdfs:subClassOf :Classification.
|
|
20
|
-
:DeveloperTools rdfs:label "Developer tools"; rdfs:subClassOf :Classification.
|
|
21
|
-
|
|
22
|
-
:InProgress
|
|
23
|
-
rdfs:label "In progress";
|
|
24
|
-
rdfs:subClassOf wf:Open, :States;
|
|
25
|
-
ui:backgroundColor "#f9ee71"^^xsd:color.
|
|
26
|
-
:NextSession
|
|
27
|
-
rdfs:label "To be done this month";
|
|
28
|
-
rdfs:subClassOf wf:Open, :States;
|
|
29
|
-
ui:backgroundColor "#91fdb9"^^xsd:color.
|
|
30
|
-
:Released
|
|
31
|
-
rdfs:label "Released";
|
|
32
|
-
rdfs:subClassOf wf:Open, :States;
|
|
33
|
-
ui:backgroundColor "#e2a7fb"^^xsd:color.
|
|
34
|
-
:Research
|
|
35
|
-
rdfs:label "Research";
|
|
36
|
-
rdfs:subClassOf wf:Open, :States;
|
|
37
|
-
ui:backgroundColor "#ffffff"^^xsd:color.
|
|
38
|
-
:Servers
|
|
39
|
-
rdfs:label "Solid Server implementations"; rdfs:subClassOf :Classification.
|
|
40
|
-
:Someday
|
|
41
|
-
rdfs:label "Someday pile";
|
|
42
|
-
rdfs:subClassOf wf:Open, :States;
|
|
43
|
-
ui:backgroundColor "#e3e3e3"^^xsd:color.
|
|
44
|
-
:Specification
|
|
45
|
-
rdfs:label "Parts of the Solid specification"; rdfs:subClassOf :Classification.
|
|
46
|
-
:States
|
|
47
|
-
rdfs:label "Task";
|
|
48
|
-
owl:disjointUnionOf
|
|
49
|
-
( :Research :Someday :ToBeDone :NextSession :InProgress :Works :Released ).
|
|
50
|
-
:Tests rdfs:label "Test suites"; rdfs:subClassOf :Classification.
|
|
51
|
-
|
|
52
|
-
:this
|
|
53
|
-
a wf:Tracker;
|
|
54
|
-
dc:author c:me;
|
|
55
|
-
dc:created "2020-07-21T14:00:20Z"^^xsd:dateTime;
|
|
56
|
-
dct:title "Solid Ecosystem: Roadmap";
|
|
57
|
-
wf:assigneeClass foaf:Person;
|
|
58
|
-
wf:defaultView :Classification;
|
|
59
|
-
wf:description
|
|
60
|
-
"""A roadmap for the whole Solid ecosystem.
|
|
61
|
-
See also other linked roadmaps for parts of the ecosystem like the SolidOS, Spec, open source projects, etc.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
""";
|
|
65
|
-
wf:initialState :Someday;
|
|
66
|
-
wf:issueCategory :Classification;
|
|
67
|
-
wf:issueClass :States;
|
|
68
|
-
wf:stateStore <state.ttl>.
|
|
69
|
-
:ToBeDone
|
|
70
|
-
rdfs:label "To Be Done";
|
|
71
|
-
rdfs:subClassOf wf:Open, :States;
|
|
72
|
-
ui:backgroundColor "#e0ffeb"^^xsd:color.
|
|
73
|
-
:Vertical rdfs:label "Apps and Verticals"; rdfs:subClassOf :Classification.
|
|
74
|
-
|
|
75
|
-
:Works
|
|
76
|
-
rdfs:label "Code runs";
|
|
77
|
-
rdfs:subClassOf wf:Open, :States;
|
|
78
|
-
ui:backgroundColor "#dcdbff"^^xsd:color.
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
@prefix dc: <http://purl.org/dc/terms/> .
|
|
2
|
-
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
3
|
-
@prefix con: <http://www.w3.org/2000/10/swap/pim/contact#> .
|
|
4
|
-
@prefix dc: <http://purl.org/dc/elements/1.1/>.
|
|
5
|
-
@prefix doap: <http://usefulinc.com/ns/doap#> .
|
|
6
|
-
@prefix wf: <http://www.w3.org/2005/01/wf/flow#> .
|
|
7
|
-
@prefix ical: <http://www.w3.org/2002/12/cal/ical#> .
|
|
8
|
-
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
9
|
-
@prefix ui: <http://www.w3.org/ns/ui#> .
|
|
10
|
-
@prefix dct: <http://purl.org/dc/terms/> .
|
|
11
|
-
@prefix : <big-tracker.ttl#> . # the config file
|
|
12
|
-
|
|
13
|
-
<#Task1> wf:tracker :this ;
|
|
14
|
-
dc:title "Test task 1";
|
|
15
|
-
a :New , :Action;
|
|
16
|
-
dct:created 2004-08-21;
|
|
17
|
-
ical:location "London";
|
|
18
|
-
wf:dateReceived 2000-01-12;
|
|
19
|
-
wf:earliestDate 1999-12-12;
|
|
20
|
-
wf:latestDate 2001-12-12;
|
|
21
|
-
|
|
22
|
-
wf:description "Yadda Tadda yadda ";
|
|
23
|
-
rdfs:comment "This is a comment".
|
|
24
|
-
|
|
25
|
-
<#Task2> wf:tracker :this ;
|
|
26
|
-
dc:title "Test task 2";
|
|
27
|
-
a :Accepted , :Action;
|
|
28
|
-
dct:created 2004-06-21;
|
|
29
|
-
ical:location "London";
|
|
30
|
-
wf:dateReceived 2000-01-12;
|
|
31
|
-
wf:earliestDate 1999-12-12;
|
|
32
|
-
wf:latestDate 2001-12-12;
|
|
33
|
-
|
|
34
|
-
wf:description "Yadda Tadda yadda ";
|
|
35
|
-
rdfs:comment "This is a comment".
|
|
36
|
-
|
|
37
|
-
<#Task3> wf:tracker :this ;
|
|
38
|
-
dc:title "Test task 3";
|
|
39
|
-
a :New , :Book;
|
|
40
|
-
dct:created 2004-04-21;
|
|
41
|
-
ical:location "London";
|
|
42
|
-
wf:dateReceived 2000-01-12;
|
|
43
|
-
wf:earliestDate 1999-12-12;
|
|
44
|
-
wf:latestDate 2001-12-12;
|
|
45
|
-
|
|
46
|
-
wf:description "Yadda Tadda yadda ";
|
|
47
|
-
rdfs:comment "This is a comment".
|
|
48
|
-
|
|
49
|
-
<#Task4> wf:tracker :this ;
|
|
50
|
-
dc:title "Test task 4";
|
|
51
|
-
a :Accepted , :Book;
|
|
52
|
-
dct:created 2004-09-21;
|
|
53
|
-
ical:location "London";
|
|
54
|
-
wf:dateReceived 2000-01-12;
|
|
55
|
-
wf:earliestDate 1999-12-12;
|
|
56
|
-
wf:latestDate 2001-12-12;
|
|
57
|
-
|
|
58
|
-
wf:description "Yadda Tadda yadda ";
|
|
59
|
-
rdfs:comment "This is a comment".
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# ENDS
|
|
64
|
-
|
package/eslint.config.mjs
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import globals from "globals";
|
|
2
|
-
import js from "@eslint/js";
|
|
3
|
-
import importPlugin from "eslint-plugin-import";
|
|
4
|
-
|
|
5
|
-
export default [
|
|
6
|
-
{
|
|
7
|
-
languageOptions: {
|
|
8
|
-
globals: {
|
|
9
|
-
...globals.browser,
|
|
10
|
-
...globals.node,
|
|
11
|
-
Atomics: "readonly",
|
|
12
|
-
SharedArrayBuffer: "readonly",
|
|
13
|
-
},
|
|
14
|
-
ecmaVersion: 2022,
|
|
15
|
-
sourceType: "module",
|
|
16
|
-
},
|
|
17
|
-
plugins: {
|
|
18
|
-
import: importPlugin,
|
|
19
|
-
},
|
|
20
|
-
rules: {
|
|
21
|
-
...js.configs.recommended.rules,
|
|
22
|
-
...importPlugin.configs.recommended.rules,
|
|
23
|
-
"no-unused-vars": [
|
|
24
|
-
"warn",
|
|
25
|
-
{
|
|
26
|
-
argsIgnorePattern: "^_",
|
|
27
|
-
varsIgnorePattern: "^_",
|
|
28
|
-
},
|
|
29
|
-
],
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
];
|
package/issue.js
DELETED
|
@@ -1,506 +0,0 @@
|
|
|
1
|
-
// All the UI for a single issue, without store load or listening for changes
|
|
2
|
-
//
|
|
3
|
-
import { icons, messageArea, ns, rdf, style, utils, widgets } from 'solid-ui'
|
|
4
|
-
import { authn, store } from 'solid-logic'
|
|
5
|
-
import { newIssueForm } from './newIssue'
|
|
6
|
-
|
|
7
|
-
const $rdf = rdf
|
|
8
|
-
const kb = store
|
|
9
|
-
|
|
10
|
-
const SET_MODIFIED_DATES = false
|
|
11
|
-
|
|
12
|
-
export const TASK_ICON = icons.iconBase + 'noun_17020_gray-tick.svg'
|
|
13
|
-
export const OPEN_TASK_ICON = icons.iconBase + 'noun_17020_sans-tick.svg'
|
|
14
|
-
export const CLOSED_TASK_ICON = icons.iconBase + 'noun_17020.svg'
|
|
15
|
-
|
|
16
|
-
function complain (message, context) {
|
|
17
|
-
console.warn(message)
|
|
18
|
-
context.paneDiv.appendChild(widgets.errorMessageBlock(context.dom, message))
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function isOpen (issue) {
|
|
22
|
-
const types = kb.findTypeURIs(issue)
|
|
23
|
-
return !!types[ns.wf('Open').uri]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function iconForIssue (issue) {
|
|
27
|
-
return isOpen(issue) ? TASK_ICON : CLOSED_TASK_ICON
|
|
28
|
-
}
|
|
29
|
-
export function getState (issue, classification) {
|
|
30
|
-
const tracker = kb.the(issue, ns.wf('tracker'), null, issue.doc())
|
|
31
|
-
const states = kb.any(tracker, ns.wf('issueClass'))
|
|
32
|
-
classification = classification || states
|
|
33
|
-
const types = kb.each(issue, ns.rdf('type'))
|
|
34
|
-
.filter(ty => kb.holds(ty, ns.rdfs('subClassOf'), classification))
|
|
35
|
-
if (types.length !== 1) {
|
|
36
|
-
// const initialState = kb.any(tracker, ns.wf('initialState')) No do NOT default
|
|
37
|
-
// if (initialState) return initialState
|
|
38
|
-
throw new Error('Issue must have one type as state: ' + types.length)
|
|
39
|
-
}
|
|
40
|
-
return types[0]
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function getBackgroundColorFromTypes (issue) {
|
|
44
|
-
const classes = kb.each(issue, ns.rdf('type')) // @@ pick cats in order then state
|
|
45
|
-
const catColors = classes.map(cat => kb.any(cat, ns.ui('backgroundColor'))).filter(c => !!c)
|
|
46
|
-
|
|
47
|
-
if (catColors.length) return catColors[0].value // pick first one
|
|
48
|
-
return null
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function renderIssueCard (issue, context) {
|
|
52
|
-
function refresh () {
|
|
53
|
-
const backgroundColor = getBackgroundColorFromTypes(issue) || 'white'
|
|
54
|
-
card.style.backgroundColor = backgroundColor
|
|
55
|
-
editButton.style.backgroundColor = backgroundColor // Override white from style sheet
|
|
56
|
-
}
|
|
57
|
-
const dom = context.dom
|
|
58
|
-
const uncategorized = !getBackgroundColorFromTypes(issue) // This is a suspect issue. Prompt to delete it
|
|
59
|
-
|
|
60
|
-
const card = dom.createElement('div')
|
|
61
|
-
const table = card.appendChild(dom.createElement('table'))
|
|
62
|
-
table.style.width = '100%'
|
|
63
|
-
const options = { draggable: false } // Let the board make the whole card draggable
|
|
64
|
-
table.appendChild(widgets.personTR(dom, null, issue, options))
|
|
65
|
-
table.subject = issue
|
|
66
|
-
card.style = 'border-radius: 0.4em; border: 0.05em solid grey; margin: 0.3em;'
|
|
67
|
-
|
|
68
|
-
const img = card.firstChild.firstChild.firstChild.firstChild // div/table/tr/td/img
|
|
69
|
-
img.setAttribute('src', icons.iconBase + 'noun_Danger_1259514.svg') // override
|
|
70
|
-
// Add a button for viewing the whole issue in overlay
|
|
71
|
-
const buttonsCell = card.firstChild.firstChild.children[2] // right hand part of card
|
|
72
|
-
const editButton = widgets.button(dom, icons.iconBase + 'noun_253504.svg', 'edit', async _event => {
|
|
73
|
-
exposeOverlay(issue, context)
|
|
74
|
-
})
|
|
75
|
-
const editButtonImage = editButton.firstChild
|
|
76
|
-
editButtonImage.style.width = editButtonImage.style.height = '1.5em'
|
|
77
|
-
buttonsCell.appendChild(editButton)
|
|
78
|
-
|
|
79
|
-
// If uncategorized, shortcut to delete issue
|
|
80
|
-
if (uncategorized) {
|
|
81
|
-
const deleteButton = widgets.deleteButtonWithCheck(dom, buttonsCell, 'issue', async function () { // noun?
|
|
82
|
-
try {
|
|
83
|
-
await kb.updater.update(kb.connectedStatements(issue))
|
|
84
|
-
} catch (err) {
|
|
85
|
-
complain(`Unable to delete issue: ${err}`, context)
|
|
86
|
-
}
|
|
87
|
-
console.log('User deleted issue ' + issue)
|
|
88
|
-
card.parentNode.removeChild(card) // refresh doesn't work yet because it is not passed though tabs so short cut
|
|
89
|
-
widgets.refreshTree(context.paneDiv) // Should delete the card if nec when tabs pass it though
|
|
90
|
-
// complain('DELETED OK', context)
|
|
91
|
-
})
|
|
92
|
-
buttonsCell.appendChild(deleteButton)
|
|
93
|
-
}
|
|
94
|
-
card.style.maxWidth = '24em' // @@ User adjustable??
|
|
95
|
-
card.refresh = refresh
|
|
96
|
-
refresh()
|
|
97
|
-
return card
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function exposeOverlay (subject, context) {
|
|
101
|
-
function hideOverlay () {
|
|
102
|
-
overlay.innerHTML = '' // clear overlay
|
|
103
|
-
overlay.style.visibility = 'hidden'
|
|
104
|
-
}
|
|
105
|
-
const overlay = context.overlay
|
|
106
|
-
overlay.innerHTML = '' // clear existing
|
|
107
|
-
const button = overlay.appendChild(
|
|
108
|
-
widgets.button(context.dom, icons.iconBase + 'noun_1180156.svg', 'close', hideOverlay))
|
|
109
|
-
button.style.float = 'right'
|
|
110
|
-
button.style.margin = '0.7em'
|
|
111
|
-
delete button.style.backgroundColor // do not want white
|
|
112
|
-
overlay.style.visibility = 'visible'
|
|
113
|
-
overlay.appendChild(renderIssue(subject, context))
|
|
114
|
-
overlay.firstChild.style.overflow = 'auto' // was scroll
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function renderSpacer (dom, backgroundColor) {
|
|
118
|
-
const spacer = dom.createElement('div')
|
|
119
|
-
spacer.setAttribute('style', 'height: 1em; margin: 0.5em;') // spacer and placeHolder
|
|
120
|
-
spacer.style.backgroundColor = backgroundColor // try that
|
|
121
|
-
return spacer
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export function renderIssue (issue, context) {
|
|
125
|
-
// Don't bother changing the last modified dates of things: save time
|
|
126
|
-
function setModifiedDate (subj, kb, doc) {
|
|
127
|
-
if (SET_MODIFIED_DATES) {
|
|
128
|
-
if (!getOption(tracker, 'trackLastModified')) return
|
|
129
|
-
const deletions = kb.statementsMatching(issue, ns.dct('modified'))
|
|
130
|
-
.concat(kb.statementsMatching(issue, ns.wf('modifiedBy'))
|
|
131
|
-
)
|
|
132
|
-
const insertions = [$rdf.st(issue, ns.dct('modified'), new Date(), doc)]
|
|
133
|
-
if (me) insertions.push($rdf.st(issue, ns.wf('modifiedBy'), me, doc))
|
|
134
|
-
kb.updater.update(deletions, insertions, function (_uri, _ok, _body) {})
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function say (message, style) {
|
|
139
|
-
const pre = dom.createElement('pre')
|
|
140
|
-
pre.setAttribute('style', style || 'color: grey')
|
|
141
|
-
issueDiv.appendChild(pre)
|
|
142
|
-
pre.appendChild(dom.createTextNode(message))
|
|
143
|
-
return pre
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function timestring () {
|
|
147
|
-
const now = new Date()
|
|
148
|
-
return '' + now.getTime()
|
|
149
|
-
// http://www.w3schools.com/jsref/jsref_obj_date.asp
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function complain (message) {
|
|
153
|
-
console.warn(message)
|
|
154
|
-
issueDiv.appendChild(widgets.errorMessageBlock(dom, message))
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function complainIfBad (ok, body) {
|
|
158
|
-
if (!ok) {
|
|
159
|
-
complain(
|
|
160
|
-
'Sorry, failed to save your change:\n' + body,
|
|
161
|
-
'background-color: pink;', context
|
|
162
|
-
)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
function getOption (tracker, option) {
|
|
166
|
-
// eg 'allowSubIssues'
|
|
167
|
-
const opt = kb.any(tracker, ns.ui(option))
|
|
168
|
-
return !!(opt && opt.value)
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function setPaneStyle () {
|
|
172
|
-
const backgroundColor = getBackgroundColorFromTypes(issue) || '#eee' // default grey
|
|
173
|
-
const mystyle0 = 'padding: 0.5em 1.5em 1em 1.5em; border: 0.7em;'
|
|
174
|
-
const mystyle = mystyle0 + 'border-color: ' + backgroundColor + '; '
|
|
175
|
-
issueDiv.setAttribute('style', mystyle)
|
|
176
|
-
issueDiv.style.backgroundColor = 'white'
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/// ////////////// Body of renderIssue
|
|
180
|
-
|
|
181
|
-
const dom = context.dom
|
|
182
|
-
// eslint-disable-next-line no-use-before-define
|
|
183
|
-
const tracker = kb.the(issue, ns.wf('tracker'), null, issue.doc())
|
|
184
|
-
if (!tracker) throw new Error('No tracker')
|
|
185
|
-
// eslint-disable-next-line no-use-before-define
|
|
186
|
-
const stateStore = kb.any(tracker, ns.wf('stateStore'))
|
|
187
|
-
const store = issue.doc()
|
|
188
|
-
|
|
189
|
-
const issueDiv = dom.createElement('div')
|
|
190
|
-
const me = authn.currentUser()
|
|
191
|
-
const backgroundColor = getBackgroundColorFromTypes(issue) || 'white'
|
|
192
|
-
|
|
193
|
-
setPaneStyle()
|
|
194
|
-
|
|
195
|
-
authn.checkUser() // kick off async operation
|
|
196
|
-
|
|
197
|
-
const iconButton = issueDiv.appendChild(widgets.button(dom, iconForIssue(issue)))
|
|
198
|
-
widgets.makeDraggable(iconButton, issue) // Drag me wherever you need to do stuff with this issue
|
|
199
|
-
|
|
200
|
-
const states = kb.any(tracker, ns.wf('issueClass'))
|
|
201
|
-
if (!states) { throw new Error('This tracker ' + tracker + ' has no issueClass') }
|
|
202
|
-
const select = widgets.makeSelectForCategory(
|
|
203
|
-
dom,
|
|
204
|
-
kb,
|
|
205
|
-
issue,
|
|
206
|
-
states,
|
|
207
|
-
stateStore,
|
|
208
|
-
function (ok, body) {
|
|
209
|
-
if (ok) {
|
|
210
|
-
setModifiedDate(store, kb, store)
|
|
211
|
-
widgets.refreshTree(issueDiv)
|
|
212
|
-
} else {
|
|
213
|
-
console.log('Failed to change state:\n' + body)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
)
|
|
217
|
-
issueDiv.appendChild(select)
|
|
218
|
-
|
|
219
|
-
const cats = kb.each(tracker, ns.wf('issueCategory')) // zero or more
|
|
220
|
-
for (const cat of cats) {
|
|
221
|
-
issueDiv.appendChild(
|
|
222
|
-
widgets.makeSelectForCategory(
|
|
223
|
-
dom,
|
|
224
|
-
kb,
|
|
225
|
-
issue,
|
|
226
|
-
cat,
|
|
227
|
-
stateStore,
|
|
228
|
-
function (ok, body) {
|
|
229
|
-
if (ok) {
|
|
230
|
-
setModifiedDate(store, kb, store)
|
|
231
|
-
widgets.refreshTree(issueDiv)
|
|
232
|
-
} else {
|
|
233
|
-
console.log('Failed to change category:\n' + body)
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
)
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// For when issue is the main solo subject, include link to tracker itself.
|
|
241
|
-
const a = dom.createElement('a')
|
|
242
|
-
a.setAttribute('href', tracker.uri)
|
|
243
|
-
a.setAttribute('style', 'float:right')
|
|
244
|
-
issueDiv.appendChild(a).textContent = utils.label(tracker)
|
|
245
|
-
a.addEventListener('click', widgets.openHrefInOutlineMode, true)
|
|
246
|
-
|
|
247
|
-
// Main Form for Title, description only
|
|
248
|
-
const coreIssueFormText = `
|
|
249
|
-
@prefix : <http://www.w3.org/ns/ui#> .
|
|
250
|
-
@prefix core: <http://www.w3.org/2005/01/wf/flow#>.
|
|
251
|
-
@prefix dc: <http://purl.org/dc/elements/1.1/>.
|
|
252
|
-
@prefix wf: <http://www.w3.org/2005/01/wf/flow#> .
|
|
253
|
-
|
|
254
|
-
core:coreIsueForm a :Form;
|
|
255
|
-
<http://purl.org/dc/elements/1.1/title> "Core issue data";
|
|
256
|
-
:parts (
|
|
257
|
-
core:titleField
|
|
258
|
-
core:descriptionField ) .
|
|
259
|
-
|
|
260
|
-
core:descriptionField a :MultiLineTextField;
|
|
261
|
-
:label "Description";
|
|
262
|
-
:property wf:description;
|
|
263
|
-
:size "40" .
|
|
264
|
-
|
|
265
|
-
core:titleField a :SingleLineTextField;
|
|
266
|
-
:label "Title";
|
|
267
|
-
:maxLength "128";
|
|
268
|
-
:property dc:title; # @@ Should move to dct or schema
|
|
269
|
-
:size "40" .
|
|
270
|
-
|
|
271
|
-
wf:Task :creationForm core:coreIsueForm .
|
|
272
|
-
`
|
|
273
|
-
const CORE_ISSUE_FORM = ns.wf('coreIsueForm')
|
|
274
|
-
$rdf.parse(coreIssueFormText, kb, CORE_ISSUE_FORM.doc().uri, 'text/turtle')
|
|
275
|
-
const form = widgets.appendForm(
|
|
276
|
-
dom,
|
|
277
|
-
null, // was: container
|
|
278
|
-
{},
|
|
279
|
-
issue,
|
|
280
|
-
CORE_ISSUE_FORM,
|
|
281
|
-
stateStore,
|
|
282
|
-
complainIfBad
|
|
283
|
-
)
|
|
284
|
-
issueDiv.appendChild(form)
|
|
285
|
-
form.style.backgroundColor = backgroundColor
|
|
286
|
-
|
|
287
|
-
// Assigned to whom?
|
|
288
|
-
|
|
289
|
-
const assignments = kb.statementsMatching(issue, ns.wf('assignee'))
|
|
290
|
-
if (assignments.length > 1) {
|
|
291
|
-
say('Weird, was assigned to more than one person. Fixing ..')
|
|
292
|
-
const deletions = assignments.slice(1)
|
|
293
|
-
kb.updater.update(deletions, [], function (uri, ok, body) {
|
|
294
|
-
if (ok) {
|
|
295
|
-
say('Now fixed.')
|
|
296
|
-
} else {
|
|
297
|
-
complain('Fixed failed: ' + body, context)
|
|
298
|
-
}
|
|
299
|
-
})
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Who could be assigned to this?
|
|
303
|
-
// Anyone assigned to any issue we know about
|
|
304
|
-
|
|
305
|
-
async function getPossibleAssignees () {
|
|
306
|
-
const devGroups = kb.each(issue, ns.wf('assigneeGroup'))
|
|
307
|
-
await kb.fetcher.load(devGroups) // Load them all
|
|
308
|
-
const groupDevs = devGroups.map(group => kb.each(group, ns.vcard('member'), null, group.doc())).flat()
|
|
309
|
-
// Anyone who is a developer of any project which uses this tracker
|
|
310
|
-
const proj = kb.any(null, ns.doap('bug-database'), tracker) // What project?
|
|
311
|
-
if (proj) {
|
|
312
|
-
await kb.fetcher.load(proj)
|
|
313
|
-
}
|
|
314
|
-
const projectDevs = proj ? kb.each(proj, ns.doap('developer')) : []
|
|
315
|
-
return groupDevs.concat(projectDevs)
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Super issues first - like parent directories .. maybe use breadcrums from?? @@
|
|
319
|
-
function renderSubIssue (issue) {
|
|
320
|
-
const options = { link: false }
|
|
321
|
-
return widgets.personTR(dom, ns.wf('dependent'), issue, options)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
getPossibleAssignees().then(devs => {
|
|
325
|
-
if (devs.length) {
|
|
326
|
-
devs.forEach(function (person) {
|
|
327
|
-
kb.fetcher.lookUpThing(person)
|
|
328
|
-
}) // best effort async for names etc
|
|
329
|
-
const opts = {
|
|
330
|
-
// 'mint': '** Add new person **',
|
|
331
|
-
nullLabel: '(unassigned)'
|
|
332
|
-
/* 'mintStatementsFun': function (newDev) {
|
|
333
|
-
var sts = [ $rdf.st(newDev, ns.rdf('type'), ns.foaf('Person')) ]
|
|
334
|
-
if (proj) sts.push($rdf.st(proj, ns.doap('developer'), newDev))
|
|
335
|
-
return sts
|
|
336
|
-
}
|
|
337
|
-
*/
|
|
338
|
-
}
|
|
339
|
-
issueDiv.appendChild(
|
|
340
|
-
widgets.makeSelectForOptions(
|
|
341
|
-
dom,
|
|
342
|
-
kb,
|
|
343
|
-
issue,
|
|
344
|
-
ns.wf('assignee'),
|
|
345
|
-
devs,
|
|
346
|
-
opts,
|
|
347
|
-
store,
|
|
348
|
-
function (ok, body) {
|
|
349
|
-
if (ok) setModifiedDate(store, kb, store)
|
|
350
|
-
else console.log('Failed to change assignee:\n' + body)
|
|
351
|
-
}
|
|
352
|
-
)
|
|
353
|
-
)
|
|
354
|
-
}
|
|
355
|
-
})
|
|
356
|
-
|
|
357
|
-
/* The trees of super-issues and sub-issues
|
|
358
|
-
*/
|
|
359
|
-
function supersOver (issue, stack) {
|
|
360
|
-
stack = stack || []
|
|
361
|
-
const sup = kb.any(null, ns.wf('dependent'), issue, issue.doc())
|
|
362
|
-
if (sup) return supersOver(sup, [sup].concat(stack))
|
|
363
|
-
return stack
|
|
364
|
-
}
|
|
365
|
-
if (getOption(tracker, 'allowSubIssues')) {
|
|
366
|
-
const subIssuePanel = issueDiv.appendChild(dom.createElement('div'))
|
|
367
|
-
subIssuePanel.style = 'margin: 1em; padding: 1em;'
|
|
368
|
-
|
|
369
|
-
subIssuePanel.appendChild(dom.createElement('h4')).textContent = 'Super Issues'
|
|
370
|
-
const listOfSupers = subIssuePanel.appendChild(dom.createElement('div'))
|
|
371
|
-
listOfSupers.style.display = 'flex'
|
|
372
|
-
listOfSupers.refresh = function () {
|
|
373
|
-
// const supers = kb.each(null, ns.wf('dependent'), issue, issue.doc())
|
|
374
|
-
const supers = supersOver(issue)
|
|
375
|
-
utils.syncTableToArrayReOrdered(listOfSupers, supers, renderSubIssue)
|
|
376
|
-
}
|
|
377
|
-
listOfSupers.refresh()
|
|
378
|
-
|
|
379
|
-
// Sub issues
|
|
380
|
-
subIssuePanel.appendChild(dom.createElement('h4')).textContent = 'Sub Issues'
|
|
381
|
-
const listOfSubs = subIssuePanel.appendChild(dom.createElement('div'))
|
|
382
|
-
listOfSubs.style.display = 'flex'
|
|
383
|
-
listOfSubs.style.flexDirection = 'reverse' // Or center
|
|
384
|
-
listOfSubs.refresh = function () {
|
|
385
|
-
const subs = kb.each(issue, ns.wf('dependent'), null, issue.doc())
|
|
386
|
-
utils.syncTableToArrayReOrdered(listOfSubs, subs, renderSubIssue)
|
|
387
|
-
}
|
|
388
|
-
listOfSubs.refresh()
|
|
389
|
-
|
|
390
|
-
const b = dom.createElement('button')
|
|
391
|
-
b.setAttribute('type', 'button')
|
|
392
|
-
subIssuePanel.appendChild(b)
|
|
393
|
-
const classLabel = utils.label(states)
|
|
394
|
-
b.innerHTML = 'New sub ' + classLabel
|
|
395
|
-
b.setAttribute('style', 'float: right; margin: 0.5em 1em;')
|
|
396
|
-
b.addEventListener(
|
|
397
|
-
'click',
|
|
398
|
-
function (_event) {
|
|
399
|
-
subIssuePanel.insertBefore(newIssueForm(dom, kb, tracker, issue, listOfSubs.refresh), b.nextSibling) // Pop form just after button
|
|
400
|
-
},
|
|
401
|
-
false
|
|
402
|
-
)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
issueDiv.appendChild(dom.createElement('br'))
|
|
406
|
-
|
|
407
|
-
// Extras are stored centrally to the tracker
|
|
408
|
-
const extrasForm = kb.any(tracker, ns.wf('extrasEntryForm'))
|
|
409
|
-
if (extrasForm) {
|
|
410
|
-
widgets.appendForm(
|
|
411
|
-
dom,
|
|
412
|
-
issueDiv,
|
|
413
|
-
{},
|
|
414
|
-
issue,
|
|
415
|
-
extrasForm,
|
|
416
|
-
stateStore,
|
|
417
|
-
complainIfBad
|
|
418
|
-
)
|
|
419
|
-
// issueDiv.appendChild(renderSpacer(backgroundColor))
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// Comment/discussion area
|
|
423
|
-
|
|
424
|
-
const spacer = issueDiv.appendChild(renderSpacer(dom, backgroundColor))
|
|
425
|
-
|
|
426
|
-
const template = kb.anyValue(tracker, ns.wf('issueURITemplate'))
|
|
427
|
-
/*
|
|
428
|
-
var chatDocURITemplate = kb.anyValue(tracker, ns.wf('chatDocURITemplate')) // relaive to issue
|
|
429
|
-
var chat
|
|
430
|
-
if (chatDocURITemplate) {
|
|
431
|
-
let template = $rdf.uri.join(chatDocURITemplate, issue.uri) // Template is relative to issue
|
|
432
|
-
chat = kb.sym(expandTemplate(template))
|
|
433
|
-
} else
|
|
434
|
-
*/
|
|
435
|
-
let messageStore
|
|
436
|
-
if (template) {
|
|
437
|
-
messageStore = issue.doc() // for now. Could go deeper
|
|
438
|
-
} else {
|
|
439
|
-
messageStore = kb.any(tracker, ns.wf('messageStore'))
|
|
440
|
-
if (!messageStore) messageStore = kb.any(tracker, ns.wf('stateStore'))
|
|
441
|
-
kb.sym(messageStore.uri + '#' + 'Chat' + timestring()) // var chat =
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
kb.fetcher.nowOrWhenFetched(messageStore, function (ok, body, _xhr) {
|
|
445
|
-
if (!ok) {
|
|
446
|
-
const er = dom.createElement('p')
|
|
447
|
-
er.textContent = body // @@ use nice error message
|
|
448
|
-
issueDiv.insertBefore(er, spacer)
|
|
449
|
-
} else {
|
|
450
|
-
const discussion = messageArea(dom, kb, issue, messageStore)
|
|
451
|
-
issueDiv.insertBefore(discussion, spacer)
|
|
452
|
-
issueDiv.insertBefore(renderSpacer(dom, backgroundColor), discussion)
|
|
453
|
-
} // Not sure why e stuck this in upwards rather than downwards
|
|
454
|
-
})
|
|
455
|
-
|
|
456
|
-
// Draggable attachment list
|
|
457
|
-
const attachmentHint = issueDiv.appendChild(dom.createElement('div'))
|
|
458
|
-
attachmentHint.innerHTML = `<h4>Attachments</h4>
|
|
459
|
-
<p>Drag files, emails,
|
|
460
|
-
web pages onto the paper clip, or click the file upload button.</p>`
|
|
461
|
-
const uploadFolderURI =
|
|
462
|
-
issue.uri.endsWith('/index.ttl#this') // This has a whole folder to itself
|
|
463
|
-
? issue.uri.slice(0, 14) + 'Files/' // back to slash
|
|
464
|
-
: issue.dir().uri + 'Files/' + issue.uri.split('#')[1] + '/' // New folder for issue in file with others
|
|
465
|
-
|
|
466
|
-
widgets.attachmentList(dom, issue, issueDiv, {
|
|
467
|
-
doc: stateStore,
|
|
468
|
-
promptIcon: icons.iconBase + 'noun_25830.svg',
|
|
469
|
-
uploadFolder: kb.sym(uploadFolderURI), // Allow local files to be uploaded
|
|
470
|
-
predicate: ns.wf('attachment')
|
|
471
|
-
})
|
|
472
|
-
|
|
473
|
-
// Delete button to delete the issue
|
|
474
|
-
const deleteButton = widgets.deleteButtonWithCheck(dom, issueDiv, 'issue', async function () {
|
|
475
|
-
try {
|
|
476
|
-
await kb.updater.update(kb.connectedStatements(issue))
|
|
477
|
-
} catch (err) {
|
|
478
|
-
complain(`Unable to delete issue: ${err}`, context)
|
|
479
|
-
}
|
|
480
|
-
// @@ refreshTree
|
|
481
|
-
complain('DELETED OK', context)
|
|
482
|
-
issueDiv.style.backgroundColor = '#eee'
|
|
483
|
-
issueDiv.style.fontColor = 'orange'
|
|
484
|
-
})
|
|
485
|
-
deleteButton.style.float = 'right'
|
|
486
|
-
|
|
487
|
-
// Refresh button
|
|
488
|
-
const refreshButton = dom.createElement('button')
|
|
489
|
-
refreshButton.textContent = 'refresh messages'
|
|
490
|
-
refreshButton.addEventListener(
|
|
491
|
-
'click',
|
|
492
|
-
async function (_event) {
|
|
493
|
-
try {
|
|
494
|
-
await kb.fetcher.load(messageStore, { force: true, clearPreviousData: true })
|
|
495
|
-
} catch (err) {
|
|
496
|
-
alert(err)
|
|
497
|
-
return
|
|
498
|
-
}
|
|
499
|
-
widgets.refreshTree(issueDiv)
|
|
500
|
-
},
|
|
501
|
-
false
|
|
502
|
-
)
|
|
503
|
-
refreshButton.setAttribute('style', style.button)
|
|
504
|
-
issueDiv.appendChild(refreshButton)
|
|
505
|
-
return issueDiv
|
|
506
|
-
} // renderIssue
|