cob-cli 2.13.1 → 2.13.2

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.
Files changed (33) hide show
  1. package/package.json +1 -1
  2. package/templates/dashboards/dash/dist/css/app.97b1c4b4.css +8 -0
  3. package/templates/dashboards/dash/dist/dashboard.html +5 -5
  4. package/templates/dashboards/dash/dist/js/{app.f9c19b80.js → app.a1c26814.js} +9 -9
  5. package/templates/dashboards/dash/dist/js/app.a1c26814.js.map +1 -0
  6. package/templates/dashboards/dash/package-lock.json +3918 -5165
  7. package/templates/dashboards/dash/package.json +1 -1
  8. package/templates/dashboards/dash/src/App.vue +108 -64
  9. package/templates/dashboards/dash/src/collector.js +107 -35
  10. package/templates/dashboards/dash/src/components/Attention.vue +30 -0
  11. package/templates/dashboards/dash/src/components/Board.vue +22 -53
  12. package/templates/dashboards/dash/src/components/Dashboard.vue +24 -0
  13. package/templates/dashboards/dash/src/components/Filter.vue +58 -0
  14. package/templates/dashboards/dash/src/components/Kibana.vue +107 -0
  15. package/templates/dashboards/dash/src/components/Label.vue +15 -0
  16. package/templates/dashboards/dash/src/components/Menu.vue +33 -22
  17. package/templates/dashboards/dash/src/components/Totals.vue +35 -53
  18. package/templates/dashboards/dash/src/components/TotalsValue.vue +48 -59
  19. package/templates/dashboards/dash/src/dashboard.html +1 -1
  20. package/templates/dashboards/dash/src/definition_dashboard.json +741 -0
  21. package/templates/dashboards/dash/src/output.css +20103 -76122
  22. package/templates/dashboards/dash/tailwind.config.js +6 -10
  23. package/templates/frontend/common/js/cob/_show_hidden.js +1 -1
  24. package/templates/dashboards/dash/definition_dashboard_v59.json +0 -1
  25. package/templates/dashboards/dash/dist/css/app.2ca409ad.css +0 -8
  26. package/templates/dashboards/dash/dist/js/app.8423eff3.js +0 -188
  27. package/templates/dashboards/dash/dist/js/app.8423eff3.js.map +0 -1
  28. package/templates/dashboards/dash/dist/js/app.f9c19b80.js.map +0 -1
  29. package/templates/dashboards/dash/src/Dashboard.vue +0 -66
  30. package/templates/dashboards/dash/src/components/BoardsNav.vue +0 -23
  31. package/templates/dashboards/dash/src/components/BoardsPage.vue +0 -36
  32. package/templates/dashboards/dash/src/components/Title.vue +0 -21
  33. package/templates/dashboards/dash/src/definition_dashboard_v59.json +0 -394
@@ -7,7 +7,7 @@
7
7
  "build": "vue-cli-service build"
8
8
  },
9
9
  "dependencies": {
10
- "@cob/dashboard-info": "^2.4.1",
10
+ "@cob/dashboard-info": "^2.6.0",
11
11
  "@cob/rest-api-wrapper": "^2.1.6",
12
12
  "tailwindcss": "^3.0.23",
13
13
  "vue": "^2.6.11"
@@ -1,77 +1,121 @@
1
1
  <template>
2
- <div>
3
- <div v-if="state=='Loading'" class="text-center my-20 text-2xl text-slate-500">
2
+ <div class="h-full w-full">
3
+ <div v-if="dashboardState=='Loading'" class="text-center my-20 text-2xl text-slate-500">
4
4
  Loading...
5
5
  </div>
6
- <div v-else-if="state=='Error'" class="text-center my-20 text-2xl text-error">
6
+ <div v-else-if="dashboardState=='Error'" class="text-center my-20 text-2xl text-red-500">
7
7
  {{error}}
8
8
  </div>
9
- <Dashboard v-else :dashboard="dashboard" />
9
+ <Dashboard v-else :dashboard="dashboardParsed" />
10
10
  </div>
11
11
  </template>
12
12
 
13
13
  <script>
14
- import axios from 'axios';
15
- import { umLoggedin } from '@cob/rest-api-wrapper';
16
- import { instancesList } from '@cob/dashboard-info';
17
- import Dashboard from './Dashboard.vue'
18
- import {parseDashboard} from './collector.js'
14
+ import axios from 'axios';
15
+ import { umLoggedin } from '@cob/rest-api-wrapper';
16
+ import { instancesList } from '@cob/dashboard-info';
17
+ import { parseDashboard } from './collector.js'
18
+ import Dashboard from './components/Dashboard.vue'
19
19
 
20
- export default {
21
- name: 'App',
22
- components: {
23
- Dashboard
24
- },
25
- data: () => ({
26
- userInfo: {},
27
- state: "Loading",
28
- error:"",
29
- currentDashboardInstance: null,
30
- dashboard: null,
31
- page_title: null
32
- }),
33
- created() {
34
- umLoggedin().then( userInfo => this.userInfo = userInfo )
35
- //At the initial load we get the dashboard instance from the url
36
- this.page_title = document.getElementById("dash").getAttribute('data-name')
37
- this.currentDashboardInstance = instancesList("Dashboard", "page_title.raw:\"" + this.page_title + "\"", 1)
20
+ export default {
21
+ name: 'App',
22
+ components: { Dashboard },
23
+ data: () => ({
24
+ userInfo: null,
25
+ name: null,
26
+ error:"",
27
+ dashboardInstance: null,
28
+ dashboardParsed: null,
29
+ dashboardState: "Loading"
30
+ }),
31
+ created() {
32
+ // At the initial load we get the dashboard instance name from the url
33
+ umLoggedin().then( userInfo => {
34
+ let name = document.getElementsByClassName("custom-resource")[0].getAttribute('data-name')
35
+ this.dashboardInstance = instancesList("Dashboard", this.getDashboardQuery(name , userInfo), 1)
36
+ })
38
37
 
39
- $('section.custom-resource').on('resume', (e, params) => {
40
- //register this callback to every anchor navigation. In these cases we get the dashboard instance from the first param to the callback
41
- this.page_title = params[0]
42
- this.currentDashboardInstance.changeArgs({query:"page_title.raw:\"" + this.page_title + "\""})
43
- });
44
- },
45
- watch: {
46
- 'currentDashboardInstance.state'(state) {
47
- if(state == "loading" || state == "updating") {
48
- this.state = "Loading"
49
- } else if(state == "error") {
50
- this.error = "Error: error getting dashboard"
51
- this.state = "Error"
52
- } else if( this.currentDashboardInstance.value.length > 0) {
53
- this.state = "Loading"
54
- let instanceId = this.currentDashboardInstance.value[0].id
55
- axios.get("/recordm/recordm/instances/" + instanceId)
56
- .then(resp => {
57
- try {
58
- this.dashboard = parseDashboard(resp.data)
59
- this.state = "Ready"
60
- }
61
- catch(e) {
62
- this.error = "Error: error processing dashboard " + instanceId
63
- this.state = "Error"
64
- }
65
- })
66
- .catch( (e) => {
67
- this.error = "Error: error getting dashboard " + instanceId
68
- this.state = "Error"
69
- })
70
- } else {
71
- this.error = "Error: dashboard '" + this.page_title + "' not found"
72
- this.state = "Error"
38
+ // Upon anchor navigation we get the dashboard instance name from the first param to the 'resume' callback.
39
+ $('section.custom-resource').on('resume', (e, params) => {
40
+ //Recheck user (the user might have changed or his groups might have changed after previous load)
41
+ umLoggedin().then( userInfo => {
42
+ this.dashboardInstance.changeArgs({query: this.getDashboardQuery(params[0], userInfo) })
43
+ })
44
+ });
45
+ },
46
+ methods: {
47
+ getDashboardQuery(name,userInfo) {
48
+ this.userInfo = userInfo
49
+ this.name = name
50
+ document.title = "Recordm["+name+"]"
51
+ let groups = userInfo.groups.map(g=> "\"" + g.name + "\"").join(" OR ")
52
+
53
+ let nameQuery = "name.raw:\"" + name + "\" "
54
+ let accessQuery = " (groupaccess.raw:(" + groups + ") OR (-groupaccess:*) )"
55
+ return "(" + nameQuery + accessQuery +") OR id:" + name
56
+ }
57
+ },
58
+ watch: {
59
+ // Monitor state changes to the searching of the Dashboard instance
60
+ 'dashboardInstance.state'(instanceInfoState) {
61
+ if(instanceInfoState == "loading" || instanceInfoState == "updating") {
62
+ this.dashboardState = "Loading"
63
+ } else if(instanceInfoState == "error") {
64
+ // Special treatment for 430 (unauthorized) error:
65
+ if(this.dashboardInstance.errorCode == 403) {
66
+ // check who's the new user:
67
+ umLoggedin().then( userInfo => {
68
+ if(userInfo.username == "anonymous") {
69
+ // If the user is anonymous it means we timed out the cookie validity - reload at the same url
70
+ document.location.reload()
71
+ } else {
72
+ // Otherwise the user changed (in another tab) OR the user groups changed OR the dashboards access groups changed
73
+ // send to root
74
+ document.location = "/"
75
+ }
76
+ })
77
+ } else {
78
+ this.dashboardState = "Error"
79
+ this.error = "Error: error getting dashboard (" + this.dashboardInstance.errorCode + ")"
80
+ }
81
+ } else if( this.dashboardInstance.value.length == 0) {
82
+ this.dashboardState = "Error"
83
+ this.error = "Error: a dashboard '" + this.name + "' was not found for your user"
84
+ }
85
+ },
86
+
87
+ // Monitor value changes to the values of the Dashboard instance
88
+ 'dashboardInstance.value'(newDashboards) {
89
+ if(newDashboards.length == 0) {
90
+ this.dashboardState = "Error"
91
+ this.error = "Error: dashboard '" + this.name + "' was not found for your user"
92
+ return
93
+ }
94
+ //Instance(s) found (from ES) but we still need to get the raw instance of the 1st result(from RM) and parse it
95
+ let firstDashId = newDashboards[0].id
96
+ axios
97
+ .get("/recordm/recordm/instances/" + firstDashId)
98
+ .then(resp => {
99
+ try {
100
+ this.dashboardParsed = parseDashboard(resp.data, this.userInfo)
101
+ this.dashboardState = "Ready"
102
+ }
103
+ catch(e) {
104
+ this.error = "Error: error parsing dashboard " + firstDashId
105
+ this.dashboardState = "Error"
106
+ console.error(e)
107
+ }
108
+ })
109
+ .catch( (e) => {
110
+ if( e.response && e.response.status && e.response.status == 403) {
111
+ this.error = "New authorization required..."
112
+ } else {
113
+ this.error = "Error: error getting dashboard " + firstDashId
114
+ }
115
+ this.dashboardState = "Error"
116
+ console.error(e)
117
+ })
73
118
  }
74
119
  }
75
- }
76
- };
77
- </script>
120
+ };
121
+ </script>
@@ -1,6 +1,5 @@
1
1
  import * as dashFunctions from '@cob/dashboard-info';
2
2
  const linkFunction = (url, icon) => { return { value: icon, href: url, state: undefined, isLink: true } }
3
- // dashFunctions["link"] = linkFunction // does not work on brower after deploy (only on localhost dev)
4
3
 
5
4
  const clone = (obj) => JSON.parse(JSON.stringify(obj))
6
5
 
@@ -29,13 +28,18 @@ function collect(bucket, source) {
29
28
  // Means it's additional source matches
30
29
  initialBucketCopy = clone(bucket[sourceName][0].Initial_Template) // we use a copy of the previously copied template
31
30
  }
31
+ initialBucketCopy.instanceId = bucket.instanceId //needed to build $file url
32
32
  children.reduce(collect,initialBucketCopy) // collect values from the children
33
33
  initialBucketCopy[sourceName] = source.value // Add extra field with original name of the source
34
34
  bucket[sourceName].push(initialBucketCopy) // Add to bucket collector
35
35
  }
36
36
  } else {
37
37
  // Means it's not an array and we can collect the final value
38
- bucket[sourceName] = source.value;
38
+ if(source.value && source.fieldDefinition.description && source.fieldDefinition.description.indexOf("$file") >= 0) {
39
+ bucket[sourceName] = "/recordm/recordm/instances/" + bucket.instanceId + "/files/" + source.fieldDefinition.id + "/" + source.value
40
+ } else {
41
+ bucket[sourceName] = source.value;
42
+ }
39
43
  }
40
44
  } else if (children.length > 0) {
41
45
  // Means it didn't find a match. Continue looking in the children
@@ -46,49 +50,95 @@ function collect(bucket, source) {
46
50
  return bucket;
47
51
  }
48
52
 
49
- function parseDashboard(raw_dashboard){
53
+ function parseDashboard(raw_dashboard, userInfo){
50
54
  let dash = {
51
- "Page Title": "",
52
- "Grid Columns": "",
53
- "Max Width": "",
54
- "Board Title": [{
55
- "Col Span": "",
56
- "Row Span": "",
55
+ "Name": "",
56
+ "DashboardCustomize": [{
57
+ "Grid": "",
58
+ "Width": "",
59
+ "DashboardClasses": "",
60
+ "Image": "",
61
+ "GroupAccess": [{}]
62
+ }],
63
+ "Board": [{
64
+ "BoardCustomize": [{
65
+ "BoardClasses": "",
66
+ "Image": ""
67
+ }],
57
68
  "Component": []
58
69
  }],
59
70
  };
60
71
 
72
+ dash.instanceId = ""+raw_dashboard.id //needed to build $file url
61
73
  raw_dashboard.fields.reduce(collect, dash);
62
74
 
63
75
  const ComponentsTemplates = {
64
- "Title": {
65
- "Title": ""
76
+ "Label": {
77
+ "LabelCustomize": [{
78
+ "LabelClasses": "",
79
+ "Image": ""
80
+ }],
81
+ "Label": "",
82
+ },
83
+ "Menu": {
84
+ "MenuCustomize": [{
85
+ "MenuClasses": ""
86
+ }],
87
+ "Text": [{
88
+ "Link": "",
89
+ "TextCustomize": [{
90
+ "TextClasses": "",
91
+ "Icon": "",
92
+ "TextAttention": "",
93
+ "GroupVisibility": [{}]
94
+ }],
95
+ }],
66
96
  },
67
97
  "Totals" : {
68
- "Header": [{
69
- "Text": [{}],
70
- "Style Header": ""
98
+ "TotalsCustomize": [{
99
+ "TotalsClasses": "",
100
+ "InputVarTotals": [{}],
71
101
  }],
72
102
  "Line": [{
73
- "Style Line": "",
103
+ "LineCustomize": [{
104
+ "LineClasses": "",
105
+ "TitleClasses": "",
106
+ }],
74
107
  "Value": [{
108
+ "ValueCustomize": [{
109
+ "ValueClasses": "",
110
+ "View": "",
111
+ "ValueAttention": "",
112
+ "AttentionClasses": "",
113
+ "Unit": "",
114
+ }],
75
115
  "Style Value": "",
76
116
  "Arg": [{}]
77
117
  }]
78
- }]
118
+ }],
79
119
  },
80
- "Menu": {
81
- "Text": [{
82
- "Link": "",
83
- "Style Text": ""
84
- }]
120
+ "Kibana": {
121
+ "KibanaCustomize": [{
122
+ "KibanaClasses": "",
123
+ "OutputVarKibana": "",
124
+ "InputVarKibana": [{}],
125
+ }],
126
+ "ShareLink": "",
127
+ },
128
+ "Filter": {
129
+ "FilterCustomize": [{
130
+ "FilterClasses": ""
131
+ }],
132
+ "OutputVarFilter": "",
85
133
  }
86
134
  }
87
135
 
88
- for( let board of dash["Board Title"]) {
136
+ for( let board of dash["Board"]) {
89
137
  let componentsList = clone([])
90
138
  for( let component of board["Component"]) {
139
+ if(component["Component"] == null) continue
91
140
  let componentTemplate = clone(ComponentsTemplates[component["Component"]])
141
+ componentTemplate.instanceId = ""+raw_dashboard.id //needed to $build file url
92
142
  component.fields.reduce(collect,componentTemplate)
93
143
  componentTemplate["Component"] = component.Component
94
144
  componentsList.push(componentTemplate)
@@ -96,28 +146,50 @@ function parseDashboard(raw_dashboard){
96
146
  board["Component"] = componentsList
97
147
  }
98
148
 
99
- // remove all Initial_Templates added
100
- dash = JSON.parse(JSON.stringify(dash, (k,v) => (k === 'Initial_Template')? undefined : v))
149
+
150
+
151
+ // remove all 'Initial_Templates' and 'instanceId' added for processing
152
+ dash = JSON.parse(JSON.stringify(dash, (k,v) => (k === 'Initial_Template' || k === 'instanceId')? undefined : v))
153
+ dash.vars = {} //Available to every components in component.vars
154
+
155
+ // Add extra info to structure
156
+ dash["Board"].forEach(b => b.Component.forEach(c => {
157
+ // Add user info for permission evaluations
158
+ c.userInfo = userInfo
159
+ c.vars = dash.vars
101
160
 
102
- // replace values in Totals by dashboard-info
103
- dash["Board Title"].forEach(b => b.Component.forEach(c => {
104
- if (c.Component == "Totals") {
161
+ if (c.Component == "Menu") {
162
+ c.Text.forEach(t => {
163
+ // If Attention is configured for this menu line then add attention status as user check
164
+ if(t["TextCustomize"][0]["TextAttention"]) {
165
+ t["TextCustomize"][0].AttentionInfo = dashFunctions.instancesList("Dashboard-Attention","name.raw:" + t["TextCustomize"][0]["TextAttention"],1,0,{validity:30})
166
+ }
167
+ })
168
+ } else if (c.Component == "Totals") {
105
169
  c.Line.forEach(l => {
106
170
  l.Value = l.Value.map(v => {
107
- if(v.Arg[2] && v.Arg[2].startsWith("{")) {
171
+ if(v.Arg[2] && (v.Arg[2]+"").startsWith("{")) {
108
172
  v.Arg[2] = JSON.parse(v.Arg[2])
109
173
  }
110
- const valueFunction = v.Value != "link" ? dashFunctions[v.Value] : linkFunction
111
- return ({
112
- dash_info: valueFunction.apply(this, v['Arg'].map( a => a['Arg'])), // Return DashInfo, which is used by the component
113
- style: v["Style Value"]
114
- })
174
+ // If Attention is configured for this value line then add attention status as user check
175
+ if(v["ValueCustomize"][0]["ValueAttention"]) {
176
+ v["ValueCustomize"][0].AttentionInfo = dashFunctions.instancesList("Dashboard-Attention","name.raw:" + v["ValueCustomize"][0]["ValueAttention"],1,0,{validity:10})
177
+ }
178
+
179
+ if(v.Value == 'Label') {
180
+ v.dash_info = {value: v.Arg[0].Arg, state:"ready"}
181
+ } else if(v.Value == 'link') {
182
+ v.dash_info = { value: icon, href: url, state: undefined, isLink: true }
183
+ } else {
184
+ // add dash-info values in Totals
185
+ v.dash_info = dashFunctions[v.Value].apply(this, v['Arg'].map( a => a['Arg'])) // Return DashInfo, which is used by the component
186
+ }
187
+ return v
115
188
  })
116
189
  })
117
190
  }
118
191
  }))
119
192
  return dash
120
-
121
193
  }
122
194
 
123
- export { parseDashboard, clone, collect }
195
+ export { parseDashboard, clone, collect }
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <i v-if="attentionClasses" :class="attentionClasses" style="line-height: inherit;"></i>
3
+ </template>
4
+
5
+ <script>
6
+ export default {
7
+ props: {
8
+ attentionInfo: Object,
9
+ classes: String
10
+ },
11
+ computed: {
12
+ inputClasses() { return this.classes || "fa-solid fa-circle pr-1 animate-pulse text-lg align-middle" },
13
+ attentionClasses() {
14
+ let attentionInfo = this.attentionInfo
15
+ if(attentionInfo && attentionInfo.value && attentionInfo.value[0]) {
16
+ let severity = attentionInfo.value[0].severity && attentionInfo.value[0].severity[0] || 0
17
+ if (severity == 0) return ""
18
+
19
+ let baseColor = severity > 0 ? "lime" : "red"
20
+ let severityAbs = Math.abs(severity)
21
+ let colorIntensity = severityAbs > 4 ? 500 : severityAbs * 100
22
+ let valenceColor = baseColor + (colorIntensity ? "-" + colorIntensity: "")
23
+ return this.inputClasses + " " + "text-" + valenceColor
24
+ } else {
25
+ return ""
26
+ }
27
+ }
28
+ }
29
+ }
30
+ </script>
@@ -1,61 +1,30 @@
1
1
  <template>
2
- <li :class="wrapperClass">
3
- <div class="py-4 px-5 space-y-2">
4
- <slot></slot>
5
- </div>
6
- </li>
2
+ <div :class="classes" :style="image" >
3
+ <template v-for="(item, i) in components">
4
+ <Label v-if="item['Component'] == 'Label'" :component="item" :key="i" />
5
+ <Menu v-if="item['Component'] == 'Menu'" :component="item" :key="i" />
6
+ <Totals v-if="item['Component'] == 'Totals'" :component="item" :key="i" />
7
+ <Kibana v-if="item['Component'] == 'Kibana'" :component="item" :key="i" />
8
+ <Filtro v-if="item['Component'] == 'Filter'" :component="item" :key="i" />
9
+ </template>
10
+ </div>
7
11
  </template>
8
12
 
9
13
  <script>
10
- export default {
11
- props: {
12
- rowSpan: String,
13
- colSpan: String,
14
- title: String,
15
- showTitle: Boolean
16
- },
17
- computed: {
18
- wrapperClass() {
19
- const c = "w-full bg-cobbg rounded-md border border-cobline" // hover:ring-1 ring-slate-400/50 ring-offset-1;
14
+ import Label from './Label.vue'
15
+ import Menu from './Menu.vue'
16
+ import Totals from './Totals.vue'
17
+ import Kibana from './Kibana.vue'
18
+ import Filtro from './Filter.vue'
20
19
 
21
- // Force possible dynamic classes to be loaded
22
- const dynamicRowClasses = {
23
- 1: "md:row-span-1",
24
- 2: "md:row-span-2",
25
- 3: "md:row-span-3",
26
- 4: "md:row-span-4",
27
- 5: "md:row-span-5",
28
- 6: "md:row-span-6",
29
- 7: "md:row-span-7",
30
- 8: "md:row-span-8",
31
- 9: "md:row-span-9",
32
- 10: "md:row-span-10",
33
- 11: "md:row-span-11",
34
- 12: "md:row-span-12",
35
- "full": "md:row-span-full"
36
- }
37
- const rspan = this.rowSpan ? " " + dynamicRowClasses[this.rowSpan] : "";
38
-
39
- // Force possible dynamic classes to be loaded
40
- const dynamicColClasses = {
41
- 1: "md:col-span-1",
42
- 2: "md:col-span-2",
43
- 3: "md:col-span-3",
44
- 4: "md:col-span-4",
45
- 5: "md:col-span-5",
46
- 6: "md:col-span-6",
47
- 7: "md:col-span-7",
48
- 8: "md:col-span-8",
49
- 9: "md:col-span-9",
50
- 10: "md:col-span-10",
51
- 11: "md:col-span-11",
52
- 12: "md:col-span-12",
53
- "full": "md:col-span-full"
54
- }
55
- const cspan = this.colSpan ? " " + dynamicColClasses[this.colSpan] : "";
56
-
57
- return c + rspan + cspan;
20
+ export default {
21
+ components: { Label, Menu, Totals, Kibana, Filtro },
22
+ props: { board: Object },
23
+ computed: {
24
+ options() { return this.board['BoardCustomize'][0] },
25
+ components() { return this.board['Component'] },
26
+ classes() { return this.options['BoardClasses'] || "md:col-span-4 rounded-md border border-gray-300 p-4 m-1" },
27
+ image() { return this.options['Image'] ? "background-image: url(" + this.options['Image'] + ");" : "" }
58
28
  }
59
29
  }
60
- }
61
30
  </script>
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <div :class="classes" :style="image" >
3
+ <div :class="width + ' ' + grid">
4
+ <Board v-for="(board,i) in boards" :board="board" :key="i"/>
5
+ </div>
6
+ </div>
7
+ </template>
8
+
9
+ <script>
10
+ import Board from './Board.vue'
11
+
12
+ export default {
13
+ components: { Board },
14
+ props: { dashboard: Object },
15
+ computed: {
16
+ options() { return this.dashboard['DashboardCustomize'][0] },
17
+ boards() { return this.dashboard['Board'] },
18
+ classes() { return this.options['DashboardClasses'] || "h-full bg-cover bg-center overflow-auto p-3" },
19
+ width() { return this.options['Width'] || "max-w-6xl mx-auto" },
20
+ grid() { return this.options['Grid'] || "grid grid-flow-row-dense md:grid-cols-12" },
21
+ image() { return this.options['Image'] ? "background-image: url(" + this.options['Image'] + ");" : "" }
22
+ }
23
+ }
24
+ </script>
@@ -0,0 +1,58 @@
1
+ <template>
2
+ <div class="flex h-fit pb-0 justify-center items-center">
3
+ <textarea :class="classes"
4
+ v-model="inputContent"
5
+ ref="textarea"
6
+ @keyup.enter="applyFilter"
7
+ @focus="resize"
8
+ @keyup="resize"
9
+ placeholder="Pesquisar ..."
10
+ ></textarea>
11
+ <button @click="applyFilter" type="submit" class="max-h-11 p-2.5 ml-2 text-sm font-medium text-white bg-blue-700 rounded-lg border border-blue-700 hover:bg-blue-800">
12
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
13
+ </button>
14
+ </div>
15
+ </template>
16
+
17
+ <script>
18
+ export default {
19
+ props: { component: Object },
20
+ data: () => ({
21
+ inputContent: "",
22
+ activeFilter: ""
23
+ }),
24
+ created() {
25
+ this.$nextTick(() => {
26
+ this.applyFilter()
27
+ this.resize()
28
+ })
29
+ },
30
+ updated() {
31
+ // Necessário para quando fazemos back para uma página com o inputContent preenchido mas que não está actualizada nas variáveis
32
+ if(this.activeFilter != this.component.vars[this.outputVar]) {
33
+ this.applyFilter()
34
+ }
35
+ },
36
+ computed: {
37
+ options() { return this.component['FilterCustomize'][0] },
38
+ outputVar() { return this.component['OutputVarFilter'] || "" },
39
+ classes() { return this.options['FilterClasses'] || "w-full max-w-xs resize-none min-h-min h-min border border-slate-300 rounded-md py-2 px-2 outline-slate-300 leading-5" },
40
+ },
41
+ methods: {
42
+ applyFilter: function() {
43
+ this.inputContent = this.inputContent.trim()
44
+ this.activeFilter = "(" + (this.inputContent ? this.inputContent.replace(/\n/,' ') : "*") +")"
45
+ this.$set(this.component.vars,this.outputVar,this.activeFilter)
46
+ },
47
+ resize() {
48
+ const { textarea } = this.$refs;
49
+ if(this.inputContent && ( textarea.textLength >= textarea.cols || this.inputContent.split("\n").length > 1) ) {
50
+ textarea.style.height = "auto";
51
+ textarea.style.height = (textarea.scrollHeight + 2) + 'px'; // Os 14px são do padding acrescentado
52
+ } else {
53
+ textarea.style.height = "40px";
54
+ }
55
+ }
56
+ }
57
+ }
58
+ </script>