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,118 @@
1
+ import { createStore } from "zustand/vanilla";
2
+ import { subscribeWithSelector } from "zustand/middleware";
3
+ import dataPointStore from "./pointStore";
4
+ import filterStore from "./filterStore";
5
+ import dropdownFilterStore from "./dropdownFilterStore";
6
+
7
+ const store = createStore(
8
+ subscribeWithSelector((set) => ({
9
+ /**
10
+ * Filters given by the backend
11
+ */
12
+ defaultFilters: [],
13
+ /**
14
+ * data point that is currently clicked
15
+ */
16
+ selectedPoint: undefined,
17
+ /**
18
+ *
19
+ */
20
+ selectedScope: undefined,
21
+ previousState: null,
22
+ /**
23
+ * Action when we click on a point.
24
+ */
25
+ selectPoint: (point) => {
26
+ set((state) => ({
27
+ ...state,
28
+ selectedScope: point ? dataPointStore.getState().scopeForId(point.scopeId) : undefined,
29
+ selectedPoint: point,
30
+ previousState: state
31
+ }));
32
+ },
33
+ /**
34
+ * Action when a scope is selected.
35
+ * - select the scope if any
36
+ * -
37
+ */
38
+ selectScope: (scope) => {
39
+ set((state) => ({
40
+ selectedScope: scope,
41
+ ...(`${scope?.id}` !== `${state.selectedScope?.id}`
42
+ ? { previousState: state }
43
+ : {}),
44
+ // unselect point when moving away from its scope
45
+ selectedPoint:
46
+ state.selectedPoint && state.selectedPoint.scopeId === scope?.id
47
+ ? state.selectedPoint
48
+ : undefined
49
+ }));
50
+ if(scope)
51
+ scope.repaint();
52
+ },
53
+ /**
54
+ * Go back to previous state
55
+ */
56
+ goBack: () => {
57
+ set((state) => ({
58
+ ...state.previousState
59
+ }));
60
+ }
61
+ }))
62
+ );
63
+
64
+ store.subscribe(
65
+ (state) => [state.selectedScope],
66
+ ([selectedScope]) => {
67
+ // Close the drowp down
68
+ dropdownFilterStore.getState().close();
69
+
70
+ // Update filter associated to the scope
71
+ const { activeFilters, setFilters, scopeFilter, defaultFilters } =
72
+ filterStore.getState();
73
+ const filtersWithoutScopes = activeFilters.filter(({ scopeFilter }) => {
74
+ return !scopeFilter;
75
+ });
76
+ const currentScopeFilterId = scopeFilter(activeFilters);
77
+ if (`${currentScopeFilterId}` === `${selectedScope?.id}`) return;
78
+ if (selectedScope?.id) {
79
+ const matchFilter = activeFilters.find(
80
+ ({ scopeFilter }) => scopeFilter?.scopeId === selectedScope.id
81
+ );
82
+ if (!matchFilter)
83
+ setFilters(
84
+ filtersWithoutScopes.concat({ scopeFilter: { scopeId: selectedScope.id } })
85
+ );
86
+ } else {
87
+ const defaultScopeFilter = defaultFilters.find(({ scopeFiler }) => scopeFilter);
88
+ if (defaultScopeFilter) setFilters(filtersWithoutScopes.concat(defaultScopeFilter));
89
+ else setFilters(filtersWithoutScopes);
90
+ }
91
+ }
92
+ );
93
+
94
+ store.subscribe(
95
+ (state) => [state.selectedScope],
96
+ ([selectedScope], [previousScope]) => {
97
+ // Close the filter dropdown
98
+ dropdownFilterStore.getState().close();
99
+ if (previousScope) previousScope.repaint();
100
+ if (selectedScope) {
101
+ selectedScope.repaint();
102
+ }
103
+ }
104
+ );
105
+
106
+ store.subscribe(
107
+ (state) => [state.selectedPoint],
108
+ async ([selectedPoint], [previousPoint]) => {
109
+ if (!selectedPoint || selectedPoint === previousPoint) return;
110
+ // Close the filter dropdown
111
+ dropdownFilterStore.getState().close();
112
+ // Center to the marker
113
+ selectedPoint.repaint();
114
+ await selectedPoint.panToMarker();
115
+ if (previousPoint) previousPoint.repaint();
116
+ }
117
+ );
118
+ export default store;
@@ -0,0 +1,134 @@
1
+ import { createStore } from "zustand/vanilla";
2
+ import configStore from "./configStore";
3
+ import geoStore from "./geoStore";
4
+ import { getGeoDatasource, getGeoScopes } from "../api";
5
+ import GeoDatasourceNode from "./geoDatasourceNode";
6
+ import GeoScope from "./geoScope";
7
+ import { subscribeWithSelector } from "zustand/middleware";
8
+ import _ from "lodash";
9
+
10
+ const store = createStore(
11
+ subscribeWithSelector((set) => ({
12
+ /**
13
+ * Data Points
14
+ */
15
+ points: [],
16
+ scopes: [],
17
+ isLoading: 0,
18
+ _lastFilter: "",
19
+ _lastResponse: [],
20
+ scopeForId: (scopeId) => {
21
+ return store.getState().scopes.find(({ data }) => `${data.id}` === `${scopeId}`);
22
+ },
23
+ clearCache: () => {
24
+ set(() => ({ _lastFilter: "", _lastResponse: [] }));
25
+ },
26
+ addProcess: () => {
27
+ set(({ isLoading }) => ({ isLoading: isLoading + 1 }));
28
+ },
29
+ removeProcess: () => {
30
+ set(({ isLoading }) => ({ isLoading: isLoading - 1 }));
31
+ },
32
+ getFilteredPoints: () => store.getState()._lastResponse,
33
+ fetchAll: async (filters = []) => {
34
+ const { points: fetchedPoints } = store.getState();
35
+ if (fetchedPoints.length > 0) return;
36
+ const locale = configStore.getState().locale;
37
+ set(({ isLoading }) => ({ isLoading: isLoading + 1 }));
38
+ const data = await getGeoDatasource(
39
+ {
40
+ variables: { filters: filters, locale: locale }
41
+ },
42
+ true
43
+ );
44
+ const points = data.nodes
45
+ .map((node) => {
46
+ const point = new GeoDatasourceNode({
47
+ node
48
+ });
49
+ if (point.init()) {
50
+ return point;
51
+ }
52
+ return undefined;
53
+ })
54
+ .filter(Boolean);
55
+
56
+ set(() => ({
57
+ points: points
58
+ }));
59
+ const scopes = await getGeoScopes({
60
+ variables: { locale: locale }
61
+ });
62
+ const geoScopes = scopes
63
+ .map((scope) => {
64
+ const geoScope = new GeoScope({
65
+ geoScope: scope
66
+ });
67
+ geoScope.init();
68
+ return geoScope.isEmpty() ? undefined : geoScope;
69
+ })
70
+ .filter(Boolean);
71
+ set(() => ({
72
+ scopes: geoScopes
73
+ }));
74
+ set(({ isLoading }) => ({
75
+ isLoading: isLoading - 1
76
+ }));
77
+ },
78
+ pointsForFilters: async (filters = []) => {
79
+ const locale = configStore.getState().locale;
80
+ const {
81
+ points,
82
+ _lastFilter: lastFilter,
83
+ _lastResponse: lastResponse
84
+ } = store.getState();
85
+ const cacheKey = JSON.stringify(filters);
86
+ // cache
87
+ if (cacheKey === lastFilter) {
88
+ return lastResponse;
89
+ }
90
+
91
+ set(({ isLoading }) => ({ isLoading: isLoading + 1 }));
92
+ const ids = await getGeoDatasource(
93
+ {
94
+ variables: { filters, locale: locale }
95
+ },
96
+ false
97
+ );
98
+ if (!ids?.nodes) {
99
+ return [];
100
+ }
101
+ const filteredPoints = ids.nodes
102
+ .map(({ id: needleId, type: needleType }) => {
103
+ return points.find(({ id }) => `${needleType}::${needleId}` === id);
104
+ })
105
+ .filter(Boolean);
106
+
107
+ set(({ isLoading }) => ({
108
+ _lastFilter: cacheKey,
109
+ _lastResponse: filteredPoints,
110
+ isLoading: isLoading - 1
111
+ }));
112
+ return filteredPoints;
113
+ }
114
+ }))
115
+ );
116
+
117
+ store.subscribe(
118
+ (state) => [state.scopes],
119
+ async ([scopes], [previousScopes]) => {
120
+ if (scopes.length === previousScopes.length) return;
121
+
122
+ const { selectedScope, selectScope } = geoStore.getState();
123
+ if (!!selectedScope) return;
124
+
125
+ const { space_ids } = configStore.getState();
126
+ if (space_ids.length === 1) {
127
+ const { scopeForId } = store.getState();
128
+ const scope = scopeForId(space_ids[0]);
129
+ if (scope) selectScope(scope);
130
+ }
131
+ }
132
+ );
133
+
134
+ export default store;
@@ -0,0 +1,20 @@
1
+ import { createStore } from "zustand/vanilla";
2
+ import { subscribeWithSelector } from "zustand/middleware";
3
+ import dropdownFilterStore from "./dropdownFilterStore";
4
+ const store = createStore(
5
+ subscribeWithSelector((set) => ({
6
+ isOpen: false,
7
+ toggleOpen: () => set((state) => ({ isOpen: !state.isOpen })),
8
+ close: () => set(() => ({ isOpen: false }))
9
+ }))
10
+ );
11
+
12
+ store.subscribe(
13
+ (state) => [state.isOpen],
14
+ ([isOpen]) => {
15
+ if (isOpen) {
16
+ dropdownFilterStore.getState().close();
17
+ }
18
+ }
19
+ );
20
+ export default store;
@@ -0,0 +1,42 @@
1
+ import _ from "lodash";
2
+ import configStore from "../../models/configStore";
3
+ import createClasses from "../createClasses";
4
+ const fallback = (container, node) => {
5
+ const { i18n, images } = configStore.getState();
6
+ if (!_.isEmpty(node.bannerImage)) {
7
+ const image = L.DomUtil.create(
8
+ "img",
9
+ "decidimGeo__drawer__listCardImg decidimGeo__drawer__listCardImg--large",
10
+ container
11
+ );
12
+ image.src = node.bannerImage;
13
+ container.className += " decidimGeo__drawer__listCardInfo--image";
14
+ }
15
+
16
+ const infoType = L.DomUtil.create("div", "decidimGeo__drawer__listCardType", container);
17
+ infoType.textContent += i18n[node.type];
18
+ const notGeoEncodedIcon = L.DomUtil.create(
19
+ "img",
20
+ createClasses("decidimGeo__drawer__listCardIcon", [node.coordinates && "hidden"]),
21
+ infoType
22
+ );
23
+ notGeoEncodedIcon.src = images?.not_geolocated;
24
+
25
+ const infoTitle = L.DomUtil.create(
26
+ "div",
27
+ "decidimGeo__drawer__listCardTitle",
28
+ container
29
+ );
30
+ infoTitle.textContent += node.title.translation;
31
+ if (node.description) {
32
+ const infoDescription = L.DomUtil.create(
33
+ "div",
34
+ "decidimGeo__drawer__listCardDescription decidimGeo__drawer__listCardDescription--large",
35
+ container
36
+ );
37
+ infoDescription.textContent = _.truncate(node.description.translation, {
38
+ length: 2500
39
+ });
40
+ }
41
+ };
42
+ export default fallback;
@@ -0,0 +1,2 @@
1
+ export { default as meetings } from "./meetings";
2
+ export { default as fallback } from "./fallback";
@@ -0,0 +1,75 @@
1
+ import _ from "lodash";
2
+ import configStore from "../../models/configStore";
3
+ import createClasses from "../createClasses";
4
+ import { format, isSameDay } from "date-fns";
5
+
6
+ const meetings = (container, node) => {
7
+ const { i18n, images } = configStore.getState();
8
+ if (!_.isEmpty(node.bannerImage)) {
9
+ const image = L.DomUtil.create(
10
+ "img",
11
+ "decidimGeo__drawer__listCardImg decidimGeo__drawer__listCardImg--large",
12
+ container
13
+ );
14
+ image.src = node.bannerImage;
15
+ container.className += " decidimGeo__drawer__listCardInfo--image";
16
+ }
17
+
18
+ const infoType = L.DomUtil.create("div", "decidimGeo__drawer__listCardType", container);
19
+ infoType.textContent += i18n[node.type];
20
+ const notGeoEncodedIcon = L.DomUtil.create(
21
+ "img",
22
+ createClasses("decidimGeo__drawer__listCardIcon", [node.coordinates && "hidden"]),
23
+ infoType
24
+ );
25
+ notGeoEncodedIcon.src = images?.not_geolocated;
26
+ const infoTitle = L.DomUtil.create(
27
+ "div",
28
+ "decidimGeo__drawer__listCardTitle",
29
+ container
30
+ );
31
+ infoTitle.textContent += node.title.translation;
32
+ const infoDescription = L.DomUtil.create(
33
+ "div",
34
+ "decidimGeo__drawer__listCardDate",
35
+ container
36
+ );
37
+ const infoStart = L.DomUtil.create(
38
+ "strong",
39
+ "decidimGeo__drawer__listCardStartDate",
40
+ infoDescription
41
+ );
42
+ let displayedDate =
43
+ format(node.startTime, "dd/MM/yy") + " — " + format(node.endTime, "dd/MM/yy");
44
+ if (isSameDay(node.startTime, node.endTime)) {
45
+ displayedDate = format(node.startTime, "dd/MM/yy");
46
+ }
47
+ infoStart.textContent = displayedDate;
48
+
49
+ const infoStartSep = L.DomUtil.create(
50
+ "div",
51
+ "decidimGeo__drawer__listCardStartDateSep",
52
+ infoDescription
53
+ );
54
+ infoStartSep.textContent = "·";
55
+
56
+ const infoStartTime = L.DomUtil.create(
57
+ "div",
58
+ "decidimGeo__drawer__listCardStartTime",
59
+ infoDescription
60
+ );
61
+ infoStartTime.textContent =
62
+ format(node.startTime, "kk:mm") + "-" + format(node.endTime, "kk:mm");
63
+
64
+ if (node.description) {
65
+ const infoDescription = L.DomUtil.create(
66
+ "div",
67
+ "decidimGeo__drawer__listCardDescription decidimGeo__drawer__listCardDescription--large decidimGeo__drawer__listCardDescription--meetings",
68
+ container
69
+ );
70
+ infoDescription.textContent = _.truncate(node.description.translation, {
71
+ length: 2500
72
+ });
73
+ }
74
+ };
75
+ export default meetings;
@@ -0,0 +1,45 @@
1
+ import configStore from "../../models/configStore";
2
+ import createClasses from "../createClasses";
3
+ import { createDomElement } from "../createDomElement";
4
+ import _ from "lodash";
5
+
6
+ const fallback = (node) => {
7
+ const { i18n, images } = configStore.getState();
8
+ const hasImage = !_.isEmpty(node.bannerImage);
9
+ const listCard = createDomElement("li", "decidimGeo__drawer__listCard");
10
+
11
+ const info = createDomElement(
12
+ "div",
13
+ createClasses("decidimGeo__drawer__listCardInfo", [!hasImage && "large"]),
14
+ listCard
15
+ );
16
+
17
+ const infoType = createDomElement("div", "decidimGeo__drawer__listCardType", info);
18
+ infoType.textContent += i18n[node.type];
19
+ const notGeoEncodedIcon = createDomElement(
20
+ "img",
21
+ createClasses("decidimGeo__drawer__listCardIcon", [node.coordinates && "hidden"]),
22
+ infoType
23
+ );
24
+ notGeoEncodedIcon.src = images?.not_geolocated;
25
+
26
+ const infoTitle = createDomElement("div", "decidimGeo__drawer__listCardTitle", info);
27
+ infoTitle.textContent += node.title.translation;
28
+
29
+ if (node.shortDescription) {
30
+ const infoDescription = createDomElement(
31
+ "div",
32
+ "decidimGeo__drawer__listCardDescription",
33
+ info
34
+ );
35
+ infoDescription.textContent += node.shortDescription.translation;
36
+ }
37
+
38
+ if (hasImage) {
39
+ const image = createDomElement("img", "decidimGeo__drawer__listCardImg", listCard);
40
+ image.src = node.bannerImage;
41
+ }
42
+ return listCard;
43
+ };
44
+
45
+ export default fallback;
@@ -0,0 +1,2 @@
1
+ export { default as meetings } from "./meetings";
2
+ export { default as fallback } from "./fallback";
@@ -0,0 +1,62 @@
1
+ import configStore from "../../models/configStore";
2
+ import createClasses from "../createClasses";
3
+ import { createDomElement } from "../createDomElement";
4
+ import _ from "lodash";
5
+ import { format, isSameDay } from "date-fns";
6
+
7
+ const meetings = (node) => {
8
+ const { i18n, images } = configStore.getState();
9
+ const listCard = createDomElement("li", "decidimGeo__drawer__listCard");
10
+ const info = createDomElement(
11
+ "div",
12
+ "decidimGeo__drawer__listCardInfo decidimGeo__drawer__listCardInfo--large",
13
+ listCard
14
+ );
15
+
16
+ const infoType = createDomElement("div", "decidimGeo__drawer__listCardType", info);
17
+ infoType.textContent = i18n[node.type];
18
+ const notGeoEncodedIcon = createDomElement(
19
+ "img",
20
+ createClasses("decidimGeo__drawer__listCardIcon", [node.coordinates && "hidden"]),
21
+ infoType
22
+ );
23
+ notGeoEncodedIcon.src = images?.not_geolocated;
24
+
25
+ const infoTitle = createDomElement("div", "decidimGeo__drawer__listCardTitle", info);
26
+ infoTitle.textContent = node.title.translation;
27
+
28
+ const infoDescription = createDomElement(
29
+ "div",
30
+ "decidimGeo__drawer__listCardDate",
31
+ info
32
+ );
33
+ const infoStart = createDomElement(
34
+ "strong",
35
+ "decidimGeo__drawer__listCardStartDate",
36
+ infoDescription
37
+ );
38
+ let displayedDate =
39
+ format(node.startTime, "dd/MM/yy") + " — " + format(node.endTime, "dd/MM/yy");
40
+ if (isSameDay(node.startTime, node.endTime)) {
41
+ displayedDate = format(node.startTime, "dd/MM/yy");
42
+ }
43
+ infoStart.textContent = displayedDate;
44
+
45
+ const infoStartSep = createDomElement(
46
+ "span",
47
+ "decidimGeo__drawer__listCardStartDateSep",
48
+ infoDescription
49
+ );
50
+ infoStartSep.textContent = "·";
51
+
52
+ const infoStartTime = createDomElement(
53
+ "span",
54
+ "decidimGeo__drawer__listCardStartTime",
55
+ infoDescription
56
+ );
57
+ infoStartTime.textContent =
58
+ format(node.startTime, "kk:mm") + "-" + format(node.endTime, "kk:mm");
59
+ return listCard;
60
+ };
61
+
62
+ export default meetings;
@@ -0,0 +1,7 @@
1
+ const createClasses = (classname, modifiers = []) =>
2
+ [
3
+ classname,
4
+ ...modifiers.filter(Boolean).map((modifier) => `${classname}--${modifier}`)
5
+ ].join(" ");
6
+
7
+ export default createClasses;
@@ -0,0 +1,14 @@
1
+ const createCustomMarker = (location) => {
2
+ return new L.circleMarker(location, {
3
+ radius: 6,
4
+ fillColor: "#ffffff",
5
+ fillOpacity: 1,
6
+ color: "#404040",
7
+ opacity: 1,
8
+ weight: 5,
9
+ className: "decidimGeo__marker",
10
+ riseOnHover: true
11
+ });
12
+ };
13
+
14
+ export default createCustomMarker;
@@ -0,0 +1,2 @@
1
+ export const createDomElement = (tag, classes, container = undefined) =>
2
+ L.DomUtil.create(tag, classes, container);