decidim-decidim_geo 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +172 -0
  4. data/Rakefile +119 -0
  5. data/app/cells/decidim/geo/content_blocks/geo_maps/show.erb +15 -0
  6. data/app/cells/decidim/geo/content_blocks/geo_maps_cell.rb +114 -0
  7. data/app/cells/decidim/geo/content_blocks/geo_maps_settings_form_cell.rb +14 -0
  8. data/app/cells/decidim/geo/geo_collection_map/show.erb +4 -0
  9. data/app/cells/decidim/geo/geo_collection_map_cell.rb +102 -0
  10. data/app/commands/decidim/admin/create_scope.rb +56 -0
  11. data/app/commands/decidim/admin/create_scope_type.rb +47 -0
  12. data/app/commands/decidim/admin/update_scope.rb +60 -0
  13. data/app/commands/decidim/admin/update_scope_type.rb +62 -0
  14. data/app/commands/decidim/geo/admin/create_shapefile.rb +51 -0
  15. data/app/commands/decidim/geo/admin/update_geo_config.rb +54 -0
  16. data/app/commands/decidim/geo/command.rb +14 -0
  17. data/app/controllers/decidim/admin/scopes_controller.rb +115 -0
  18. data/app/controllers/decidim/geo/admin/application_controller.rb +21 -0
  19. data/app/controllers/decidim/geo/admin/geo_configs_controller.rb +44 -0
  20. data/app/controllers/decidim/geo/admin/shapefiles_controller.rb +41 -0
  21. data/app/controllers/decidim/geo/application_controller.rb +13 -0
  22. data/app/controllers/decidim/geo/shapefile_controller.rb +11 -0
  23. data/app/forms/decidim/admin/scope_form.rb +43 -0
  24. data/app/forms/decidim/admin/scope_type_form.rb +27 -0
  25. data/app/forms/decidim/geo/admin/geo_config_form.rb +20 -0
  26. data/app/forms/decidim/geo/admin/shapefile_form.rb +23 -0
  27. data/app/helpers/decidim/admin/scopes_helper.rb +72 -0
  28. data/app/jobs/decidim/geo/shp2pgsql_job.rb +14 -0
  29. data/app/models/decidim/debates/debate.rb +226 -0
  30. data/app/models/decidim/geo/application_record.rb +10 -0
  31. data/app/models/decidim/geo/geo_config.rb +28 -0
  32. data/app/models/decidim/geo/shapedata.rb +23 -0
  33. data/app/models/decidim/geo/shapefile.rb +27 -0
  34. data/app/models/decidim/geo/space_location.rb +16 -0
  35. data/app/models/decidim/scope.rb +111 -0
  36. data/app/models/decidim/scope_type.rb +26 -0
  37. data/app/overrides/decidim/assemblies/admin/assemblies/_form/insert_location_form.html.erb.deface +13 -0
  38. data/app/overrides/decidim/assemblies/assemblies/show/insert_location.html.erb.deface +7 -0
  39. data/app/overrides/decidim/participatory_processes/admin/participatory_processes/_form/insert_location_form.html.erb.deface +13 -0
  40. data/app/overrides/decidim/participatory_processes/participatory_process_groups/show/insert_map.html.erb.deface +6 -0
  41. data/app/overrides/layouts/decidim/_assembly_header/insert_map.html.erb.deface +25 -0
  42. data/app/overrides/layouts/decidim/_process_header/insert_map.html.erb.deface +22 -0
  43. data/app/overrides/layouts/decidim/_wrapper/insert_map.html.erb.deface +5 -0
  44. data/app/overrides/remove_default_meetings_map.rb +4 -0
  45. data/app/overrides/remove_default_proposals_map.rb +4 -0
  46. data/app/packs/entrypoints/decidim_geo.js +3 -0
  47. data/app/packs/images/decidim/geo/not-geolocated.svg +3 -0
  48. data/app/packs/src/decidim/geo/actions/index.js +25 -0
  49. data/app/packs/src/decidim/geo/api/index.js +53 -0
  50. data/app/packs/src/decidim/geo/api/queries.js +77 -0
  51. data/app/packs/src/decidim/geo/bootstrap.js +6 -0
  52. data/app/packs/src/decidim/geo/index.js +101 -0
  53. data/app/packs/src/decidim/geo/models/configStore.js +44 -0
  54. data/app/packs/src/decidim/geo/models/dropdownFilterStore.js +65 -0
  55. data/app/packs/src/decidim/geo/models/filterStore.js +121 -0
  56. data/app/packs/src/decidim/geo/models/geoDatasourceNode/GeoDatasourceNode.js +124 -0
  57. data/app/packs/src/decidim/geo/models/geoDatasourceNode/index.js +1 -0
  58. data/app/packs/src/decidim/geo/models/geoScope/GeoScope.js +128 -0
  59. data/app/packs/src/decidim/geo/models/geoScope/index.js +1 -0
  60. data/app/packs/src/decidim/geo/models/geoStore.js +118 -0
  61. data/app/packs/src/decidim/geo/models/pointStore.js +134 -0
  62. data/app/packs/src/decidim/geo/models/scopeDropdownStore.js +20 -0
  63. data/app/packs/src/decidim/geo/ui/DrawerDetail/fallback.js +42 -0
  64. data/app/packs/src/decidim/geo/ui/DrawerDetail/index.js +2 -0
  65. data/app/packs/src/decidim/geo/ui/DrawerDetail/meetings.js +75 -0
  66. data/app/packs/src/decidim/geo/ui/DrawerMenuItem/fallback.js +45 -0
  67. data/app/packs/src/decidim/geo/ui/DrawerMenuItem/index.js +2 -0
  68. data/app/packs/src/decidim/geo/ui/DrawerMenuItem/meetings.js +62 -0
  69. data/app/packs/src/decidim/geo/ui/createClasses.js +7 -0
  70. data/app/packs/src/decidim/geo/ui/createCustomMarker.js +14 -0
  71. data/app/packs/src/decidim/geo/ui/createDomElement.js +2 -0
  72. data/app/packs/src/decidim/geo/ui/createDrawer.js +170 -0
  73. data/app/packs/src/decidim/geo/ui/createDrawerActions.js +197 -0
  74. data/app/packs/src/decidim/geo/ui/createFilterDropdown.js +320 -0
  75. data/app/packs/src/decidim/geo/ui/createGeoScopeLayer.js +20 -0
  76. data/app/packs/src/decidim/geo/ui/createGeoScopeMenuItem.js +8 -0
  77. data/app/packs/src/decidim/geo/ui/createNodeMarker.js +9 -0
  78. data/app/packs/src/decidim/geo/ui/createNodeMenuItem.js +13 -0
  79. data/app/packs/src/decidim/geo/ui/index.js +8 -0
  80. data/app/packs/src/decidim/geo/ui/initMap.js +22 -0
  81. data/app/packs/src/decidim/geo/utils/index.js +30 -0
  82. data/app/packs/src/decidim/geo/utils/saveConfig.js +15 -0
  83. data/app/packs/stylesheets/decidim/geo/_geo.scss +2 -0
  84. data/app/packs/stylesheets/decidim/geo/geo/_decidim_geo_decidimGeo.scss +476 -0
  85. data/app/packs/stylesheets/decidim/geo/geo/_decidim_geo_override.scss +65 -0
  86. data/app/permissions/decidim/geo/admin/permissions.rb +24 -0
  87. data/app/permissions/decidim/geo/permissions.rb +15 -0
  88. data/app/uploaders/decidim/geo/shapefile_uploader.rb +14 -0
  89. data/app/validators/geocoding_validator.rb +30 -0
  90. data/app/views/decidim/admin/scope_types/_form.html.erb +10 -0
  91. data/app/views/decidim/admin/scope_types/index.html.erb +46 -0
  92. data/app/views/decidim/admin/scopes/_form.html.erb +20 -0
  93. data/app/views/decidim/admin/scopes/index.html.erb +55 -0
  94. data/app/views/decidim/geo/admin/geo_configs/_form.html.erb +47 -0
  95. data/app/views/decidim/geo/admin/geo_configs/index.html.erb +8 -0
  96. data/app/views/decidim/geo/admin/shapefiles/_form.html.erb +29 -0
  97. data/app/views/decidim/geo/admin/shapefiles/index.html.erb +38 -0
  98. data/app/views/decidim/geo/admin/shapefiles/new.html.erb +8 -0
  99. data/app/views/layouts/decidim/decidim_geo/admin/application.html.erb +19 -0
  100. data/config/assets.rb +9 -0
  101. data/config/i18n-tasks.yml +10 -0
  102. data/config/locales/de.yml +102 -0
  103. data/config/locales/en.yml +108 -0
  104. data/config/locales/fr.yml +102 -0
  105. data/db/migrate/20221019184712_add_postgis_extension_to_database.rb +5 -0
  106. data/db/migrate/20221025195520_create_decidim_geo.rb +22 -0
  107. data/db/migrate/20231013082325_create_decidim_geo_config.rb +11 -0
  108. data/db/migrate/20231206115531_add_spaces_config_to_decidim_geo_configs.rb +6 -0
  109. data/db/migrate/20240309004347_add_organization_to_shapefiles.rb +6 -0
  110. data/db/migrate/20240326052727_create_space_locations.rb +18 -0
  111. data/lib/decidim/api/geo_config_type.rb +15 -0
  112. data/lib/decidim/api/geo_coordinates_type.rb +22 -0
  113. data/lib/decidim/api/geo_datasource_type.rb +147 -0
  114. data/lib/decidim/api/geo_dates_type.rb +15 -0
  115. data/lib/decidim/api/geo_query_extension.rb +25 -0
  116. data/lib/decidim/api/geo_scope_api_type.rb +26 -0
  117. data/lib/decidim/api/geo_shapedata_type.rb +18 -0
  118. data/lib/decidim/api/geo_shapefile_type.rb +22 -0
  119. data/lib/decidim/api/input_filters/assembly_input_filter.rb +13 -0
  120. data/lib/decidim/api/input_filters/geo_datasource_input_filter.rb +18 -0
  121. data/lib/decidim/api/input_filters/geoencoded_input_filter.rb +13 -0
  122. data/lib/decidim/api/input_filters/has_scopeable_input_filter.rb +17 -0
  123. data/lib/decidim/api/input_filters/process_input_filter.rb +13 -0
  124. data/lib/decidim/api/input_filters/resource_type_input_filter.rb +13 -0
  125. data/lib/decidim/api/input_filters/scope_input_filter.rb +13 -0
  126. data/lib/decidim/api/input_filters/time_input_filter.rb +13 -0
  127. data/lib/decidim/api/meetings_input_filter.rb +14 -0
  128. data/lib/decidim/api/query_extension.rb +320 -0
  129. data/lib/decidim/api/shapefile_query_extention.rb +18 -0
  130. data/lib/decidim/api/shapefile_type.rb +18 -0
  131. data/lib/decidim/decidim_geo/admin.rb +10 -0
  132. data/lib/decidim/decidim_geo/admin_engine.rb +69 -0
  133. data/lib/decidim/decidim_geo/api.rb +23 -0
  134. data/lib/decidim/decidim_geo/engine.rb +58 -0
  135. data/lib/decidim/decidim_geo/load_shp/app_load_shp.rb +85 -0
  136. data/lib/decidim/decidim_geo/space_location/assembly_create_command_override.rb +26 -0
  137. data/lib/decidim/decidim_geo/space_location/assembly_form_override.rb +33 -0
  138. data/lib/decidim/decidim_geo/space_location/assembly_update_command_override.rb +21 -0
  139. data/lib/decidim/decidim_geo/space_location/participatory_process_command_override.rb +21 -0
  140. data/lib/decidim/decidim_geo/space_location/participatory_process_form_override.rb +33 -0
  141. data/lib/decidim/decidim_geo/space_location/space_override.rb +15 -0
  142. data/lib/decidim/decidim_geo/test/factories.rb +13 -0
  143. data/lib/decidim/decidim_geo/version.rb +14 -0
  144. data/lib/decidim/decidim_geo.rb +25 -0
  145. data/lib/tasks/decidim_geo_webpacker_tasks.rake +61 -0
  146. metadata +335 -0
@@ -0,0 +1,170 @@
1
+ import createClasses from "./createClasses";
2
+ import geoStore from "../models/geoStore";
3
+ import pointStore from "../models/pointStore";
4
+ import configStore from "../models/configStore";
5
+ import filterStore from "../models/filterStore";
6
+ import dropdownFilterStore from "../models/dropdownFilterStore";
7
+ import { meetings as meetingDetails, fallback as fallbackDetails } from "./DrawerDetail";
8
+
9
+ const createSkeletonItem = () => L.DomUtil.create("li", "decidimGeo__drawer__listCard");
10
+
11
+ async function createSidebar() {
12
+ const CustomLayerControl = L.Control.extend({
13
+ options: {
14
+ collapsed: false,
15
+ position: "topleft"
16
+ },
17
+
18
+ // View
19
+ cardList: null,
20
+ _loadingDOM: null,
21
+
22
+ isEmpty() {
23
+ return false;
24
+ },
25
+
26
+ loadingDom() {
27
+ if (this._loadingDOM) return this._loadingDOM;
28
+ this._loadingDOM = L.DomUtil.create(
29
+ "div",
30
+ createClasses("decidimGeo__drawer__listCardLoader")
31
+ );
32
+ this._loadingDOM.appendChild(createSkeletonItem());
33
+ this._loadingDOM.appendChild(createSkeletonItem());
34
+ this._loadingDOM.appendChild(createSkeletonItem());
35
+ return this._loadingDOM;
36
+ },
37
+
38
+ repaintList(points) {
39
+ points.map(({ menuItem }) => {
40
+ this.cardList.appendChild(menuItem);
41
+ });
42
+ },
43
+ repaintEmpty() {
44
+ const emptyContainer = L.DomUtil.create(
45
+ "li",
46
+ "decidimGeo__emptyDrawer__container",
47
+ this.cardList
48
+ );
49
+ const emptyParagraph = L.DomUtil.create(
50
+ "p",
51
+ "decidimGeo__emptyDrawer__paragraph",
52
+ emptyContainer
53
+ );
54
+ emptyParagraph.textContent = "No data with these filters";
55
+ const emptyAction = L.DomUtil.create(
56
+ "button",
57
+ "decidimGeo__emptyDrawer__button",
58
+ emptyContainer
59
+ );
60
+ emptyAction.textContent = "Reset filter";
61
+ emptyAction.onclick = () => {
62
+ const { resetFilters } = filterStore.getState();
63
+ const { resetFilters: resetDropdownFilter } = dropdownFilterStore.getState();
64
+ const { selectScope } = geoStore.getState();
65
+ const { space_ids } = configStore.getState();
66
+ const { scopeForId } = pointStore.getState();
67
+
68
+ resetFilters();
69
+ resetDropdownFilter();
70
+ const scopes = space_ids.map((scope) => scopeForId(scope)).filter(Boolean);
71
+ if (scopes.length === 1) {
72
+ const scope = scopeForId(scopes[0]);
73
+ selectScope(scope);
74
+ scope.repaint();
75
+ } else {
76
+ selectScope(undefined);
77
+ }
78
+ this.repaint();
79
+ };
80
+ },
81
+ repaintLoading() {
82
+ const loadingElement = this.loadingDom();
83
+ this.cardList.appendChild(loadingElement);
84
+ },
85
+ repaintDetail({ data: node }) {
86
+ const listCard = L.DomUtil.create(
87
+ "li",
88
+ "decidimGeo__drawer__listCard",
89
+ this.cardList
90
+ );
91
+ const onClick = () => {
92
+ location.href = node.link;
93
+ };
94
+ listCard.onclick = onClick;
95
+
96
+ const info = L.DomUtil.create(
97
+ "div",
98
+ "decidimGeo__drawer__listCardInfo decidimGeo__drawer__listCardInfo--large",
99
+ listCard
100
+ );
101
+ switch (node.type) {
102
+ case "Decidim::Meetings::Meeting":
103
+ meetingDetails(info, node);
104
+ break;
105
+ default:
106
+ fallbackDetails(info, node);
107
+ break;
108
+ }
109
+ const viewBtn = L.DomUtil.create("a", "decidimGeo__drawer__viewBtn", listCard);
110
+ viewBtn.textContent = "View";
111
+ viewBtn.href = node.link;
112
+ return listCard;
113
+ },
114
+ repaint() {
115
+ L.DomUtil.empty(this.cardList);
116
+ const { isLoading, getFilteredPoints, points } = pointStore.getState();
117
+ const { selectedPoint } = geoStore.getState();
118
+ const pointsInMap = getFilteredPoints();
119
+
120
+ if (isLoading) {
121
+ return this.repaintLoading();
122
+ }
123
+ if (selectedPoint) {
124
+ return this.repaintDetail(selectedPoint);
125
+ }
126
+
127
+ if (pointsInMap.length === 0) {
128
+ this.repaintEmpty();
129
+ } else {
130
+ this.repaintList(pointsInMap);
131
+ }
132
+ },
133
+ onAdd(map) {
134
+ const { isLoading, points } = pointStore.getState();
135
+ this.cardList = L.DomUtil.create(
136
+ "ul",
137
+ createClasses("decidimGeo__drawer__list", [
138
+ isLoading && "loading",
139
+ this.isEmpty() && "empty"
140
+ ])
141
+ );
142
+ L.DomEvent.disableClickPropagation(this.cardList);
143
+ L.DomEvent.disableScrollPropagation(this.cardList);
144
+
145
+ const _this = this;
146
+ const repaint = this.repaint.bind(this);
147
+
148
+ geoStore.subscribe(
149
+ (state) => [state.selectedPoint, state.selectedScope],
150
+ () => {
151
+ repaint();
152
+ }
153
+ );
154
+
155
+ pointStore.subscribe(
156
+ (state) => [state.isLoading, state._lastResponse],
157
+ ([isLoading]) => {
158
+ if (!isLoading) repaint();
159
+ }
160
+ );
161
+ this.repaint();
162
+ return this.cardList;
163
+ }
164
+ });
165
+ const control = new CustomLayerControl();
166
+ const { map } = configStore.getState();
167
+ map.addControl(control);
168
+ }
169
+
170
+ export default createSidebar;
@@ -0,0 +1,197 @@
1
+ import createGeoScopeMenuItem from "./createGeoScopeMenuItem";
2
+ import createClasses from "./createClasses";
3
+ import geoStore from "../models/geoStore";
4
+ import pointStore from "../models/pointStore";
5
+ import configStore from "../models/configStore";
6
+ import createFilterDropdown from "./createFilterDropdown";
7
+ import scopeDropdownStore from "../models/scopeDropdownStore";
8
+
9
+ async function createScopesDropdown() {
10
+ const CustomLayerControl = L.Control.extend({
11
+ options: {
12
+ collapsed: false,
13
+ position: "topleft"
14
+ },
15
+
16
+ //View
17
+ menu: null,
18
+ heading: null,
19
+ filterDropdown: null,
20
+ title: null,
21
+ dropDownOptions: null,
22
+
23
+ //Controlers
24
+ toggleOpen() {
25
+ scopeDropdownStore.getState().toggleOpen();
26
+ },
27
+ scopes() {
28
+ const { scopeForId, scopes } = pointStore.getState();
29
+ const { space_ids } = configStore.getState();
30
+ if (space_ids.length > 0)
31
+ return space_ids.map((id) => scopeForId(id)).filter(Boolean);
32
+ return scopes;
33
+ },
34
+ isEmpty() {
35
+ return this.scopes().length < 2;
36
+ },
37
+ activeScope() {
38
+ return geoStore.getState().selectedScope;
39
+ },
40
+ initMenuElements() {
41
+ const { isLoading } = pointStore.getState();
42
+ const { isOpen } = scopeDropdownStore.getState();
43
+ this.heading = L.DomUtil.create(
44
+ "div",
45
+ createClasses("decidimGeo__scopesDropdown__heading", [!isOpen && "closed"]),
46
+ this.menu
47
+ );
48
+
49
+ this.title = L.DomUtil.create(
50
+ "h6",
51
+ createClasses("decidimGeo__scopesDropdown__title", [
52
+ !isOpen && "closed",
53
+ isLoading && "loading"
54
+ ]),
55
+ this.heading
56
+ );
57
+ this.title.onclick = () => {
58
+ this.toggleOpen();
59
+ this.repaint();
60
+ };
61
+ this.filterDropdown = createFilterDropdown(this.heading, this.menu);
62
+ this.dropDownOptions = L.DomUtil.create(
63
+ "ul",
64
+ createClasses("decidimGeo__scopesDropdown__list", [
65
+ !isOpen && "closed",
66
+ this.isEmpty() && "empty"
67
+ ]),
68
+ this.menu
69
+ );
70
+
71
+ this.repaint();
72
+ },
73
+ repaintHeading() {
74
+ const { i18n } = configStore.getState();
75
+ const { selectedPoint } = geoStore.getState();
76
+ const scopes = this.scopes();
77
+ if (selectedPoint) {
78
+ this.title.textContent = i18n["decidim_geo.filters.back"];
79
+ this.title.onclick = () => {
80
+ selectedPoint.panToMarker(16);
81
+ geoStore.getState().goBack();
82
+ };
83
+ return;
84
+ } else if (scopes.length === 0) {
85
+ this.title.textContent = i18n["decidim_geo.scopes.dropdown"];
86
+ this.title.className += " decidimGeo__scopesDropdown__list--disabled";
87
+ this.title.onclick = () => false;
88
+ return;
89
+ } else {
90
+ this.title.onclick = () => {
91
+ this.toggleOpen();
92
+ this.repaint();
93
+ };
94
+ }
95
+
96
+ // Dropdown heading text
97
+ if (this.activeScope()) {
98
+ // specific scope
99
+ this.title.textContent = this.activeScope().name;
100
+ } else if (scopes.length === 1) {
101
+ this.title.textContent = scopes[0].name;
102
+ } else {
103
+ // all scopes
104
+ this.title.textContent = i18n["decidim_geo.scopes.dropdown"];
105
+ }
106
+ },
107
+ repaintOptions() {
108
+ const { i18n } = configStore.getState();
109
+ const scopes = this.scopes();
110
+ // Dropdown options
111
+ L.DomUtil.empty(this.dropDownOptions);
112
+ if (scopes.length == 1) return;
113
+ if (this.activeScope() && scopes.length > 1) {
114
+ // Add a "All Scope" menu item
115
+ const resetItem = createGeoScopeMenuItem({
116
+ label: i18n["decidim_geo.scopes.all"],
117
+ onClick: () => {
118
+ this.toggleOpen();
119
+ geoStore.getState().selectScope(undefined);
120
+ geoStore.getState().selectPoint(undefined);
121
+ }
122
+ });
123
+ this.dropDownOptions.appendChild(resetItem);
124
+ }
125
+ // Add all the other scopes
126
+ scopes.forEach((geoScope) => {
127
+ if (geoScope !== this.activeScope() && !geoScope.isEmpty())
128
+ this.dropDownOptions.appendChild(geoScope.menuItem);
129
+ });
130
+ },
131
+ repaintOpenClose() {
132
+ const { selectedPoint } = geoStore.getState();
133
+ const { isOpen } = scopeDropdownStore.getState();
134
+
135
+ // Dropdown backdrop open/close
136
+ this.title.className = createClasses("decidimGeo__scopesDropdown__title", [
137
+ !isOpen && "closed",
138
+ !selectedPoint && this.isEmpty() && "empty",
139
+ selectedPoint && "button"
140
+ ]);
141
+ this.dropDownOptions.className = createClasses("decidimGeo__scopesDropdown__list", [
142
+ !isOpen && "closed",
143
+ this.isEmpty() && "empty",
144
+ selectedPoint && "hidden"
145
+ ]);
146
+ },
147
+ repaint() {
148
+ this.repaintHeading();
149
+ this.repaintOptions();
150
+ this.repaintOpenClose();
151
+ },
152
+ onAdd(map) {
153
+ this.menu = L.DomUtil.create("div", "decidimGeo__scopesDropdown");
154
+ L.DomEvent.disableClickPropagation(this.menu);
155
+ L.DomEvent.disableScrollPropagation(this.menu);
156
+ const repaint = this.repaint.bind(this);
157
+ // We we change the available scopes, repaint.
158
+ geoStore.subscribe(
159
+ (state) => [state.selectedScope],
160
+ ([_geoScope], [prevGeoScope]) => {
161
+ if (prevGeoScope) {
162
+ prevGeoScope.repaint();
163
+ }
164
+ repaint();
165
+ }
166
+ );
167
+ pointStore.subscribe(
168
+ (state) => [state.scopes, state.isLoading],
169
+ ([scopes, isLoading]) => {
170
+ if (!scopes || scopes.length == 0 || isLoading) return;
171
+ repaint();
172
+ }
173
+ );
174
+ this.initMenuElements();
175
+ this.repaint(); // first repaint
176
+ scopeDropdownStore.subscribe(
177
+ (state) => [state.isOpen],
178
+ ([]) => {
179
+ this.repaint();
180
+ }
181
+ );
182
+ return this.menu;
183
+ },
184
+ reset() {
185
+ scopeDropdownStore.getState().close();
186
+ },
187
+ close() {
188
+ scopeDropdownStore.getState().close();
189
+ }
190
+ });
191
+
192
+ const control = new CustomLayerControl();
193
+ const { map } = configStore.getState();
194
+ map.addControl(control);
195
+ }
196
+
197
+ export default createScopesDropdown;
@@ -0,0 +1,320 @@
1
+ import createClasses from "./createClasses";
2
+ import dropdownFilterStore from "../models/dropdownFilterStore";
3
+ import geoStore from "../models/geoStore";
4
+ import configStore from "../models/configStore";
5
+ import filterStore from "../models/filterStore";
6
+ import pointStore from "../models/pointStore";
7
+ class FilterDropdown {
8
+ constructor(parent, anchor) {
9
+ this.container = L.DomUtil.create(
10
+ "div",
11
+ "decidimGeo__filterDropdown__container",
12
+ parent
13
+ );
14
+ this.titleContainer = L.DomUtil.create(
15
+ "div",
16
+ "decidimGeo__filterDropdown__titleContainer",
17
+ this.container
18
+ );
19
+ this.title = L.DomUtil.create(
20
+ "h6",
21
+ createClasses("decidimGeo__filterDropdown__title", [this.isOpen() && "active"]),
22
+ this.titleContainer
23
+ );
24
+ this.countBadge = L.DomUtil.create(
25
+ "span",
26
+ createClasses("decidimGeo__filterDropdown__counter", ["hidden"]),
27
+ this.titleContainer
28
+ );
29
+ this.countBadge.textContent = 0;
30
+
31
+ this.dropdown = L.DomUtil.create(
32
+ "div",
33
+ createClasses("decidimGeo__filterDropdown__dropdown", [!this.isOpen() && "closed"]),
34
+ anchor
35
+ );
36
+ this.dropDownOptions = L.DomUtil.create(
37
+ "ul",
38
+ "decidimGeo__filterDropdown__list",
39
+ this.dropdown
40
+ );
41
+ this.resetBtn = L.DomUtil.create(
42
+ "button",
43
+ "decidimGeo__filterDropdown__resetBtn",
44
+ this.dropdown
45
+ );
46
+ this.resetBtn.textContent = "Reset";
47
+ this.resetBtn.onclick = () => {
48
+ const { resetFilters } = filterStore.getState();
49
+ const {
50
+ resetFilters: resetDropdownFilter,
51
+ toggleOpen,
52
+ defaultFilters
53
+ } = dropdownFilterStore.getState();
54
+ resetDropdownFilter();
55
+ // resetFilters();
56
+ this.applyValues(defaultFilters);
57
+ toggleOpen();
58
+ };
59
+ this.applyBtn = L.DomUtil.create(
60
+ "button",
61
+ "decidimGeo__filterDropdown__applyBtn",
62
+ this.dropdown
63
+ );
64
+ this.applyBtn.textContent = "Apply";
65
+
66
+ this.applyBtn.onclick = () => {
67
+ const { nextFilters, toggleOpen, applyNextFilters } =
68
+ dropdownFilterStore.getState();
69
+ this.applyValues(nextFilters);
70
+ applyNextFilters();
71
+ toggleOpen();
72
+ this.repaint();
73
+ };
74
+
75
+ this.title.textContent = this.i18n()["decidim_geo.filters.button"];
76
+ this.title.onclick = () => {
77
+ this.toggle();
78
+ this.repaint();
79
+ };
80
+
81
+ dropdownFilterStore.subscribe(
82
+ (state) => [state.isOpen],
83
+ () => this.repaint()
84
+ );
85
+ filterStore.subscribe(
86
+ (state) => [state.activeFilters],
87
+ () => {
88
+ this.repaint();
89
+ }
90
+ );
91
+ }
92
+
93
+ defaultFilterFor(name) {
94
+ const { toFilterOptions, defaultFilters } = filterStore.getState();
95
+ return toFilterOptions(name, defaultFilters);
96
+ }
97
+ field(label, name, options, disabledOptions=[]) {
98
+ const { nextFilters } = dropdownFilterStore.getState();
99
+ const selectedValue =
100
+ (nextFilters && nextFilters[name]) || this.defaultFilterFor(name);
101
+
102
+ const fieldGroup = L.DomUtil.create(
103
+ "li",
104
+ "decidimGeo__filterDropdown__field",
105
+ this.dropDownOptions
106
+ );
107
+ const labelTag = L.DomUtil.create(
108
+ "label",
109
+ "decidimGeo__filterDropdown__label",
110
+ fieldGroup
111
+ );
112
+ labelTag.htmlFor = name;
113
+ labelTag.textContent = label;
114
+
115
+ const selectTag = L.DomUtil.create(
116
+ "select",
117
+ "decidimGeo__filterDropdown__select",
118
+ fieldGroup
119
+ );
120
+ selectTag.id = name;
121
+ selectTag.onchange = (evt) => {
122
+ dropdownFilterStore.getState().setNextFilter(name, evt.target.value);
123
+ };
124
+ options.forEach(([key, value]) => {
125
+ const option = L.DomUtil.create(
126
+ "option",
127
+ "decidimGeo__filterDropdown__option",
128
+ selectTag
129
+ );
130
+ option.name = name.toLowerCase();
131
+ option.value = value;
132
+ if (value === selectedValue) {
133
+ option.selected = "selected";
134
+ }
135
+ if(disabledOptions.includes(value)) {
136
+ option.disabled="disabled"
137
+ }
138
+ option.textContent = key;
139
+ });
140
+ return selectTag;
141
+ }
142
+
143
+ applyValues(filters) {
144
+ if (!filters) {
145
+ throw new Error("Filters missing");
146
+ }
147
+ const { setFilters, activeFilters, defaultFilters } = filterStore.getState();
148
+ const newFilters = activeFilters.filter((filter) => {
149
+ const [filterName] = Object.keys(filter);
150
+ return !["resourceTypeFilter", "timeFilter", "geoencodedFilter"].includes(
151
+ filterName
152
+ );
153
+ });
154
+ switch (filters.GeoShowFilter || defaultFilters.GeoShowFilter) {
155
+ case "all":
156
+ break;
157
+ case "only_geoencoded":
158
+ newFilters.push({
159
+ geoencodedFilter: { geoencoded: true }
160
+ });
161
+ break;
162
+ case "only_virtual":
163
+ newFilters.push({
164
+ geoencodedFilter: { geoencoded: false }
165
+ });
166
+ break;
167
+ }
168
+ switch (filters.GeoTimeFilter || defaultFilters.GeoTimeFilter) {
169
+ case "all":
170
+ newFilters.push({
171
+ timeFilter: { time: "all" }
172
+ });
173
+ break;
174
+ case "only_past":
175
+ newFilters.push({
176
+ timeFilter: { time: "past" }
177
+ });
178
+ break;
179
+ case "only_active":
180
+ newFilters.push({
181
+ timeFilter: { time: "active" }
182
+ });
183
+ break;
184
+ case "only_future":
185
+ newFilters.push({
186
+ timeFilter: { time: "future" }
187
+ });
188
+ break;
189
+ }
190
+
191
+ switch (filters.GeoType || defaultFilters.GeoType) {
192
+ case "all":
193
+ newFilters.push({
194
+ resourceTypeFilter: { resourceType: "all" }
195
+ });
196
+ break;
197
+ case "only_processes":
198
+ newFilters.push({
199
+ resourceTypeFilter: { resourceType: "Decidim::ParticipatoryProcess" }
200
+ });
201
+ break;
202
+ case "only_assemblies":
203
+ newFilters.push({ resourceTypeFilter: { resourceType: "Decidim::Assembly" } });
204
+ break;
205
+ case "only_proposals":
206
+ newFilters.push({
207
+ resourceTypeFilter: { resourceType: "Decidim::Proposals::Proposal" }
208
+ });
209
+ break;
210
+ case "only_meetings":
211
+ newFilters.push({
212
+ resourceTypeFilter: { resourceType: "Decidim::Meetings::Meeting" }
213
+ });
214
+ break;
215
+ }
216
+ setFilters(newFilters);
217
+ }
218
+
219
+ geoFields(points){
220
+ const i18n = this.i18n();
221
+ const i18nPrefix = "decidim_geo.filters";
222
+ const hasGeoLocated = points.find((p) => p.isGeoLocated)
223
+ const hasPhysical = points.find((p) => !p.isGeoLocated)
224
+
225
+ return this.field(i18n[`${i18nPrefix}.geo.label`], "GeoShowFilter", [
226
+ [i18n[`${i18nPrefix}.geo.all`], "all"],
227
+ [i18n[`${i18nPrefix}.geo.only_geoencoded`], "only_geoencoded"],
228
+ [i18n[`${i18nPrefix}.geo.only_virtual`], "only_virtual"]
229
+ ], hasGeoLocated && hasPhysical ? [] : ["only_geoencoded", "only_virtual"]);
230
+ }
231
+ repaintOptions() {
232
+ L.DomUtil.empty(this.dropDownOptions);
233
+ const i18n = this.i18n();
234
+ const i18nPrefix = "decidim_geo.filters"
235
+ const {points} = pointStore.getState();
236
+ this.geoFields(points);
237
+ this.field(i18n[`${i18nPrefix}.time.label`], "GeoTimeFilter", [
238
+ [i18n[`${i18nPrefix}.time.all`], "all"],
239
+ [i18n[`${i18nPrefix}.time.only_past`], "only_past"],
240
+ [i18n[`${i18nPrefix}.time.only_active`], "only_active"],
241
+ [i18n[`${i18nPrefix}.time.only_future`], "only_future"]
242
+ ]);
243
+ this.typeFields(points);
244
+
245
+ }
246
+
247
+ typeFields(points) {
248
+ const i18n = this.i18n();
249
+ const i18nPrefix = "decidim_geo.filters";
250
+ const hasMeetings = points.find((p) => p.type === "Decidim::Meetings::Meeting")
251
+ const hasProposals = points.find((p) => p.type === "Decidim::Proposals::Proposal")
252
+ const hasAssemblies = points.find((p) => p.type === "Decidim::Assembly")
253
+ const hasProcesses = points.find((p) => p.type === "Decidim::ParticipatoryProcess")
254
+
255
+ const disabledOptions = [];
256
+ if(!hasMeetings)
257
+ disabledOptions.push("only_meetings")
258
+ if(!hasProposals)
259
+ disabledOptions.push("only_proposals")
260
+ if(!hasAssemblies)
261
+ disabledOptions.push("only_assemblies")
262
+ if(!hasProcesses)
263
+ disabledOptions.push("only_processes")
264
+ this.field(i18n[`${i18nPrefix}.type.label`], "GeoType", [
265
+ [i18n[`${i18nPrefix}.type.all`], "all"],
266
+ [i18n[`${i18nPrefix}.type.only_processes`], "only_processes"],
267
+ [i18n[`${i18nPrefix}.type.only_assemblies`], "only_assemblies"],
268
+ [i18n[`${i18nPrefix}.type.only_proposals`], "only_proposals"],
269
+ [i18n[`${i18nPrefix}.type.only_meetings`], "only_meetings"]
270
+ ], disabledOptions);
271
+ }
272
+
273
+ repaint() {
274
+ const { selectedPoint } = geoStore.getState();
275
+ const { filterCount } = dropdownFilterStore.getState();
276
+ const badgeCount = filterCount();
277
+ this.title.onclick = selectedPoint
278
+ ? () => {}
279
+ : () => {
280
+ this.toggle();
281
+ this.repaint();
282
+ };
283
+ this.countBadge.className = createClasses("decidimGeo__filterDropdown__counter", [
284
+ badgeCount === 0 && "hidden"
285
+ ]);
286
+ this.countBadge.textContent = badgeCount;
287
+ this.title.className = createClasses("decidimGeo__filterDropdown__title", [
288
+ this.isOpen() && "active"
289
+ ]);
290
+ this.titleContainer.className = createClasses(
291
+ "decidimGeo__filterDropdown__titleContainer",
292
+ [selectedPoint && "disabled"]
293
+ );
294
+
295
+ this.dropdown.className = createClasses("decidimGeo__filterDropdown__dropdown", [
296
+ !this.isOpen() && "closed"
297
+ ]);
298
+
299
+ this.repaintOptions();
300
+ }
301
+
302
+ isOpen() {
303
+ return dropdownFilterStore.getState().isOpen;
304
+ }
305
+
306
+ toggle() {
307
+ dropdownFilterStore.getState().toggleOpen();
308
+ }
309
+
310
+ i18n() {
311
+ return this.config().i18n;
312
+ }
313
+
314
+ config() {
315
+ return configStore.getState();
316
+ }
317
+ }
318
+
319
+ const createFilterDropdown = (parent, anchor) => new FilterDropdown(parent, anchor);
320
+ export default createFilterDropdown;
@@ -0,0 +1,20 @@
1
+ const createGeoScopeLayer = ({ geoScope, onClick }) => {
2
+ geoScope.geom = {
3
+ ...geoScope.geom,
4
+ properties: { id: geoScope.id, name: geoScope.name.translation }
5
+ };
6
+ const layer = L.geoJSON(geoScope.geom, {
7
+ style: {
8
+ fillColor: "#cccccc",
9
+ color: "#999999",
10
+ lineJoin: "miter",
11
+ dashArray: "5, 10",
12
+ dashOffset: "5"
13
+ }
14
+ });
15
+ layer.on("click", onClick);
16
+
17
+ return layer;
18
+ };
19
+
20
+ export default createGeoScopeLayer;