Merge branch 'master' into 12479-wb-structured-vocabulary
[arvados.git] / apps / workbench / app / assets / javascripts / components / edit_tags.js
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 window.SelectOrAutocomplete = {
6     view: function(vnode) {
7         return m("input", {
8             style: {
9                 width: '100%'
10             },
11             type: 'text',
12             value: vnode.attrs.value
13         }, vnode.attrs.value)
14     },
15     oncreate: function(vnode) {
16         $(vnode.dom).selectize({
17             labelField: 'value',
18             valueField: 'value',
19             searchField: 'value',
20             sortField: 'value',
21             maxItems: 1,
22             create: vnode.attrs.create ? function(input) {
23                 return {value: input}
24             } : false,
25             items: [vnode.attrs.value()],
26             options: vnode.attrs.options.map(function(option) {
27                 return {value: option}
28             }),
29             onChange: function(val) {
30                 vnode.attrs.value(val)
31             }
32         })
33     }
34 }
35
36 window.TagEditorRow = {
37     view: function(vnode) {
38         // Value options list
39         valueOpts = []
40         if (vnode.attrs.name() in vnode.attrs.vocabulary().types &&
41             'options' in vnode.attrs.vocabulary().types[vnode.attrs.name()]) {
42                 valueOpts = vnode.attrs.vocabulary().types[vnode.attrs.name()].options
43         }
44         valueOpts.push(vnode.attrs.value())
45
46         return m("tr", [
47             // Erase tag
48             m("td",
49             vnode.attrs.editMode &&
50                 m('div.text-center', m('a.btn.btn-default.btn-sm', {
51                     style: {
52                         align: 'center'
53                     },
54                     onclick: function(e) { vnode.attrs.removeTag() }
55                 }, m('i.fa.fa-fw.fa-trash-o'))),
56             ),
57             // Tag name
58             m("td",
59             vnode.attrs.editMode ?
60             m("div", {key: 'name-'+vnode.attrs.name()},[m(SelectOrAutocomplete, {
61                 options: (vnode.attrs.name() in vnode.attrs.vocabulary().types)
62                     ? Object.keys(vnode.attrs.vocabulary().types)
63                     : Object.keys(vnode.attrs.vocabulary().types).concat(vnode.attrs.name()),
64                 value: vnode.attrs.name,
65                 create: vnode.attrs.vocabulary().strict
66             })])
67             : vnode.attrs.name),
68             // Tag value
69             m("td",
70             vnode.attrs.editMode ?
71             m("div", {key: 'value-'+vnode.attrs.name()}, [m(SelectOrAutocomplete, {
72                 options: valueOpts,
73                 value: vnode.attrs.value,
74                 create: (vnode.attrs.name() in vnode.attrs.vocabulary().types)
75                     ? (vnode.attrs.vocabulary().types[vnode.attrs.name()].type == 'text') || 
76                         vnode.attrs.vocabulary().types[vnode.attrs.name()].overridable || false
77                     : true, // If tag not in vocabulary, we should accept any value
78                 })
79             ])
80             : vnode.attrs.value)
81         ])
82     }
83 }
84
85 window.TagEditorTable = {
86     view: function(vnode) {
87         return m("table.table.table-condensed", {
88             border: "1"
89         }, [
90             m("colgroup", [
91                 m("col", {width:"5%"}),
92                 m("col", {width:"25%"}),
93                 m("col", {width:"70%"}),
94             ]),
95             m("thead", [
96                 m("tr", [
97                     m("th"),
98                     m("th", "Key"),
99                     m("th", "Value"),
100                 ])
101             ]),
102             m("tbody", [
103                 vnode.attrs.tags.length > 0
104                 ? vnode.attrs.tags.map(function(tag, idx) {
105                     return m(TagEditorRow, {
106                         key: idx,
107                         removeTag: function() {
108                             vnode.attrs.tags.splice(idx, 1)
109                             vnode.attrs.dirty(true)
110                         },
111                         editMode: vnode.attrs.editMode,
112                         name: tag.name,
113                         value: tag.value,
114                         vocabulary: vnode.attrs.vocabulary
115                     })
116                 })
117                 : m("tr", m("td[colspan=3]", m("center","(no tags)")))
118             ]),
119         ])
120     }
121 }
122
123 window.TagEditorApp = {
124     appendTag: function(vnode, name, value) {
125         var tag = {name: m.stream(name), value: m.stream(value)}
126         tag.name.map(vnode.state.dirty)
127         tag.value.map(vnode.state.dirty)
128         tag.name.map(m.redraw)
129         vnode.state.tags.push(tag)
130     },
131     oninit: function(vnode) {
132         vnode.state.sessionDB = new SessionDB()
133         // Get vocabulary
134         vnode.state.vocabulary = m.stream({"strict":false, "types":{}})
135         m.request('/vocabulary.json').then(vnode.state.vocabulary)
136         vnode.state.editMode = vnode.attrs.targetEditable
137         vnode.state.tags = []
138         vnode.state.dirty = m.stream(false)
139         vnode.state.dirty.map(m.redraw)
140         vnode.state.objPath = '/arvados/v1/'+vnode.attrs.targetController+'/'+vnode.attrs.targetUuid
141         // Get tags
142         vnode.state.sessionDB.request(
143             vnode.state.sessionDB.loadLocal(),
144             '/arvados/v1/'+vnode.attrs.targetController,
145             {
146                 data: {
147                     filters: JSON.stringify([['uuid', '=', vnode.attrs.targetUuid]]),
148                     select: JSON.stringify(['properties'])
149                 },
150             }).then(function(obj) {
151                 if (obj.items.length == 1) {
152                     o = obj.items[0]
153                     Object.keys(o.properties).forEach(function(k) {
154                         vnode.state.appendTag(vnode, k, o.properties[k])
155                     })
156                     // Data synced with server, so dirty state should be false
157                     vnode.state.dirty(false)
158                 }
159             }
160         )
161     },
162     view: function(vnode) {
163         return [
164             vnode.state.editMode &&
165             m("div.pull-left", [
166                 m("a.btn.btn-primary.btn-sm"+(vnode.state.dirty() ? '' : '.disabled'), {
167                     style: {
168                         margin: '10px 0px'
169                     },
170                     onclick: function(e) {
171                         var tags = {}
172                         vnode.state.tags.forEach(function(t) {
173                             tags[t.name()] = t.value()
174                         })
175                         vnode.state.sessionDB.request(
176                             vnode.state.sessionDB.loadLocal(),
177                             vnode.state.objPath, {
178                                 method: "PUT",
179                                 data: {properties: JSON.stringify(tags)}
180                             }
181                         ).then(function(v) {
182                             vnode.state.dirty(false)
183                         })
184                     }
185                 }, vnode.state.dirty() ? ' Save changes ' : ' Saved ')
186             ]),
187             // Tags table
188             m(TagEditorTable, {
189                 editMode: vnode.state.editMode,
190                 tags: vnode.state.tags,
191                 vocabulary: vnode.state.vocabulary,
192                 dirty: vnode.state.dirty
193             }),
194             vnode.state.editMode &&
195             m("div.pull-left", [
196                 // Add tag button
197                 m("a.btn.btn-primary.btn-sm", {
198                     onclick: function(e) {
199                         vnode.state.appendTag(vnode, 'new tag', 'new value')
200                     }
201                 }, [
202                     m("i.glyphicon.glyphicon-plus"),
203                     " Add new tag "
204                 ])
205             ])
206         ]
207     },
208 }