@mwater/visualization 5.5.0 → 5.6.1

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 (275) hide show
  1. package/lib/ColorComponent.js +2 -2
  2. package/lib/MWaterContextComponent.d.ts +1 -1
  3. package/lib/MWaterGlobalFiltersComponent.d.ts +2 -2
  4. package/lib/MWaterGlobalFiltersComponent.js +11 -20
  5. package/lib/MWaterLoaderComponent.d.ts +4 -13
  6. package/lib/MWaterLoaderComponent.js +2 -11
  7. package/lib/TranslationsTabComponent.d.ts +34 -0
  8. package/lib/TranslationsTabComponent.js +256 -0
  9. package/lib/UndoStack.d.ts +2 -1
  10. package/lib/UndoStack.js +12 -6
  11. package/lib/dashboards/DashboardComponent.js +6 -5
  12. package/lib/dashboards/DashboardDesign.d.ts +1 -1
  13. package/lib/dashboards/ServerDashboardDataSource.d.ts +0 -1
  14. package/lib/dashboards/ServerDashboardDataSource.js +0 -25
  15. package/lib/dashboards/SettingsModalComponent.js +9 -233
  16. package/lib/datagrids/DatagridComponent.js +27 -2
  17. package/lib/datagrids/DatagridDesignerComponent.d.ts +2 -3
  18. package/lib/datagrids/DatagridDesignerComponent.js +108 -120
  19. package/lib/datagrids/DatagridViewComponent.js +33 -6
  20. package/lib/datagrids/OrderBysDesignerComponent.d.ts +7 -7
  21. package/lib/datagrids/OrderBysDesignerComponent.js +19 -28
  22. package/lib/index.css +45 -2
  23. package/lib/index.d.ts +5 -5
  24. package/lib/index.js +2 -3
  25. package/lib/layouts/blocks/BlocksDisplayComponent.d.ts +8 -1
  26. package/lib/layouts/blocks/BlocksDisplayComponent.js +46 -4
  27. package/lib/maps/BufferLayer.d.ts +0 -13
  28. package/lib/maps/BufferLayer.js +24 -237
  29. package/lib/maps/BufferLayerDesign.d.ts +1 -1
  30. package/lib/maps/BufferLayerDesignerComponent.d.ts +1 -1
  31. package/lib/maps/BufferLayerDesignerComponent.js +2 -7
  32. package/lib/maps/ChoroplethLayer.d.ts +1 -16
  33. package/lib/maps/ChoroplethLayer.js +25 -358
  34. package/lib/maps/ChoroplethLayerDesign.d.ts +5 -2
  35. package/lib/maps/ChoroplethLayerDesigner.d.ts +10 -32
  36. package/lib/maps/ChoroplethLayerDesigner.js +58 -89
  37. package/lib/maps/ClusterLayer.d.ts +0 -9
  38. package/lib/maps/ClusterLayer.js +0 -250
  39. package/lib/maps/DirectMapDataSource.js +1 -48
  40. package/lib/maps/EditHoverOver.d.ts +4 -3
  41. package/lib/maps/EditHoverOver.js +3 -3
  42. package/lib/maps/GridLayer.d.ts +0 -15
  43. package/lib/maps/GridLayer.js +0 -212
  44. package/lib/maps/HoverContent.js +1 -1
  45. package/lib/maps/Layer.d.ts +1 -26
  46. package/lib/maps/Layer.js +0 -13
  47. package/lib/maps/LeafletMapComponent.js +10 -19
  48. package/lib/maps/MapComponent.d.ts +19 -35
  49. package/lib/maps/MapComponent.js +135 -77
  50. package/lib/maps/MapControlComponent.d.ts +4 -5
  51. package/lib/maps/MapControlComponent.js +5 -12
  52. package/lib/maps/MapDesign.d.ts +8 -0
  53. package/lib/maps/MapDesignerComponent.d.ts +2 -0
  54. package/lib/maps/MapDesignerComponent.js +7 -2
  55. package/lib/maps/MapLayerDataSource.d.ts +0 -4
  56. package/lib/maps/MapLayerViewDesignerComponent.d.ts +3 -1
  57. package/lib/maps/MapLayerViewDesignerComponent.js +5 -1
  58. package/lib/maps/MapLayersDesignerComponent.d.ts +2 -0
  59. package/lib/maps/MapLayersDesignerComponent.js +2 -1
  60. package/lib/maps/MapTranslationsTab.d.ts +15 -0
  61. package/lib/maps/MapTranslationsTab.js +47 -0
  62. package/lib/maps/MapUtils.d.ts +11 -0
  63. package/lib/maps/MapUtils.js +57 -1
  64. package/lib/maps/MapViewComponent.d.ts +1 -1
  65. package/lib/maps/MapViewComponent.js +1 -8
  66. package/lib/maps/MarkersLayer.d.ts +1 -14
  67. package/lib/maps/MarkersLayer.js +89 -254
  68. package/lib/maps/MarkersLayerDesign.d.ts +5 -1
  69. package/lib/maps/MarkersLayerDesignerComponent.d.ts +32 -57
  70. package/lib/maps/MarkersLayerDesignerComponent.js +158 -134
  71. package/lib/maps/ServerMapDataSource.d.ts +0 -1
  72. package/lib/maps/ServerMapDataSource.js +0 -25
  73. package/lib/maps/SwitchableTileUrlLayer.d.ts +0 -2
  74. package/lib/maps/SwitchableTileUrlLayer.js +0 -9
  75. package/lib/maps/TileUrlLayer.d.ts +0 -1
  76. package/lib/maps/TileUrlLayer.js +0 -5
  77. package/lib/maps/VectorMapViewComponent.js +13 -10
  78. package/lib/maps/symbols/font-awesome/asterisk.png +0 -0
  79. package/lib/maps/symbols/font-awesome/ban.png +0 -0
  80. package/lib/maps/symbols/font-awesome/beer.png +0 -0
  81. package/lib/maps/symbols/font-awesome/bell.png +0 -0
  82. package/lib/maps/symbols/font-awesome/bolt.png +0 -0
  83. package/lib/maps/symbols/font-awesome/building.png +0 -0
  84. package/lib/maps/symbols/font-awesome/bullseye.png +0 -0
  85. package/lib/maps/symbols/font-awesome/bus.png +0 -0
  86. package/lib/maps/symbols/font-awesome/caret-up.png +0 -0
  87. package/lib/maps/symbols/font-awesome/certificate.png +0 -0
  88. package/lib/maps/symbols/font-awesome/check-circle.png +0 -0
  89. package/lib/maps/symbols/font-awesome/check.png +0 -0
  90. package/lib/maps/symbols/font-awesome/chevron-circle-down.png +0 -0
  91. package/lib/maps/symbols/font-awesome/chevron-circle-up.png +0 -0
  92. package/lib/maps/symbols/font-awesome/cloud-rain.png +0 -0
  93. package/lib/maps/symbols/font-awesome/cloud.png +0 -0
  94. package/lib/maps/symbols/font-awesome/comment.png +0 -0
  95. package/lib/maps/symbols/font-awesome/crosshairs.png +0 -0
  96. package/lib/maps/symbols/font-awesome/dot-circle-o.png +0 -0
  97. package/lib/maps/symbols/font-awesome/exclamation-circle.png +0 -0
  98. package/lib/maps/symbols/font-awesome/exclamation-triangle.png +0 -0
  99. package/lib/maps/symbols/font-awesome/female.png +0 -0
  100. package/lib/maps/symbols/font-awesome/file.png +0 -0
  101. package/lib/maps/symbols/font-awesome/flag.png +0 -0
  102. package/lib/maps/symbols/font-awesome/flask.png +0 -0
  103. package/lib/maps/symbols/font-awesome/h-square.png +0 -0
  104. package/lib/maps/symbols/font-awesome/home.png +0 -0
  105. package/lib/maps/symbols/font-awesome/info-circle.png +0 -0
  106. package/lib/maps/symbols/font-awesome/male.png +0 -0
  107. package/lib/maps/symbols/font-awesome/medkit.png +0 -0
  108. package/lib/maps/symbols/font-awesome/mobile.png +0 -0
  109. package/lib/maps/symbols/font-awesome/plus-circle.png +0 -0
  110. package/lib/maps/symbols/font-awesome/plus-square.png +0 -0
  111. package/lib/maps/symbols/font-awesome/plus.png +0 -0
  112. package/lib/maps/symbols/font-awesome/square.png +0 -0
  113. package/lib/maps/symbols/font-awesome/star.png +0 -0
  114. package/lib/maps/symbols/font-awesome/thumbs-down.png +0 -0
  115. package/lib/maps/symbols/font-awesome/thumbs-up.png +0 -0
  116. package/lib/maps/symbols/font-awesome/ticket.png +0 -0
  117. package/lib/maps/symbols/font-awesome/times-circle.png +0 -0
  118. package/lib/maps/symbols/font-awesome/times.png +0 -0
  119. package/lib/maps/symbols/font-awesome/tint.png +0 -0
  120. package/lib/maps/symbols/font-awesome/tree.png +0 -0
  121. package/lib/maps/symbols/font-awesome/university.png +0 -0
  122. package/lib/maps/symbols/font-awesome/usd.png +0 -0
  123. package/lib/maps/symbols/font-awesome/user.png +0 -0
  124. package/lib/maps/symbols/font-awesome/users.png +0 -0
  125. package/lib/maps/symbols/font-awesome/wheelchair.png +0 -0
  126. package/lib/maps/symbols/sdf-ize.sh +93 -0
  127. package/lib/maps/vectorMaps.d.ts +6 -6
  128. package/lib/maps/vectorMaps.js +33 -45
  129. package/lib/mwater_table_selection/IndicatorsListComponent.d.ts +4 -2
  130. package/lib/mwater_table_selection/IndicatorsListComponent.js +103 -34
  131. package/lib/mwater_table_selection/MWaterCalculatedDataSourcesListComponent.d.ts +18 -0
  132. package/lib/mwater_table_selection/MWaterCalculatedDataSourcesListComponent.js +80 -0
  133. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.d.ts +26 -0
  134. package/lib/mwater_table_selection/MWaterCompleteTableSelectComponent.js +237 -51
  135. package/lib/mwater_table_selection/MWaterTableSelectComponent.d.ts +2 -2
  136. package/lib/mwater_table_selection/MWaterTableSelectComponent.js +9 -4
  137. package/lib/mwater_table_selection/MWaterWorkflowsSelectComponent.d.ts +19 -0
  138. package/lib/mwater_table_selection/MWaterWorkflowsSelectComponent.js +111 -0
  139. package/lib/quickfilter/QuickfiltersComponent.d.ts +3 -102
  140. package/lib/quickfilter/QuickfiltersComponent.js +53 -110
  141. package/lib/quickfilter/TextLiteralComponent.d.ts +23 -47
  142. package/lib/quickfilter/TextLiteralComponent.js +85 -82
  143. package/lib/widgets/MapWidget.js +6 -3
  144. package/lib/widgets/text/ExprItemEditorComponent.d.ts +3 -8
  145. package/lib/widgets/text/ExprItemEditorComponent.js +36 -33
  146. package/lib/widgets/text/ExprUpdateModalComponent.d.ts +1 -0
  147. package/package.json +3 -4
  148. package/src/ColorComponent.tsx +2 -2
  149. package/src/MWaterContextComponent.tsx +1 -1
  150. package/src/{MWaterGlobalFiltersComponent.ts → MWaterGlobalFiltersComponent.tsx} +32 -33
  151. package/src/{MWaterLoaderComponent.ts → MWaterLoaderComponent.tsx} +17 -18
  152. package/src/TranslationsTabComponent.tsx +429 -0
  153. package/src/UndoStack.ts +14 -6
  154. package/src/dashboards/DashboardComponent.tsx +6 -5
  155. package/src/dashboards/DashboardDesign.ts +1 -1
  156. package/src/dashboards/ServerDashboardDataSource.ts +0 -31
  157. package/src/dashboards/SettingsModalComponent.tsx +27 -383
  158. package/src/datagrids/DatagridComponent.tsx +36 -2
  159. package/src/datagrids/DatagridDesignerComponent.tsx +241 -229
  160. package/src/datagrids/DatagridViewComponent.tsx +44 -7
  161. package/src/datagrids/OrderBysDesignerComponent.tsx +61 -70
  162. package/src/index.css +45 -2
  163. package/src/index.ts +5 -11
  164. package/src/layouts/blocks/BlocksDisplayComponent.tsx +60 -5
  165. package/src/maps/BufferLayer.ts +30 -263
  166. package/src/maps/BufferLayerDesign.ts +1 -1
  167. package/src/maps/BufferLayerDesignerComponent.tsx +2 -7
  168. package/src/maps/ChoroplethLayer.ts +30 -394
  169. package/src/maps/ChoroplethLayerDesign.ts +5 -2
  170. package/src/maps/ChoroplethLayerDesigner.tsx +169 -165
  171. package/src/maps/ClusterLayer.ts +0 -274
  172. package/src/maps/DirectMapDataSource.ts +2 -61
  173. package/src/maps/EditHoverOver.tsx +9 -5
  174. package/src/maps/GridLayer.ts +0 -224
  175. package/src/maps/HoverContent.tsx +1 -1
  176. package/src/maps/Layer.ts +1 -35
  177. package/src/maps/LeafletMapComponent.tsx +10 -19
  178. package/src/maps/MapComponent.tsx +448 -0
  179. package/src/maps/MapControlComponent.tsx +41 -0
  180. package/src/maps/MapDesign.ts +6 -0
  181. package/src/maps/MapDesignerComponent.tsx +18 -1
  182. package/src/maps/MapLayerDataSource.ts +0 -5
  183. package/src/maps/MapLayerViewDesignerComponent.ts +9 -2
  184. package/src/maps/MapLayersDesignerComponent.ts +4 -1
  185. package/src/maps/MapTranslationsTab.tsx +53 -0
  186. package/src/maps/MapUtils.ts +61 -1
  187. package/src/maps/MapViewComponent.tsx +2 -8
  188. package/src/maps/MarkersLayer.ts +101 -275
  189. package/src/maps/MarkersLayerDesign.ts +7 -1
  190. package/src/maps/MarkersLayerDesignerComponent.tsx +436 -0
  191. package/src/maps/ServerMapDataSource.ts +0 -31
  192. package/src/maps/SwitchableTileUrlLayer.tsx +0 -11
  193. package/src/maps/TileUrlLayer.tsx +0 -6
  194. package/src/maps/VectorMapViewComponent.tsx +15 -15
  195. package/src/maps/symbols/font-awesome/asterisk.png +0 -0
  196. package/src/maps/symbols/font-awesome/ban.png +0 -0
  197. package/src/maps/symbols/font-awesome/beer.png +0 -0
  198. package/src/maps/symbols/font-awesome/bell.png +0 -0
  199. package/src/maps/symbols/font-awesome/bolt.png +0 -0
  200. package/src/maps/symbols/font-awesome/building.png +0 -0
  201. package/src/maps/symbols/font-awesome/bullseye.png +0 -0
  202. package/src/maps/symbols/font-awesome/bus.png +0 -0
  203. package/src/maps/symbols/font-awesome/caret-up.png +0 -0
  204. package/src/maps/symbols/font-awesome/certificate.png +0 -0
  205. package/src/maps/symbols/font-awesome/check-circle.png +0 -0
  206. package/src/maps/symbols/font-awesome/check.png +0 -0
  207. package/src/maps/symbols/font-awesome/chevron-circle-down.png +0 -0
  208. package/src/maps/symbols/font-awesome/chevron-circle-up.png +0 -0
  209. package/src/maps/symbols/font-awesome/cloud-rain.png +0 -0
  210. package/src/maps/symbols/font-awesome/cloud.png +0 -0
  211. package/src/maps/symbols/font-awesome/comment.png +0 -0
  212. package/src/maps/symbols/font-awesome/crosshairs.png +0 -0
  213. package/src/maps/symbols/font-awesome/dot-circle-o.png +0 -0
  214. package/src/maps/symbols/font-awesome/exclamation-circle.png +0 -0
  215. package/src/maps/symbols/font-awesome/exclamation-triangle.png +0 -0
  216. package/src/maps/symbols/font-awesome/female.png +0 -0
  217. package/src/maps/symbols/font-awesome/file.png +0 -0
  218. package/src/maps/symbols/font-awesome/flag.png +0 -0
  219. package/src/maps/symbols/font-awesome/flask.png +0 -0
  220. package/src/maps/symbols/font-awesome/h-square.png +0 -0
  221. package/src/maps/symbols/font-awesome/home.png +0 -0
  222. package/src/maps/symbols/font-awesome/info-circle.png +0 -0
  223. package/src/maps/symbols/font-awesome/male.png +0 -0
  224. package/src/maps/symbols/font-awesome/medkit.png +0 -0
  225. package/src/maps/symbols/font-awesome/mobile.png +0 -0
  226. package/src/maps/symbols/font-awesome/plus-circle.png +0 -0
  227. package/src/maps/symbols/font-awesome/plus-square.png +0 -0
  228. package/src/maps/symbols/font-awesome/plus.png +0 -0
  229. package/src/maps/symbols/font-awesome/square.png +0 -0
  230. package/src/maps/symbols/font-awesome/star.png +0 -0
  231. package/src/maps/symbols/font-awesome/thumbs-down.png +0 -0
  232. package/src/maps/symbols/font-awesome/thumbs-up.png +0 -0
  233. package/src/maps/symbols/font-awesome/ticket.png +0 -0
  234. package/src/maps/symbols/font-awesome/times-circle.png +0 -0
  235. package/src/maps/symbols/font-awesome/times.png +0 -0
  236. package/src/maps/symbols/font-awesome/tint.png +0 -0
  237. package/src/maps/symbols/font-awesome/tree.png +0 -0
  238. package/src/maps/symbols/font-awesome/university.png +0 -0
  239. package/src/maps/symbols/font-awesome/usd.png +0 -0
  240. package/src/maps/symbols/font-awesome/user.png +0 -0
  241. package/src/maps/symbols/font-awesome/users.png +0 -0
  242. package/src/maps/symbols/font-awesome/wheelchair.png +0 -0
  243. package/src/maps/symbols/sdf-ize.sh +93 -0
  244. package/src/maps/vectorMaps.tsx +32 -53
  245. package/src/mwater_table_selection/IndicatorsListComponent.tsx +165 -37
  246. package/src/mwater_table_selection/MWaterCalculatedDataSourcesListComponent.tsx +111 -0
  247. package/src/mwater_table_selection/MWaterCompleteTableSelectComponent.tsx +373 -37
  248. package/src/mwater_table_selection/MWaterTableSelectComponent.tsx +12 -8
  249. package/src/mwater_table_selection/MWaterWorkflowsSelectComponent.tsx +159 -0
  250. package/src/quickfilter/{QuickfiltersComponent.ts → QuickfiltersComponent.tsx} +165 -158
  251. package/src/quickfilter/TextLiteralComponent.tsx +197 -0
  252. package/src/widgets/MapWidget.tsx +11 -1
  253. package/src/widgets/text/ExprItemEditorComponent.tsx +83 -77
  254. package/src/widgets/text/ExprUpdateModalComponent.tsx +1 -0
  255. package/test/UndoStackTests.ts +52 -1
  256. package/.storybook/config.js +0 -7
  257. package/.storybook/head.html +0 -3
  258. package/.storybook/webpack.config.js +0 -15
  259. package/src/maps/BingLayer.ts +0 -146
  260. package/src/maps/MapComponent.ts +0 -312
  261. package/src/maps/MapControlComponent.ts +0 -46
  262. package/src/maps/MarkersLayerDesignerComponent.ts +0 -374
  263. package/src/maps/RasterMapViewComponent.ts +0 -345
  264. package/src/quickfilter/TextLiteralComponent.ts +0 -165
  265. package/stories/UpdateableComponent.js +0 -29
  266. package/stories/consoles.js +0 -202
  267. package/stories/dashboards.js +0 -217
  268. package/stories/datagridDesign.js +0 -114
  269. package/stories/datagrids.js +0 -69
  270. package/stories/dates.js +0 -80
  271. package/stories/exprcomponent.js +0 -43
  272. package/stories/index.js +0 -18
  273. package/stories/leaflet.js +0 -59
  274. package/stories/maps.js +0 -24
  275. package/stories/pivotChart.js +0 -235
@@ -1,6 +1,5 @@
1
1
  import _ from "lodash"
2
2
  import React from "react"
3
- import { default as TabbedComponent, TabbedComponentTab } from "@mwater/react-library/lib/TabbedComponent"
4
3
  import * as uiComponents from "../UIComponents"
5
4
  import { ExprUtils, Schema } from "@mwater/expressions"
6
5
  import { MWaterCustomTablesetListComponent } from "./MWaterCustomTablesetListComponent"
@@ -10,6 +9,8 @@ import { FormsListComponent } from "./FormsListComponent"
10
9
  import { IndicatorsListComponent } from "./IndicatorsListComponent"
11
10
  import { IssuesListComponent } from "./IssuesListComponent"
12
11
  import { MWaterAccountingSystemListComponent } from "./MWaterAccountingSystemListComponent"
12
+ import { MWaterCalculatedDataSourcesListComponent } from "./MWaterCalculatedDataSourcesListComponent"
13
+ import { MWaterWorkflowsSelectComponent } from "./MWaterWorkflowsSelectComponent"
13
14
 
14
15
  const sitesOrder: { [table: string]: number } = {
15
16
  "entities.water_point": 1,
@@ -45,15 +46,30 @@ interface MWaterCompleteTableSelectComponentProps {
45
46
  onExtraTablesChange: any
46
47
  }
47
48
 
48
- // Allows selection of a table. Is the complete list mode of tables
49
- export default class MWaterCompleteTableSelectComponent extends React.Component<MWaterCompleteTableSelectComponentProps, {
50
- showLegacyAssets: boolean
51
- }> {
49
+ interface Category {
50
+ id: string
51
+ name: string
52
+ description: string
53
+ icon: string
54
+ render: () => JSX.Element
55
+ }
56
+
57
+ /** Allows selection of a table. Card-based layout with categories */
58
+ export default class MWaterCompleteTableSelectComponent extends React.Component<
59
+ MWaterCompleteTableSelectComponentProps,
60
+ {
61
+ selectedCategory: string | null
62
+ showLegacyAssets: boolean
63
+ searchText: string
64
+ }
65
+ > {
52
66
  constructor(props: MWaterCompleteTableSelectComponentProps) {
53
67
  super(props)
54
68
 
55
69
  this.state = {
56
- showLegacyAssets: false
70
+ selectedCategory: null,
71
+ showLegacyAssets: false,
72
+ searchText: ""
57
73
  }
58
74
  }
59
75
 
@@ -62,12 +78,36 @@ export default class MWaterCompleteTableSelectComponent extends React.Component<
62
78
  }
63
79
 
64
80
  handleExtraTableRemove = (tableId: any) => {
65
- // Set to null if current table
66
- if (this.props.table === tableId) {
67
- this.props.onChange(null)
81
+ const displayName = this.getExtraTableDisplayName(tableId)
82
+ if (!displayName) {
83
+ return
68
84
  }
69
85
 
70
- return this.props.onExtraTablesChange(_.without(this.props.extraTables, tableId))
86
+ if (
87
+ confirm(
88
+ T`Remove ${displayName}? Any widgets that depend on it will no longer work properly.`
89
+ )
90
+ ) {
91
+ // Set to null if current table matches (for wildcards, check if current table starts with prefix)
92
+ if (tableId.endsWith(".*")) {
93
+ const prefix = tableId.slice(0, -2)
94
+ if (this.props.table && this.props.table.startsWith(prefix + ".")) {
95
+ this.props.onChange(null)
96
+ }
97
+ } else if (this.props.table === tableId) {
98
+ this.props.onChange(null)
99
+ }
100
+
101
+ return this.props.onExtraTablesChange(_.without(this.props.extraTables, tableId))
102
+ }
103
+ }
104
+
105
+ handleCategorySelect = (categoryId: string) => {
106
+ this.setState({ selectedCategory: categoryId })
107
+ }
108
+
109
+ handleBackToCategories = () => {
110
+ this.setState({ selectedCategory: null, showLegacyAssets: false })
71
111
  }
72
112
 
73
113
  renderSites() {
@@ -147,6 +187,19 @@ export default class MWaterCompleteTableSelectComponent extends React.Component<
147
187
  />
148
188
  }
149
189
 
190
+ renderWorkflows() {
191
+ return <MWaterWorkflowsSelectComponent
192
+ schema={this.props.schema}
193
+ client={this.props.client}
194
+ apiUrl={this.props.apiUrl}
195
+ user={this.props.user}
196
+ onChange={this.props.onChange}
197
+ extraTables={this.props.extraTables}
198
+ onExtraTableAdd={this.handleExtraTableAdd}
199
+ onExtraTableRemove={this.handleExtraTableRemove}
200
+ />
201
+ }
202
+
150
203
  renderSweetSense() {
151
204
  let sweetSenseTables = this.getSweetSenseTables()
152
205
 
@@ -238,7 +291,6 @@ export default class MWaterCompleteTableSelectComponent extends React.Component<
238
291
  </div>
239
292
  }
240
293
 
241
-
242
294
  renderLegacyAssets() {
243
295
  return <MWaterAssetSystemsListComponent
244
296
  schema={this.props.schema}
@@ -253,6 +305,20 @@ export default class MWaterCompleteTableSelectComponent extends React.Component<
253
305
  />
254
306
  }
255
307
 
308
+ renderCalculated() {
309
+ return <MWaterCalculatedDataSourcesListComponent
310
+ schema={this.props.schema}
311
+ client={this.props.client}
312
+ apiUrl={this.props.apiUrl}
313
+ user={this.props.user}
314
+ onChange={this.props.onChange}
315
+ extraTables={this.props.extraTables}
316
+ onExtraTableAdd={this.handleExtraTableAdd}
317
+ onExtraTableRemove={this.handleExtraTableRemove}
318
+ locale={T.locale}
319
+ />
320
+ }
321
+
256
322
  renderOther() {
257
323
  let otherTables = _.filter(this.props.schema.getTables(), (table) => {
258
324
  // Remove deprecated
@@ -300,6 +366,16 @@ export default class MWaterCompleteTableSelectComponent extends React.Component<
300
366
  return false
301
367
  }
302
368
 
369
+ // Remove calculated data sources
370
+ if (table.id.match(/^calculated:/)) {
371
+ return false
372
+ }
373
+
374
+ // Remove workflows
375
+ if (table.id.match(/^(workflows|workflow_events):/)) {
376
+ return false
377
+ }
378
+
303
379
  return true
304
380
  })
305
381
 
@@ -329,49 +405,309 @@ export default class MWaterCompleteTableSelectComponent extends React.Component<
329
405
  })
330
406
  }
331
407
 
332
- render() {
333
- const sweetSenseTables = this.getSweetSenseTables()
334
-
335
- const tabs: TabbedComponentTab[] = [
336
- { id: "sites", label: <><i className="fa fa-map-marker" /> {T`Sites`}</>, elem: this.renderSites() },
337
- { id: "forms", label: <><i className="fa fa-th-list" /> {T`Surveys`}</>, elem: this.renderForms() },
408
+ /** Get all available categories */
409
+ getCategories(): Category[] {
410
+ const categories: Category[] = [
411
+ {
412
+ id: "sites",
413
+ name: T("Sites").toString(),
414
+ description: T("Water points, sanitation facilities, households, and other sites").toString(),
415
+ icon: "fa fa-map-marker",
416
+ render: () => this.renderSites()
417
+ },
418
+ {
419
+ id: "forms",
420
+ name: T("Surveys").toString(),
421
+ description: T("Data collected from survey forms").toString(),
422
+ icon: "fa fa-th-list",
423
+ render: () => this.renderForms()
424
+ },
425
+ {
426
+ id: "assets",
427
+ name: T("Assets").toString(),
428
+ description: T("Asset tracking and management").toString(),
429
+ icon: "fas fa-map-pin",
430
+ render: () => this.renderAssets()
431
+ },
338
432
  {
339
433
  id: "indicators",
340
- label: <><i className="fa fa-check-circle" /> {T`Indicators`}</>,
341
- elem: this.renderIndicators()
434
+ name: T("Indicators").toString(),
435
+ description: T("Calculated indicators and metrics").toString(),
436
+ icon: "fa fa-check-circle",
437
+ render: () => this.renderIndicators()
438
+ },
439
+ {
440
+ id: "tablesets",
441
+ name: T("Tables").toString(),
442
+ description: T("Custom tables and datasets").toString(),
443
+ icon: "fa fa-table",
444
+ render: () => this.renderTablesets()
445
+ },
446
+ {
447
+ id: "calculated",
448
+ name: T("Calculated").toString(),
449
+ description: T("Calculated and derived data sources").toString(),
450
+ icon: "fa fa-cogs",
451
+ render: () => this.renderCalculated()
452
+ },
453
+ {
454
+ id: "accounting",
455
+ name: T("Accounting").toString(),
456
+ description: T("Financial and accounting data").toString(),
457
+ icon: "fa fa-calculator",
458
+ render: () => this.renderAccountingSystems()
459
+ },
460
+ {
461
+ id: "workflows",
462
+ name: T("Workflows").toString(),
463
+ description: T("Multi-step processes and workflows").toString(),
464
+ icon: "fa fa-project-diagram",
465
+ render: () => this.renderWorkflows()
342
466
  },
343
467
  {
344
468
  id: "issues",
345
- label: <><i className="fa fa-exclamation-circle" /> {T`Issues`}</>,
346
- elem: this.renderIssues()
469
+ name: T("Issues").toString(),
470
+ description: T("Reported problems and issues").toString(),
471
+ icon: "fa fa-exclamation-circle",
472
+ render: () => this.renderIssues()
347
473
  },
348
- { id: "tablesets", label: <><i className="fa fa-table" /> {T`Tables`}</>, elem: this.renderTablesets() },
349
- { id: "metrics", label: <><i className="fa fa-line-chart" /> {T`Metrics`}</>, elem: this.renderMetrics() },
350
- {
351
- id: "accounting",
352
- label: <><i className="fa fa-calculator" /> {T`Accounting`}</>,
353
- elem: this.renderAccountingSystems()
474
+ {
475
+ id: "metrics",
476
+ name: T("Metrics").toString(),
477
+ description: T("Performance metrics and KPIs").toString(),
478
+ icon: "fa fa-line-chart",
479
+ render: () => this.renderMetrics()
354
480
  },
355
- { id: "assets", label: <><i className="fas fa-map-pin" /> {T`Assets`}</>, elem: this.renderAssets() }
481
+ {
482
+ id: "other",
483
+ name: T("Advanced").toString(),
484
+ description: T("Other advanced data sources").toString(),
485
+ icon: "fa fa-ellipsis-h",
486
+ render: () => this.renderOther()
487
+ }
356
488
  ]
357
489
 
358
- if (sweetSenseTables.length > 0) {
359
- tabs.push({ id: "sensors", label: T`Sensors`, elem: this.renderSweetSense() })
490
+ // // Add sensors if applicable
491
+ // const sweetSenseTables = this.getSweetSenseTables()
492
+ // if (sweetSenseTables.length > 0) {
493
+ // categories.splice(9, 0, {
494
+ // id: "sensors",
495
+ // name: T("Sensors").toString(),
496
+ // description: T("Sensor data and monitoring").toString(),
497
+ // icon: "fa fa-wifi",
498
+ // render: () => this.renderSweetSense()
499
+ // })
500
+ // }
501
+
502
+ return categories
503
+ }
504
+
505
+ /** Render a single category card */
506
+ renderCategoryCard(category: Category) {
507
+ return (
508
+ <div
509
+ key={category.id}
510
+ className="card mb-2"
511
+ style={{
512
+ cursor: "pointer",
513
+ transition: "background-color 0.15s, border-color 0.15s"
514
+ }}
515
+ onMouseEnter={(e) => {
516
+ e.currentTarget.style.backgroundColor = "#f8f9fa"
517
+ e.currentTarget.style.borderColor = "#adb5bd"
518
+ }}
519
+ onMouseLeave={(e) => {
520
+ e.currentTarget.style.backgroundColor = ""
521
+ e.currentTarget.style.borderColor = ""
522
+ }}
523
+ onClick={() => this.handleCategorySelect(category.id)}
524
+ >
525
+ <div className="card-body d-flex align-items-center" style={{ padding: "0.75rem" }}>
526
+ <div style={{ fontSize: "1.75rem", width: "2.5rem", textAlign: "center", color: "#6c757d" }}>
527
+ <i className={category.icon} />
528
+ </div>
529
+ <div style={{ flex: 1, marginLeft: "0.75rem" }}>
530
+ <div style={{ fontWeight: 600, fontSize: "1rem", marginBottom: "0.15rem" }}>
531
+ {category.name}
532
+ </div>
533
+ <div className="text-muted" style={{ fontSize: "0.85rem" }}>
534
+ {category.description}
535
+ </div>
536
+ </div>
537
+ <div style={{ color: "#adb5bd", fontSize: "1.1rem" }}>
538
+ <i className="fa fa-chevron-right" />
539
+ </div>
540
+ </div>
541
+ </div>
542
+ )
543
+ }
544
+
545
+ /** Get display name for an extra table (handles wildcards) */
546
+ getExtraTableDisplayName(tableId: string): string | null {
547
+ // Check if it's a wildcard pattern
548
+ if (tableId.endsWith(".*")) {
549
+ // Find first matching table
550
+ const prefix = tableId.slice(0, -2) // Remove .*
551
+ const matchingTable = this.props.schema.getTables().find(t => t.id.startsWith(prefix + "."))
552
+
553
+ if (matchingTable) {
554
+ const fullName = ExprUtils.localizeString(matchingTable.name, T.locale)
555
+ // Split by " > " and take first part
556
+ const parts = fullName.split(" > ")
557
+ return parts[0]
558
+ }
559
+
560
+ // Fallback to showing the pattern
561
+ return tableId
360
562
  }
563
+
564
+ // Regular table
565
+ const table = this.props.schema.getTable(tableId)
566
+ if (!table) {
567
+ return null
568
+ }
569
+ return ExprUtils.localizeString(table.name, T.locale)
570
+ }
571
+
572
+ /** Render the currently included extra tables section */
573
+ renderExtraTables() {
574
+ if (!this.props.extraTables || this.props.extraTables.length === 0) {
575
+ return null
576
+ }
577
+
578
+ return (
579
+ <div className="mb-3 pb-2" style={{ borderBottom: "1px solid #dee2e6" }}>
580
+ <span className="text-muted me-2" style={{ fontSize: "0.9rem" }}>
581
+ {T`Currently Included`}:
582
+ </span>
583
+ {this.props.extraTables.map((tableId: string) => {
584
+ const displayName = this.getExtraTableDisplayName(tableId)
585
+ if (!displayName) {
586
+ return null
587
+ }
588
+ return (
589
+ <span
590
+ key={tableId}
591
+ className="d-inline-flex align-items-center me-2 px-2 py-1 bg-light border rounded"
592
+ style={{ fontSize: "0.85rem" }}
593
+ >
594
+ <span>{displayName}</span>
595
+ <button
596
+ className="btn btn-link btn-sm p-0 ms-1"
597
+ style={{ fontSize: "0.9rem", color: "#6c757d" }}
598
+ onClick={(e) => {
599
+ e.stopPropagation()
600
+ this.handleExtraTableRemove(tableId)
601
+ }}
602
+ >
603
+ <i className="fa fa-times" />
604
+ </button>
605
+ </span>
606
+ )
607
+ })}
608
+ </div>
609
+ )
610
+ }
611
+
612
+ /** Render the landing page with all categories */
613
+ renderLandingPage() {
614
+ const categories = this.getCategories()
615
+
616
+ // Filter by search text
617
+ const filteredCategories = this.state.searchText
618
+ ? categories.filter((cat) => {
619
+ const searchLower = this.state.searchText.toLowerCase()
620
+ return (
621
+ cat.name.toLowerCase().includes(searchLower) ||
622
+ cat.description.toLowerCase().includes(searchLower)
623
+ )
624
+ })
625
+ : categories
626
+
627
+ return (
628
+ <div>
629
+ <div className="text-muted mb-3">
630
+ {T`Select a data source category to browse available tables and datasets.`}
631
+ </div>
632
+
633
+ {/* Search box */}
634
+ <div className="mb-3 position-relative">
635
+ <i className="fa fa-search position-absolute" style={{ left: "0.75rem", top: "50%", transform: "translateY(-50%)", color: "#6c757d" }} />
636
+ <input
637
+ type="text"
638
+ className="form-control"
639
+ style={{ paddingLeft: "2.5rem" }}
640
+ placeholder={T("Search categories...").toString()}
641
+ value={this.state.searchText}
642
+ onChange={(e) => this.setState({ searchText: e.target.value })}
643
+ ref={(c: HTMLInputElement | null) => {
644
+ if (c) {
645
+ c.focus()
646
+ }
647
+ }}
648
+ />
649
+ </div>
650
+
651
+ {/* Extra tables section */}
652
+ {this.renderExtraTables()}
653
+
654
+ {/* Category cards grid */}
655
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "0.5rem" }}>
656
+ {filteredCategories.map((category) => this.renderCategoryCard(category))}
657
+ </div>
658
+
659
+ {filteredCategories.length === 0 && this.state.searchText && (
660
+ <div className="text-center text-muted mt-4">
661
+ {T`No categories match your search.`}
662
+ </div>
663
+ )}
664
+ </div>
665
+ )
666
+ }
361
667
 
362
- tabs.push({ id: "other", label: T`Advanced`, elem: this.renderOther() })
668
+ /** Render a specific category view */
669
+ renderCategoryView() {
670
+ const categories = this.getCategories()
671
+ const category = categories.find((c) => c.id === this.state.selectedCategory)
672
+
673
+ if (!category) {
674
+ return null
675
+ }
363
676
 
364
677
  return (
365
678
  <div>
366
- <div className="text-muted">
367
- {T`Select data from sites, surveys or an advanced category below. Indicators can be found within their associated site types.`}
679
+ {/* Back button */}
680
+ <div className="mb-3">
681
+ <button
682
+ className="btn btn-link p-0"
683
+ style={{ textDecoration: "none" }}
684
+ onClick={this.handleBackToCategories}
685
+ >
686
+ <i className="fa fa-arrow-left me-2" />
687
+ {T`Back to All Data Sources`}
688
+ </button>
368
689
  </div>
369
690
 
370
- <TabbedComponent
371
- tabs={tabs}
372
- initialTabId="sites"
373
- />
691
+ {/* Category title */}
692
+ <h5 className="mb-3">
693
+ <i className={`${category.icon} me-2 text-muted`} />
694
+ {category.name}
695
+ </h5>
696
+
697
+ {/* Category content */}
698
+ {category.render()}
699
+ </div>
700
+ )
701
+ }
702
+
703
+ render() {
704
+ return (
705
+ <div style={{ height: "65vh", overflowY: "auto" }}>
706
+ {this.state.selectedCategory === null
707
+ ? this.renderLandingPage()
708
+ : this.renderCategoryView()}
374
709
  </div>
375
710
  )
376
711
  }
377
712
  }
713
+
@@ -18,8 +18,8 @@ export interface MWaterTableSelectComponentProps {
18
18
  table?: string
19
19
  /** Called with table selected */
20
20
  onChange: (table: string | null) => void
21
- extraTables: string[]
22
- onExtraTablesChange: (tables: string[]) => void
21
+ extraTables?: string[]
22
+ onExtraTablesChange?: (tables: string[]) => void
23
23
  /** Can also perform filtering for some types. Include these props to enable this */
24
24
  filter?: any
25
25
  onFilterChange?: (filter: any) => void
@@ -94,11 +94,15 @@ export default class MWaterTableSelectComponent extends React.Component<
94
94
  handleTableChange = (tableId: string | null) => {
95
95
  // If not part of extra tables, add it and wait for new schema
96
96
  if (tableId && !this.props.schema.getTable(tableId)) {
97
- return this.setState({ pendingExtraTable: tableId }, () => {
98
- return this.props.onExtraTablesChange(_.union(this.props.extraTables, [tableId]))
99
- })
97
+ if (this.props.onExtraTablesChange) {
98
+ this.setState({ pendingExtraTable: tableId }, () => {
99
+ this.props.onExtraTablesChange!(_.union(this.props.extraTables || [], [tableId]))
100
+ })
101
+ } else {
102
+ alert(T`Cannot add new tables.`)
103
+ }
100
104
  } else {
101
- return this.handleChange(tableId)
105
+ this.handleChange(tableId)
102
106
  }
103
107
  }
104
108
 
@@ -166,8 +170,8 @@ interface EditModeTableSelectComponentProps {
166
170
  table?: string
167
171
  /** Called with table selected */
168
172
  onChange: (table: string | null) => void
169
- extraTables: string[]
170
- onExtraTablesChange: (tables: string[]) => void
173
+ extraTables?: string[]
174
+ onExtraTablesChange?: (tables: string[]) => void
171
175
  }
172
176
 
173
177
  interface EditModeTableSelectComponentState {
@@ -0,0 +1,159 @@
1
+ import _ from "lodash"
2
+ import $ from "jquery"
3
+ import React, { useState, useEffect } from "react"
4
+ import querystring from "querystring"
5
+ import * as uiComponents from "../UIComponents"
6
+ import { ExprUtils, Schema } from "@mwater/expressions"
7
+ import { WorkflowType } from "@mwater/common/lib/tables/workflow_types"
8
+
9
+ interface MWaterWorkflowsSelectComponentProps {
10
+ /** Url to hit api */
11
+ apiUrl: string
12
+ /** Optional client */
13
+ client?: string
14
+ schema: Schema
15
+ /** User id */
16
+ user?: string
17
+ /** Called with table selected */
18
+ onChange: (tableId: string) => void
19
+ extraTables: string[]
20
+ onExtraTableAdd: (tableId: string) => void
21
+ onExtraTableRemove: (tableId: string) => void
22
+ }
23
+
24
+ interface WorkflowTypeItem {
25
+ id: string
26
+ name: string
27
+ desc?: string
28
+ }
29
+
30
+ /** Searchable list of workflow types */
31
+ export function MWaterWorkflowsSelectComponent(props: MWaterWorkflowsSelectComponentProps) {
32
+ const [workflowTypes, setWorkflowTypes] = useState<WorkflowTypeItem[] | null>(null)
33
+ const [search, setSearch] = useState("")
34
+ const [error, setError] = useState<string | null>(null)
35
+
36
+ useEffect(() => {
37
+ // Get names and basic of workflow types
38
+ const query: any = {}
39
+ query.fields = JSON.stringify({
40
+ name: 1,
41
+ description: 1,
42
+ roles: 1,
43
+ _created_by: 1,
44
+ _modified_on: 1
45
+ })
46
+ query.client = props.client
47
+
48
+ // Get list of all workflow types
49
+ $.getJSON(props.apiUrl + "workflow_types?" + querystring.stringify(query), (types: WorkflowType[]) => {
50
+ // Sort by included first, then user-created, then by name
51
+ types = _.sortByOrder(
52
+ types,
53
+ [
54
+ (type: WorkflowType) => ((props.extraTables || []).includes("workflows:" + type._id) ? 0 : 1),
55
+ (type: WorkflowType) => (type._created_by === props.user ? 0 : 1),
56
+ (type: WorkflowType) => ExprUtils.localizeString(type.name, T.locale) || T`Untitled Workflow Type`
57
+ ],
58
+ ["asc", "asc", "asc"]
59
+ )
60
+
61
+ setWorkflowTypes(
62
+ _.map(types, (type) => ({
63
+ id: type._id,
64
+ name: ExprUtils.localizeString(type.name, T.locale) || T`Untitled Workflow Type`,
65
+ desc: ExprUtils.localizeString(type.description, T.locale)
66
+ }))
67
+ )
68
+ }).fail((xhr: any) => {
69
+ setError(xhr.responseText)
70
+ })
71
+ }, [props.apiUrl, props.client, props.user, props.extraTables])
72
+
73
+ const handleTableRemove = (table: WorkflowTypeItem) => {
74
+ if (
75
+ confirm(
76
+ T`Remove ${table.name}? Any widgets that depend on it will no longer work properly.`
77
+ )
78
+ ) {
79
+ props.onExtraTableRemove(`workflows:${table.id}`)
80
+ }
81
+ }
82
+
83
+ if (error) {
84
+ return <div className="alert alert-danger">{error}</div>
85
+ }
86
+
87
+ // Filter workflow types by search
88
+ let filteredWorkflowTypes: WorkflowTypeItem[] | null = workflowTypes
89
+ if (search && workflowTypes) {
90
+ const escapeRegExp = (s: string) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")
91
+ const searchStringRegExp = new RegExp(escapeRegExp(search), "i")
92
+ filteredWorkflowTypes = _.filter(workflowTypes, (type) => type.name.match(searchStringRegExp))
93
+ }
94
+
95
+ // Remove if already included
96
+ filteredWorkflowTypes = _.filter(
97
+ filteredWorkflowTypes || [],
98
+ (wt) => !(props.extraTables || []).includes(`workflows:${wt.id}`)
99
+ )
100
+
101
+ // Get included workflow tables from schema
102
+ let includedTables = _.filter(
103
+ props.schema.getTables(),
104
+ (table) => (table.id.match(/^workflows:/) || table.id.match(/^workflow_events:/)) && !table.deprecated
105
+ )
106
+ includedTables = _.sortBy(includedTables, (t) => t.name.en)
107
+
108
+ return (
109
+ <div>
110
+ <label>{T`Included Workflows:`}</label>
111
+ {includedTables.length > 0 ? (
112
+ <uiComponents.OptionListComponent
113
+ items={_.map(includedTables, (table) => {
114
+ return {
115
+ name: ExprUtils.localizeString(table.name, T.locale),
116
+ desc: ExprUtils.localizeString(table.desc, T.locale),
117
+ onClick: () => props.onChange(table.id),
118
+ onRemove: () => handleTableRemove({ id: table.id.replace(/^workflows:/, ""), name: ExprUtils.localizeString(table.name, T.locale) })
119
+ }
120
+ })}
121
+ />
122
+ ) : (
123
+ <div>{T`None`}</div>
124
+ )}
125
+
126
+ <br />
127
+
128
+ <label>{T`All Workflows:`}</label>
129
+ {!workflowTypes || workflowTypes.length === 0 ? (
130
+ <div className="alert alert-info">
131
+ <i className="fa fa-spinner fa-spin" />
132
+ &nbsp;{T`Loading...`}
133
+ </div>
134
+ ) : (
135
+ <>
136
+ <input
137
+ type="text"
138
+ className="form-control form-control-sm"
139
+ placeholder={T`Search...`}
140
+ key="search"
141
+ autoFocus
142
+ style={{ maxWidth: "20em", marginBottom: 10 }}
143
+ value={search}
144
+ onChange={(ev) => setSearch(ev.target.value)}
145
+ />
146
+
147
+ <uiComponents.OptionListComponent
148
+ items={_.map(filteredWorkflowTypes, (workflowType) => ({
149
+ name: workflowType.name,
150
+ desc: workflowType.desc,
151
+ onClick: () => props.onChange("workflows:" + workflowType.id)
152
+ }))}
153
+ />
154
+ </>
155
+ )}
156
+ </div>
157
+ )
158
+ }
159
+