@mwater/visualization 5.4.5 → 5.5.0

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 (132) hide show
  1. package/.storybook/head.html +0 -1
  2. package/lib/MWaterContextComponent.js +1 -1
  3. package/lib/MWaterLoaderComponent.d.ts +2 -2
  4. package/lib/dashboards/DashboardComponent.js +2 -1
  5. package/lib/dashboards/LayoutOptionsComponent.js +18 -11
  6. package/lib/dashboards/ServerDashboardDataSource.d.ts +10 -1
  7. package/lib/dashboards/ServerDashboardDataSource.js +29 -0
  8. package/lib/dashboards/layoutOptions.d.ts +5 -1
  9. package/lib/datagrids/DatagridComponent.js +1 -1
  10. package/lib/datagrids/ExprCellComponent.d.ts +1 -0
  11. package/lib/datagrids/ExprCellComponent.js +22 -20
  12. package/lib/maps/BufferLayer.d.ts +18 -0
  13. package/lib/maps/BufferLayer.js +24 -14
  14. package/lib/maps/ChoroplethLayer.d.ts +18 -0
  15. package/lib/maps/ChoroplethLayer.js +34 -25
  16. package/lib/maps/ChoroplethLayerDesign.d.ts +3 -2
  17. package/lib/maps/ChoroplethLayerDesigner.d.ts +11 -1
  18. package/lib/maps/DirectMapDataSource.js +17 -0
  19. package/lib/maps/EditHoverOver.d.ts +1 -1
  20. package/lib/maps/EditHoverOver.js +62 -33
  21. package/lib/maps/HoverContent.d.ts +10 -5
  22. package/lib/maps/HoverContent.js +6 -35
  23. package/lib/maps/Layer.d.ts +37 -0
  24. package/lib/maps/Layer.js +30 -4
  25. package/lib/maps/MWaterServerLayer.d.ts +2 -2
  26. package/lib/maps/MWaterServerLayer.js +6 -6
  27. package/lib/maps/MapLayerDataSource.d.ts +9 -0
  28. package/lib/maps/MapUtils.d.ts +19 -1
  29. package/lib/maps/MapUtils.js +71 -1
  30. package/lib/maps/MarkersLayer.d.ts +18 -0
  31. package/lib/maps/MarkersLayer.js +24 -24
  32. package/lib/maps/MarkersLayerDesignerComponent.d.ts +14 -1
  33. package/lib/maps/RasterMapViewComponent.js +1 -1
  34. package/lib/maps/ServerMapDataSource.d.ts +9 -0
  35. package/lib/maps/ServerMapDataSource.js +29 -0
  36. package/lib/maps/VectorMapViewComponent.js +6 -6
  37. package/lib/maps/maps.d.ts +4 -2
  38. package/lib/mwater_table_selection/FormsListComponent.d.ts +33 -0
  39. package/lib/mwater_table_selection/FormsListComponent.js +141 -0
  40. package/lib/mwater_table_selection/IndicatorsListComponent.d.ts +47 -0
  41. package/lib/mwater_table_selection/IndicatorsListComponent.js +182 -0
  42. package/lib/mwater_table_selection/IssuesListComponent.d.ts +29 -0
  43. package/lib/mwater_table_selection/IssuesListComponent.js +123 -0
  44. package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.d.ts +20 -0
  45. package/lib/mwater_table_selection/MWaterAccountingSystemListComponent.js +157 -0
  46. package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.d.ts +17 -0
  47. package/lib/mwater_table_selection/MWaterAssetSystemsListComponent.js +79 -0
  48. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.d.ts +37 -0
  49. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.js +275 -0
  50. package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.d.ts +17 -0
  51. package/lib/mwater_table_selection/MWaterCustomTablesetListComponent.js +94 -0
  52. package/lib/mwater_table_selection/MWaterMetricsTableListComponent.d.ts +17 -0
  53. package/lib/mwater_table_selection/MWaterMetricsTableListComponent.js +80 -0
  54. package/lib/mwater_table_selection/MWaterTableSelectComponent.d.ts +32 -0
  55. package/lib/mwater_table_selection/MWaterTableSelectComponent.js +158 -0
  56. package/lib/widgets/charts/Chart.d.ts +11 -0
  57. package/lib/widgets/charts/Chart.js +15 -0
  58. package/lib/widgets/charts/ChartWidgetComponent.d.ts +1 -0
  59. package/lib/widgets/charts/ChartWidgetComponent.js +27 -1
  60. package/lib/widgets/charts/layered/LayeredChartDesign.d.ts +1 -1
  61. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.d.ts +1 -1
  62. package/lib/widgets/charts/layered/LayeredChartDesignerComponent.js +5 -12
  63. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.d.ts +43 -57
  64. package/lib/widgets/charts/layered/LayeredChartLayerDesignerComponent.js +113 -110
  65. package/lib/widgets/charts/layered/LayeredChartUtils.d.ts +2 -1
  66. package/lib/widgets/charts/layered/LayeredChartUtils.js +0 -2
  67. package/lib/widgets/charts/pivot/PivotChart.d.ts +2 -0
  68. package/lib/widgets/charts/pivot/PivotChart.js +156 -0
  69. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.d.ts +5 -20
  70. package/lib/widgets/charts/pivot/PivotChartDesignerComponent.js +31 -61
  71. package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.d.ts +4 -0
  72. package/lib/widgets/charts/pivot/PivotChartLayoutBuilder.js +4 -2
  73. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.d.ts +5 -44
  74. package/lib/widgets/charts/pivot/PivotChartLayoutComponent.js +38 -63
  75. package/lib/widgets/charts/pivot/SegmentDesignerComponent.d.ts +7 -68
  76. package/lib/widgets/charts/pivot/SegmentDesignerComponent.js +58 -106
  77. package/lib/widgets/charts/table/TableChart.d.ts +2 -0
  78. package/lib/widgets/charts/table/TableChart.js +172 -1
  79. package/lib/widgets/charts/table/TableChartDesignerComponent.d.ts +7 -17
  80. package/lib/widgets/charts/table/TableChartDesignerComponent.js +79 -95
  81. package/lib/widgets/charts/table/TableChartViewComponent.d.ts +1 -7
  82. package/lib/widgets/charts/table/TableChartViewComponent.js +19 -27
  83. package/package.json +3 -8
  84. package/src/MWaterContextComponent.tsx +1 -1
  85. package/src/MWaterLoaderComponent.ts +1 -1
  86. package/src/dashboards/DashboardComponent.tsx +2 -1
  87. package/src/dashboards/LayoutOptionsComponent.tsx +22 -10
  88. package/src/dashboards/ServerDashboardDataSource.ts +36 -1
  89. package/src/dashboards/layoutOptions.tsx +5 -1
  90. package/src/datagrids/DatagridComponent.tsx +1 -1
  91. package/src/datagrids/ExprCellComponent.tsx +23 -20
  92. package/src/maps/BufferLayer.ts +35 -20
  93. package/src/maps/ChoroplethLayer.ts +51 -33
  94. package/src/maps/ChoroplethLayerDesign.ts +3 -2
  95. package/src/maps/ChoroplethLayerDesigner.tsx +2 -2
  96. package/src/maps/DirectMapDataSource.ts +21 -1
  97. package/src/maps/EditHoverOver.tsx +91 -51
  98. package/src/maps/HoverContent.tsx +16 -47
  99. package/src/maps/Layer.ts +42 -4
  100. package/src/maps/MWaterServerLayer.ts +6 -6
  101. package/src/maps/MapLayerDataSource.ts +8 -0
  102. package/src/maps/MapUtils.ts +70 -3
  103. package/src/maps/MarkersLayer.ts +34 -24
  104. package/src/maps/RasterMapViewComponent.ts +1 -1
  105. package/src/maps/ServerMapDataSource.ts +35 -0
  106. package/src/maps/VectorMapViewComponent.tsx +6 -6
  107. package/src/maps/maps.ts +4 -2
  108. package/src/mwater_table_selection/FormsListComponent.tsx +188 -0
  109. package/src/mwater_table_selection/IndicatorsListComponent.tsx +283 -0
  110. package/src/mwater_table_selection/IssuesListComponent.tsx +167 -0
  111. package/src/mwater_table_selection/MWaterAccountingSystemListComponent.tsx +225 -0
  112. package/src/{MWaterAssetSystemsListComponent.tsx → mwater_table_selection/MWaterAssetSystemsListComponent.tsx} +2 -2
  113. package/src/mwater_table_selection/MWaterCompleteTableSelectComponent.tsx +377 -0
  114. package/src/{MWaterCustomTablesetListComponent.tsx → mwater_table_selection/MWaterCustomTablesetListComponent.tsx} +1 -1
  115. package/src/{MWaterMetricsTableListComponent.tsx → mwater_table_selection/MWaterMetricsTableListComponent.tsx} +1 -1
  116. package/src/{MWaterTableSelectComponent.tsx → mwater_table_selection/MWaterTableSelectComponent.tsx} +83 -86
  117. package/src/widgets/charts/Chart.ts +17 -0
  118. package/src/widgets/charts/ChartWidgetComponent.tsx +36 -1
  119. package/src/widgets/charts/layered/LayeredChartDesign.ts +1 -1
  120. package/src/widgets/charts/layered/LayeredChartDesignerComponent.tsx +23 -24
  121. package/src/widgets/charts/layered/LayeredChartLayerDesignerComponent.tsx +260 -211
  122. package/src/widgets/charts/layered/LayeredChartUtils.ts +7 -7
  123. package/src/widgets/charts/pivot/PivotChart.ts +191 -0
  124. package/src/widgets/charts/pivot/PivotChartDesignerComponent.tsx +124 -129
  125. package/src/widgets/charts/pivot/PivotChartLayoutBuilder.ts +4 -2
  126. package/src/widgets/charts/pivot/PivotChartLayoutComponent.tsx +120 -149
  127. package/src/widgets/charts/pivot/SegmentDesignerComponent.tsx +178 -198
  128. package/src/widgets/charts/table/TableChart.ts +177 -1
  129. package/src/widgets/charts/table/TableChartDesignerComponent.tsx +422 -0
  130. package/src/widgets/charts/table/{TableChartViewComponent.ts → TableChartViewComponent.tsx} +65 -60
  131. package/src/MWaterCompleteTableSelectComponent.tsx +0 -975
  132. package/src/widgets/charts/table/TableChartDesignerComponent.ts +0 -441
@@ -1,975 +0,0 @@
1
- import _ from "lodash"
2
- import $ from "jquery"
3
- import React from "react"
4
- const R = React.createElement
5
- import querystring from "querystring"
6
- import { default as TabbedComponent, TabbedComponentTab } from "@mwater/react-library/lib/TabbedComponent"
7
- import * as uiComponents from "./UIComponents"
8
- import { ExprUtils, Schema } from "@mwater/expressions"
9
- import moment from "moment"
10
- import ModalPopupComponent from "@mwater/react-library/lib/ModalPopupComponent"
11
- import { MWaterCustomTablesetListComponent } from "./MWaterCustomTablesetListComponent"
12
- import { MWaterMetricsTableListComponent } from "./MWaterMetricsTableListComponent"
13
- import { Form } from "@mwater/forms"
14
- import { MWaterAssetSystemsListComponent } from "./MWaterAssetSystemsListComponent"
15
-
16
- const sitesOrder: { [table: string]: number } = {
17
- "entities.water_point": 1,
18
- "entities.sanitation_facility": 2,
19
- "entities.household": 3,
20
- "entities.community": 4,
21
- "entities.school": 5,
22
- "entities.health_facility": 6,
23
- "entities.place_of_worship": 7,
24
- "entities.water_system": 8,
25
- "entities.water_system_component": 9,
26
- "entities.wastewater_treatment_system": 10,
27
- "entities.waste_disposal_site": 11
28
- }
29
-
30
- /** Entity types that are assets */
31
- const assetEntities = [
32
- "entities.water_asset"
33
- ]
34
-
35
- interface MWaterCompleteTableSelectComponentProps {
36
- /** Url to hit api */
37
- apiUrl: string
38
- /** Optional client */
39
- client?: string
40
- schema: Schema
41
- /** User id */
42
- user?: string
43
- table?: string
44
- /** Called with table selected */
45
- onChange: any
46
- extraTables: any
47
- onExtraTablesChange: any
48
- }
49
-
50
- // Allows selection of a table. Is the complete list mode of tables
51
- export default class MWaterCompleteTableSelectComponent extends React.Component<MWaterCompleteTableSelectComponentProps, {
52
- showLegacyAssets: boolean
53
- }> {
54
- constructor(props: MWaterCompleteTableSelectComponentProps) {
55
- super(props)
56
-
57
- this.state = {
58
- showLegacyAssets: false
59
- }
60
- }
61
-
62
- handleExtraTableAdd = (tableId: any) => {
63
- return this.props.onExtraTablesChange(_.union(this.props.extraTables, [tableId]))
64
- }
65
-
66
- handleExtraTableRemove = (tableId: any) => {
67
- // Set to null if current table
68
- if (this.props.table === tableId) {
69
- this.props.onChange(null)
70
- }
71
-
72
- return this.props.onExtraTablesChange(_.without(this.props.extraTables, tableId))
73
- }
74
-
75
- renderSites() {
76
- let table
77
- let types = []
78
-
79
- for (table of this.props.schema.getTables()) {
80
- if (table.deprecated) {
81
- continue
82
- }
83
-
84
- if (!table.id.match(/^entities\./)) {
85
- continue
86
- }
87
-
88
- // Skip assets
89
- if (assetEntities.includes(table.id)) {
90
- continue
91
- }
92
-
93
- types.push(table.id)
94
- }
95
-
96
- // Sort by order if present
97
- types = _.sortBy(types, (type) => sitesOrder[type] || 999)
98
-
99
- return R(uiComponents.OptionListComponent, {
100
- items: _.compact(
101
- _.map(types, (tableId) => {
102
- table = this.props.schema.getTable(tableId)!
103
- return {
104
- name: ExprUtils.localizeString(table.name, T.locale),
105
- desc: ExprUtils.localizeString(table.desc, T.locale),
106
- onClick: this.props.onChange.bind(null, table.id)
107
- }
108
- })
109
- )
110
- })
111
- }
112
-
113
- renderForms() {
114
- return R(FormsListComponent, {
115
- schema: this.props.schema,
116
- client: this.props.client,
117
- apiUrl: this.props.apiUrl,
118
- user: this.props.user,
119
- onChange: this.props.onChange,
120
- extraTables: this.props.extraTables,
121
- onExtraTableAdd: this.handleExtraTableAdd,
122
- onExtraTableRemove: this.handleExtraTableRemove
123
- })
124
- }
125
-
126
- renderIndicators() {
127
- return R(IndicatorsListComponent, {
128
- schema: this.props.schema,
129
- client: this.props.client,
130
- apiUrl: this.props.apiUrl,
131
- user: this.props.user,
132
- onChange: this.props.onChange,
133
- extraTables: this.props.extraTables,
134
- onExtraTableAdd: this.handleExtraTableAdd,
135
- onExtraTableRemove: this.handleExtraTableRemove
136
- })
137
- }
138
-
139
- renderIssues() {
140
- return R(IssuesListComponent, {
141
- schema: this.props.schema,
142
- client: this.props.client,
143
- apiUrl: this.props.apiUrl,
144
- user: this.props.user,
145
- onChange: this.props.onChange,
146
- extraTables: this.props.extraTables,
147
- onExtraTableAdd: this.handleExtraTableAdd,
148
- onExtraTableRemove: this.handleExtraTableRemove
149
- })
150
- }
151
-
152
- renderSweetSense() {
153
- let sweetSenseTables = this.getSweetSenseTables()
154
-
155
- sweetSenseTables = _.sortBy(sweetSenseTables, (table) => table.name.en)
156
- return R(uiComponents.OptionListComponent, {
157
- items: _.map(sweetSenseTables, (table) => {
158
- return {
159
- name: ExprUtils.localizeString(table.name, T.locale),
160
- desc: ExprUtils.localizeString(table.desc, T.locale),
161
- onClick: this.props.onChange.bind(null, table.id)
162
- }
163
- })
164
- })
165
- }
166
-
167
- renderTablesets() {
168
- return R(MWaterCustomTablesetListComponent, {
169
- schema: this.props.schema,
170
- client: this.props.client,
171
- apiUrl: this.props.apiUrl,
172
- user: this.props.user,
173
- onChange: this.props.onChange,
174
- extraTables: this.props.extraTables,
175
- onExtraTableAdd: this.handleExtraTableAdd,
176
- onExtraTableRemove: this.handleExtraTableRemove,
177
- locale: T.locale
178
- })
179
- }
180
-
181
- renderMetrics() {
182
- return R(MWaterMetricsTableListComponent, {
183
- schema: this.props.schema,
184
- client: this.props.client,
185
- apiUrl: this.props.apiUrl,
186
- user: this.props.user,
187
- onChange: this.props.onChange,
188
- extraTables: this.props.extraTables,
189
- onExtraTableAdd: this.handleExtraTableAdd,
190
- onExtraTableRemove: this.handleExtraTableRemove,
191
- locale: T.locale
192
- })
193
- }
194
-
195
- renderAssets() {
196
- const items: {
197
- name: string
198
- desc?: string
199
- onClick: () => void
200
- }[] = []
201
-
202
- for (const tableId of assetEntities) {
203
- const table = this.props.schema.getTable(tableId)
204
- if (!table) {
205
- continue
206
- }
207
- items.push({
208
- name: ExprUtils.localizeString(table.name, T.locale),
209
- desc: ExprUtils.localizeString(table.desc, T.locale),
210
- onClick: this.props.onChange.bind(null, table.id)
211
- })
212
- }
213
-
214
- return <div>
215
- <uiComponents.OptionListComponent items={items} />
216
- <div className="text-center mt-2 mb-2">
217
- <button className="btn btn-link" onClick={() => this.setState({ showLegacyAssets: !this.state.showLegacyAssets })}>
218
- {this.state.showLegacyAssets ? T`Hide` : T`Show`} {T`Legacy Assets`}
219
- </button>
220
- </div>
221
- {this.state.showLegacyAssets && this.renderLegacyAssets()}
222
- </div>
223
- }
224
-
225
-
226
- renderLegacyAssets() {
227
- return R(MWaterAssetSystemsListComponent, {
228
- schema: this.props.schema,
229
- client: this.props.client,
230
- apiUrl: this.props.apiUrl,
231
- user: this.props.user,
232
- onChange: this.props.onChange,
233
- extraTables: this.props.extraTables,
234
- onExtraTableAdd: this.handleExtraTableAdd,
235
- onExtraTableRemove: this.handleExtraTableRemove,
236
- locale: T.locale
237
- })
238
- }
239
-
240
- renderOther() {
241
- let otherTables = _.filter(this.props.schema.getTables(), (table) => {
242
- // Remove deprecated
243
- if (table.deprecated) {
244
- return false
245
- }
246
-
247
- // Remove sites
248
- if (table.id.match(/^entities\./)) {
249
- return false
250
- }
251
-
252
- // sweetsense tables
253
- if (table.id.match(/^sweetsense/)) {
254
- return false
255
- }
256
-
257
- // Remove responses
258
- if (table.id.match(/^responses:/)) {
259
- return false
260
- }
261
-
262
- // Remove indicators
263
- if (table.id.match(/^indicator_values:/)) {
264
- return false
265
- }
266
-
267
- // Remove issues
268
- if (table.id.match(/^(issues|issue_events):/)) {
269
- return false
270
- }
271
-
272
- // Remove custom tablesets
273
- if (table.id.match(/^custom\./)) {
274
- return false
275
- }
276
-
277
- // Remove metrics
278
- if (table.id.match(/^metrics:/)) {
279
- return false
280
- }
281
-
282
- // Remove assets
283
- if (table.id.match(/^assets:/)) {
284
- return false
285
- }
286
-
287
- return true
288
- })
289
-
290
- otherTables = _.sortBy(otherTables, (table) => table.name.en)
291
- return R(uiComponents.OptionListComponent, {
292
- items: _.map(otherTables, (table) => {
293
- return {
294
- name: ExprUtils.localizeString(table.name, T.locale),
295
- desc: ExprUtils.localizeString(table.desc, T.locale),
296
- onClick: this.props.onChange.bind(null, table.id)
297
- }
298
- })
299
- })
300
- }
301
-
302
- getSweetSenseTables() {
303
- return _.filter(this.props.schema.getTables(), (table) => {
304
- if (table.deprecated) {
305
- return false
306
- }
307
-
308
- if (table.id.match(/^sweetsense/)) {
309
- return true
310
- }
311
-
312
- return false
313
- })
314
- }
315
-
316
- render() {
317
- const sweetSenseTables = this.getSweetSenseTables()
318
-
319
- const tabs: TabbedComponentTab[] = [
320
- { id: "sites", label: [R("i", { className: "fa fa-map-marker" }), " ", T`Sites`], elem: this.renderSites() },
321
- { id: "forms", label: [R("i", { className: "fa fa-th-list" }), " ", T`Surveys`], elem: this.renderForms() },
322
- {
323
- id: "indicators",
324
- label: [R("i", { className: "fa fa-check-circle" }), " ", T`Indicators`],
325
- elem: this.renderIndicators()
326
- },
327
- {
328
- id: "issues",
329
- label: [R("i", { className: "fa fa-exclamation-circle" }), " ", T`Issues`],
330
- elem: this.renderIssues()
331
- },
332
- { id: "tablesets", label: [R("i", { className: "fa fa-table" }), " ", T`Tables`], elem: this.renderTablesets() },
333
- { id: "metrics", label: [R("i", { className: "fa fa-line-chart" }), " ", T`Metrics`], elem: this.renderMetrics() },
334
- { id: "assets", label: [R("i", { className: "fas fa-map-pin" }), " ", T`Assets`], elem: this.renderAssets() }
335
- ]
336
-
337
- if (sweetSenseTables.length > 0) {
338
- tabs.push({ id: "sensors", label: T`Sensors`, elem: this.renderSweetSense() })
339
- }
340
-
341
- tabs.push({ id: "other", label: T`Advanced`, elem: this.renderOther() })
342
-
343
- return R(
344
- "div",
345
- null,
346
- R(
347
- "div",
348
- { className: "text-muted" },
349
- T`Select data from sites, surveys or an advanced category below. Indicators can be found within their associated site types.`
350
- ),
351
-
352
- R(TabbedComponent, {
353
- tabs,
354
- initialTabId: "sites"
355
- })
356
- )
357
- }
358
- }
359
-
360
- interface FormsListComponentProps {
361
- /** Url to hit api */
362
- apiUrl: string
363
- /** Optional client */
364
- client?: string
365
- schema: Schema
366
- /** User id */
367
- user?: string
368
- /** Called with table selected */
369
- onChange: any
370
- extraTables: any
371
- onExtraTableAdd: any
372
- onExtraTableRemove: any
373
- }
374
-
375
- interface FormsListComponentState {
376
- error?: any
377
- search: any
378
- forms: { id: string, name: string, desc?: string }[] | null
379
- }
380
-
381
- // Searchable list of forms
382
- class FormsListComponent extends React.Component<FormsListComponentProps, FormsListComponentState> {
383
- constructor(props: any) {
384
- super(props)
385
- this.state = {
386
- forms: null,
387
- search: ""
388
- }
389
- }
390
-
391
- componentDidMount() {
392
- // Get names and basic of forms
393
- const query: any = {}
394
- query.fields = JSON.stringify({
395
- "design.name": 1,
396
- "design.description": 1,
397
- roles: 1,
398
- created: 1,
399
- modified: 1,
400
- state: 1,
401
- isMaster: 1
402
- })
403
- query.selector = JSON.stringify({ design: { $exists: true }, state: { $ne: "deleted" } })
404
- query.client = this.props.client
405
-
406
- // Get list of all form names
407
- $.getJSON(this.props.apiUrl + "forms?" + querystring.stringify(query), (forms: Form[]) => {
408
- // Sort by modified.on desc but first by user
409
- forms = _.sortByOrder(
410
- forms,
411
- [
412
- (form: Form) => ((this.props.extraTables || []).includes("responses:" + form._id) ? 1 : 0),
413
- (form: Form) => (form.created.by === this.props.user ? 1 : 0),
414
- (form: Form) => form.modified?.on
415
- ],
416
- ["desc", "desc", "desc"]
417
- )
418
-
419
- // TODO use name instead of design.name
420
- this.setState({
421
- forms: _.map(forms, (form) => {
422
- let desc = ExprUtils.localizeString(form.design.description, null) || ""
423
- if (desc) {
424
- desc += " - "
425
- }
426
- desc += T`Modified ${moment(form.modified?.on, moment.ISO_8601).format("ll")}`
427
-
428
- return {
429
- id: form._id,
430
- name: ExprUtils.localizeString(form.design.name, null),
431
- desc
432
- }
433
- })
434
- })
435
- }).fail((xhr: any) => {
436
- this.setState({ error: xhr.responseText })
437
- })
438
- }
439
-
440
- handleTableRemove = (table: any) => {
441
- if (
442
- confirm(
443
- T`Remove ${ExprUtils.localizeString(
444
- table.name,
445
- T.locale
446
- )}? Any widgets that depend on it will no longer work properly.`
447
- )
448
- ) {
449
- return this.props.onExtraTableRemove(table.id)
450
- }
451
- }
452
-
453
- searchRef = (comp: any) => {
454
- // Focus
455
- if (comp) {
456
- return comp.focus()
457
- }
458
- }
459
-
460
- render() {
461
- let forms
462
- if (this.state.error) {
463
- return R("div", { className: "alert alert-danger" }, this.state.error)
464
- }
465
-
466
- // Filter forms
467
- if (this.state.search) {
468
- const escapeRegExp = (s: any) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")
469
-
470
- const searchStringRegExp = new RegExp(escapeRegExp(this.state.search), "i")
471
-
472
- forms = _.filter(this.state.forms!, (form) => form.name.match(searchStringRegExp))
473
- } else {
474
- ;({ forms } = this.state)
475
- }
476
-
477
- // Remove if already included
478
- forms = _.filter(forms || [], (f) => !(this.props.extraTables || []).includes(`responses:${f.id}`))
479
-
480
- let tables = _.filter(
481
- this.props.schema.getTables(),
482
- (table) => (table.id.match(/^responses:/) || table.id.match(/^master_responses:/)) && !table.deprecated
483
- )
484
- tables = _.sortBy(tables, (t) => t.name.en)
485
-
486
- return R(
487
- "div",
488
- null,
489
- R("label", null, T`Included Surveys:`),
490
- tables.length > 0
491
- ? R(uiComponents.OptionListComponent, {
492
- items: _.map(tables, (table) => {
493
- return {
494
- name: ExprUtils.localizeString(table.name, T.locale),
495
- desc: ExprUtils.localizeString(table.desc, T.locale),
496
- onClick: this.props.onChange.bind(null, table.id),
497
- onRemove: this.handleTableRemove.bind(null, table)
498
- }
499
- })
500
- })
501
- : R("div", null, T`None`),
502
-
503
- R("br"),
504
-
505
- R("label", null, T`All Surveys:`),
506
- !this.state.forms || this.state.forms.length === 0
507
- ? R(
508
- "div",
509
- { className: "alert alert-info" },
510
- R("i", { className: "fa fa-spinner fa-spin" }),
511
- "\u00A0" + T`Loading...`
512
- )
513
- : [
514
- R("input", {
515
- type: "text",
516
- className: "form-control form-control-sm",
517
- placeholder: T`Search...`,
518
- key: "search",
519
- ref: this.searchRef,
520
- style: { maxWidth: "20em", marginBottom: 10 },
521
- value: this.state.search,
522
- onChange: (ev: any) => this.setState({ search: ev.target.value })
523
- }),
524
-
525
- R(uiComponents.OptionListComponent, {
526
- items: _.map(forms, (form) => ({
527
- name: form.name,
528
- desc: form.desc,
529
- onClick: this.props.onChange.bind(null, "responses:" + form.id)
530
- }))
531
- })
532
- ]
533
- )
534
- }
535
- }
536
-
537
- interface IndicatorsListComponentProps {
538
- /** Url to hit api */
539
- apiUrl: string
540
- /** Optional client */
541
- client?: string
542
- schema: Schema
543
- /** User id */
544
- user?: string
545
- /** Called with table selected */
546
- onChange: any
547
- extraTables: any
548
- onExtraTableAdd: any
549
- onExtraTableRemove: any
550
- }
551
-
552
- interface IndicatorsListComponentState {
553
- error?: any
554
- search: any
555
- indicators: any[] | null
556
- }
557
-
558
- // Searchable list of indicators
559
- class IndicatorsListComponent extends React.Component<IndicatorsListComponentProps, IndicatorsListComponentState> {
560
- addIndicatorConfirmPopup: AddIndicatorConfirmPopupComponent | null
561
-
562
- constructor(props: any) {
563
- super(props)
564
- this.state = {
565
- indicators: null,
566
- search: ""
567
- }
568
- }
569
-
570
- componentDidMount() {
571
- // Get names and basic of forms
572
- const query: any = {}
573
- query.fields = JSON.stringify({ "design.name": 1, "design.desc": 1, "design.recommended": 1, deprecated: 1 })
574
- query.client = this.props.client
575
-
576
- // Get list of all indicator names
577
- return $.getJSON(this.props.apiUrl + "indicators?" + querystring.stringify(query), (indicators: any[]) => {
578
- // Remove deprecated
579
- indicators = _.filter(indicators, (indicator) => !indicator.deprecated)
580
-
581
- // Sort by name
582
- indicators = _.sortByOrder(
583
- indicators,
584
- [
585
- (indicator: any) => ((this.props.extraTables || []).includes("indicator_values:" + indicator._id) ? 0 : 1),
586
- (indicator: any) => (indicator.design.recommended ? 0 : 1),
587
- (indicator: any) => ExprUtils.localizeString(indicator.design.name, T.locale)
588
- ],
589
- ["asc", "asc", "asc"]
590
- )
591
-
592
- return this.setState({
593
- indicators: _.map(indicators, (indicator) => ({
594
- id: indicator._id,
595
- name: ExprUtils.localizeString(indicator.design.name, T.locale),
596
- desc: ExprUtils.localizeString(indicator.design.desc, T.locale)
597
- }))
598
- })
599
- }).fail((xhr: any) => {
600
- return this.setState({ error: xhr.responseText })
601
- })
602
- }
603
-
604
- handleTableRemove = (table: any) => {
605
- if (
606
- confirm(
607
- T`Remove ${ExprUtils.localizeString(
608
- table.name,
609
- T.locale
610
- )}? Any widgets that depend on it will no longer work properly.`
611
- )
612
- ) {
613
- return this.props.onExtraTableRemove(table.id)
614
- }
615
- }
616
-
617
- searchRef = (comp: any) => {
618
- // Focus
619
- if (comp) {
620
- return comp.focus()
621
- }
622
- }
623
-
624
- handleSelect = (tableId: any) => {
625
- // Add table if not present
626
- if (!this.props.schema.getTable(tableId)) {
627
- this.props.onExtraTableAdd(tableId)
628
- }
629
-
630
- this.addIndicatorConfirmPopup!.show(tableId)
631
- }
632
-
633
- render() {
634
- let indicators
635
- if (this.state.error) {
636
- return R("div", { className: "alert alert-danger" }, this.state.error)
637
- }
638
-
639
- // Filter indicators
640
- if (this.state.search) {
641
- const escapeRegExp = (s: any) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")
642
-
643
- const searchStringRegExp = new RegExp(escapeRegExp(this.state.search), "i")
644
-
645
- indicators = _.filter(this.state.indicators || [], (indicator) => indicator.name.match(searchStringRegExp))
646
- } else {
647
- ;({ indicators } = this.state)
648
- }
649
-
650
- // Remove if already included
651
- indicators = _.filter(indicators || [], (f) => !(this.props.extraTables || []).includes(`indicator_values:${f.id}`))
652
-
653
- let tables = _.filter(
654
- this.props.schema.getTables(),
655
- (table) => table.id.match(/^indicator_values:/) && !table.deprecated
656
- )
657
- tables = _.sortBy(tables, (t) => t.name.en)
658
-
659
- return R(
660
- "div",
661
- null,
662
- R(AddIndicatorConfirmPopupComponent, {
663
- schema: this.props.schema,
664
- onChange: this.props.onChange,
665
- onExtraTableAdd: this.props.onExtraTableAdd,
666
- ref: (c: AddIndicatorConfirmPopupComponent | null) => {
667
- this.addIndicatorConfirmPopup = c
668
- }
669
- }),
670
-
671
- R("label", null, T`Included Indicators:`),
672
- tables.length > 0
673
- ? R(uiComponents.OptionListComponent, {
674
- items: _.map(tables, (table) => {
675
- return {
676
- name: ExprUtils.localizeString(table.name, T.locale),
677
- desc: ExprUtils.localizeString(table.desc, T.locale),
678
- onClick: this.handleSelect.bind(null, table.id),
679
- onRemove: this.handleTableRemove.bind(null, table)
680
- }
681
- })
682
- })
683
- : R("div", null, T`None`),
684
-
685
- R("br"),
686
-
687
- R("label", null, T`All Indicators:`),
688
- !this.state.indicators || this.state.indicators.length === 0
689
- ? R(
690
- "div",
691
- { className: "alert alert-info" },
692
- R("i", { className: "fa fa-spinner fa-spin" }),
693
- "\u00A0" + T`Loading...`
694
- )
695
- : [
696
- R("input", {
697
- type: "text",
698
- className: "form-control form-control-sm",
699
- placeholder: T`Search...`,
700
- key: "search",
701
- ref: this.searchRef,
702
- style: { maxWidth: "20em", marginBottom: 10 },
703
- value: this.state.search,
704
- onChange: (ev: any) => this.setState({ search: ev.target.value })
705
- }),
706
-
707
- R(uiComponents.OptionListComponent, {
708
- items: _.map(indicators, (indicator) => ({
709
- name: indicator.name,
710
- desc: indicator.desc,
711
- onClick: this.handleSelect.bind(null, "indicator_values:" + indicator.id)
712
- }))
713
- })
714
- ]
715
- )
716
- }
717
- }
718
-
719
- interface AddIndicatorConfirmPopupComponentProps {
720
- schema: Schema
721
- /** Called with table selected */
722
- onChange: any
723
- onExtraTableAdd: any
724
- }
725
-
726
- interface AddIndicatorConfirmPopupComponentState {
727
- indicatorTable: any
728
- visible: any
729
- }
730
-
731
- class AddIndicatorConfirmPopupComponent extends React.Component<
732
- AddIndicatorConfirmPopupComponentProps,
733
- AddIndicatorConfirmPopupComponentState
734
- > {
735
- constructor(props: any) {
736
- super(props)
737
- this.state = {
738
- visible: false,
739
- indicatorTable: null
740
- }
741
- }
742
-
743
- show(indicatorTable: any) {
744
- return this.setState({ visible: true, indicatorTable })
745
- }
746
-
747
- renderContents() {
748
- // Show loading if table not loaded
749
- if (!this.props.schema.getTable(this.state.indicatorTable)) {
750
- return R(
751
- "div",
752
- { className: "alert alert-info" },
753
- R("i", { className: "fa fa-spinner fa-spin" }),
754
- "\u00A0" + T`Loading...`
755
- )
756
- }
757
-
758
- // Find entity links
759
- const entityColumns = _.filter(this.props.schema.getColumns(this.state.indicatorTable), (col) =>
760
- col.join?.toTable?.match(/^entities\./)
761
- )
762
-
763
- return R(
764
- "div",
765
- null,
766
- R(
767
- "p",
768
- null,
769
- T`In general, it is better to get indicator values from the related site. Please select the site
770
- below, then find the indicator values in the 'Related Indicators' section. Or click on 'Use Raw Indicator' if you
771
- are certain that you want to use the raw indicator table`
772
- ),
773
-
774
- R(uiComponents.OptionListComponent, {
775
- items: _.map(entityColumns, (entityColumn) => ({
776
- name: ExprUtils.localizeString(entityColumn.name, T.locale),
777
- desc: ExprUtils.localizeString(entityColumn.desc, T.locale),
778
- onClick: () => {
779
- // Select table
780
- this.props.onChange(entityColumn.join!.toTable)
781
- return this.setState({ visible: false })
782
- }
783
- }))
784
- }),
785
-
786
- R("br"),
787
-
788
- R(
789
- "div",
790
- null,
791
- R(
792
- "a",
793
- { className: "link-plain", onClick: this.props.onChange.bind(null, this.state.indicatorTable) },
794
- T`Use Raw Indicator`
795
- )
796
- )
797
- )
798
- }
799
-
800
- render() {
801
- if (!this.state.visible) {
802
- return null
803
- }
804
-
805
- return R(
806
- ModalPopupComponent,
807
- {
808
- showCloseX: true,
809
- onClose: () => this.setState({ visible: false }),
810
- header: T`Add Indicator`
811
- },
812
- this.renderContents()
813
- )
814
- }
815
- }
816
-
817
- interface IssuesListComponentProps {
818
- /** Url to hit api */
819
- apiUrl: string
820
- /** Optional client */
821
- client?: string
822
- schema: Schema
823
- /** User id */
824
- user?: string
825
- /** Called with table selected */
826
- onChange: any
827
- extraTables: any
828
- onExtraTableAdd: any
829
- onExtraTableRemove: any
830
- }
831
-
832
- interface IssuesListComponentState {
833
- error?: any
834
- search: any
835
- issueTypes: any[] | null
836
- }
837
-
838
- // Searchable list of issue types
839
- class IssuesListComponent extends React.Component<IssuesListComponentProps, IssuesListComponentState> {
840
- constructor(props: any) {
841
- super(props)
842
- this.state = {
843
- issueTypes: null,
844
- search: ""
845
- }
846
- }
847
-
848
- componentDidMount() {
849
- // Get names and basic of issueTypes
850
- const query: any = {}
851
- query.fields = JSON.stringify({ name: 1, desc: 1, roles: 1, created: 1, modified: 1 })
852
- query.client = this.props.client
853
-
854
- // Get list of all issueType names
855
- return $.getJSON(this.props.apiUrl + "issue_types?" + querystring.stringify(query), (issueTypes: any[]) => {
856
- // Sort by modified.on desc but first by user
857
- issueTypes = _.sortByOrder(
858
- issueTypes,
859
- [
860
- (issueType) => ((this.props.extraTables || []).includes("issues:" + issueType._id) ? 0 : 1),
861
- (issueType) => (issueType.created.by === this.props.user ? 0 : 1),
862
- (issueType) => ExprUtils.localizeString(issueType.name, T.locale)
863
- ],
864
- ["asc", "asc", "asc"]
865
- )
866
-
867
- return this.setState({
868
- issueTypes: _.map(issueTypes, (issueType) => ({
869
- id: issueType._id,
870
- name: ExprUtils.localizeString(issueType.name, T.locale),
871
- desc: ExprUtils.localizeString(issueType.desc, T.locale)
872
- }))
873
- })
874
- }).fail((xhr: any) => {
875
- return this.setState({ error: xhr.responseText })
876
- })
877
- }
878
-
879
- handleTableRemove = (table: any) => {
880
- if (
881
- confirm(
882
- T`Remove ${ExprUtils.localizeString(
883
- table.name,
884
- T.locale
885
- )}? Any widgets that depend on it will no longer work properly.`
886
- )
887
- ) {
888
- return this.props.onExtraTableRemove(table.id)
889
- }
890
- }
891
-
892
- searchRef = (comp: any) => {
893
- // Focus
894
- if (comp) {
895
- return comp.focus()
896
- }
897
- }
898
-
899
- render() {
900
- let issueTypes
901
- if (this.state.error) {
902
- return R("div", { className: "alert alert-danger" }, this.state.error)
903
- }
904
-
905
- // Filter issueTypes
906
- if (this.state.search) {
907
- const escapeRegExp = (s: any) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")
908
-
909
- const searchStringRegExp = new RegExp(escapeRegExp(this.state.search), "i")
910
-
911
- issueTypes = _.filter(this.state.issueTypes || [], (issueType) => issueType.name.match(searchStringRegExp))
912
- } else {
913
- ;({ issueTypes } = this.state)
914
- }
915
-
916
- // Remove if already included
917
- issueTypes = _.filter(issueTypes || [], (f) => !(this.props.extraTables || []).includes(`issues:${f.id}`))
918
-
919
- let tables = _.filter(
920
- this.props.schema.getTables(),
921
- (table) => (table.id.match(/^issues:/) || table.id.match(/^issue_events:/)) && !table.deprecated
922
- )
923
- tables = _.sortBy(tables, (t) => t.name.en)
924
-
925
- return R(
926
- "div",
927
- null,
928
- R("label", null, T`Included Issues:`),
929
- tables.length > 0
930
- ? R(uiComponents.OptionListComponent, {
931
- items: _.map(tables, (table) => {
932
- return {
933
- name: ExprUtils.localizeString(table.name, T.locale),
934
- desc: ExprUtils.localizeString(table.desc, T.locale),
935
- onClick: this.props.onChange.bind(null, table.id),
936
- onRemove: this.handleTableRemove.bind(null, table)
937
- }
938
- })
939
- })
940
- : R("div", null, T`None`),
941
-
942
- R("br"),
943
-
944
- R("label", null, T`All Issues:`),
945
- !this.state.issueTypes || this.state.issueTypes.length === 0
946
- ? R(
947
- "div",
948
- { className: "alert alert-info" },
949
- R("i", { className: "fa fa-spinner fa-spin" }),
950
- "\u00A0",
951
- T`Loading...`
952
- )
953
- : [
954
- R("input", {
955
- type: "text",
956
- className: "form-control form-control-sm",
957
- placeholder: T`Search...`,
958
- key: "search",
959
- ref: this.searchRef,
960
- style: { maxWidth: "20em", marginBottom: 10 },
961
- value: this.state.search,
962
- onChange: (ev: any) => this.setState({ search: ev.target.value })
963
- }),
964
-
965
- R(uiComponents.OptionListComponent, {
966
- items: _.map(issueTypes, (issueType) => ({
967
- name: issueType.name,
968
- desc: issueType.desc,
969
- onClick: this.props.onChange.bind(null, "issues:" + issueType.id)
970
- }))
971
- })
972
- ]
973
- )
974
- }
975
- }