spina-admin-conferences 1.3.3

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 (212) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +101 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/config/spina_admin_conferences_manifest.js +4 -0
  6. data/app/assets/javascripts/spina/admin/conferences/application.es6 +20 -0
  7. data/app/assets/javascripts/spina/admin/conferences/controllers/conference_events_form_controller.es6 +175 -0
  8. data/app/assets/javascripts/spina/admin/conferences/controllers/presentation_attachments_form_controller.es6 +175 -0
  9. data/app/assets/javascripts/spina/admin/conferences/controllers/select_options_controller.es6 +216 -0
  10. data/app/assets/stylesheets/spina/admin/conferences/application.sass +14 -0
  11. data/app/controllers/spina/admin/conferences/application_controller.rb +20 -0
  12. data/app/controllers/spina/admin/conferences/conferences_controller.rb +148 -0
  13. data/app/controllers/spina/admin/conferences/delegates_controller.rb +116 -0
  14. data/app/controllers/spina/admin/conferences/dietary_requirements_controller.rb +106 -0
  15. data/app/controllers/spina/admin/conferences/events_controller.rb +24 -0
  16. data/app/controllers/spina/admin/conferences/institutions_controller.rb +106 -0
  17. data/app/controllers/spina/admin/conferences/presentation_attachment_types_controller.rb +104 -0
  18. data/app/controllers/spina/admin/conferences/presentation_attachments_controller.rb +24 -0
  19. data/app/controllers/spina/admin/conferences/presentation_types_controller.rb +109 -0
  20. data/app/controllers/spina/admin/conferences/presentations_controller.rb +124 -0
  21. data/app/controllers/spina/admin/conferences/rooms_controller.rb +109 -0
  22. data/app/controllers/spina/admin/conferences/sessions_controller.rb +118 -0
  23. data/app/helpers/spina/admin/conferences/conferences_helper.rb +44 -0
  24. data/app/jobs/spina/admin/conferences/application_job.rb +11 -0
  25. data/app/jobs/spina/admin/conferences/delegate_import_job.rb +37 -0
  26. data/app/jobs/spina/admin/conferences/import_job.rb +26 -0
  27. data/app/jobs/spina/admin/conferences/presentation_import_job.rb +38 -0
  28. data/app/mailers/spina/admin/conferences/application_mailer.rb +13 -0
  29. data/app/models/spina/admin/conferences.rb +11 -0
  30. data/app/models/spina/admin/conferences/application_record.rb +14 -0
  31. data/app/models/spina/admin/conferences/conference.rb +152 -0
  32. data/app/models/spina/admin/conferences/date_part.rb +15 -0
  33. data/app/models/spina/admin/conferences/delegate.rb +89 -0
  34. data/app/models/spina/admin/conferences/dietary_requirement.rb +33 -0
  35. data/app/models/spina/admin/conferences/email_address_part.rb +21 -0
  36. data/app/models/spina/admin/conferences/event.rb +120 -0
  37. data/app/models/spina/admin/conferences/institution.rb +46 -0
  38. data/app/models/spina/admin/conferences/part.rb +20 -0
  39. data/app/models/spina/admin/conferences/presentation.rb +143 -0
  40. data/app/models/spina/admin/conferences/presentation_attachment.rb +27 -0
  41. data/app/models/spina/admin/conferences/presentation_attachment_type.rb +35 -0
  42. data/app/models/spina/admin/conferences/presentation_type.rb +60 -0
  43. data/app/models/spina/admin/conferences/room.rb +52 -0
  44. data/app/models/spina/admin/conferences/session.rb +56 -0
  45. data/app/models/spina/admin/conferences/time_part.rb +53 -0
  46. data/app/models/spina/admin/conferences/url_part.rb +21 -0
  47. data/app/validators/spina/admin/conferences/conference_date_validator.rb +20 -0
  48. data/app/validators/spina/admin/conferences/email_address_validator.rb +28 -0
  49. data/app/validators/spina/admin/conferences/finish_date_validator.rb +20 -0
  50. data/app/validators/spina/admin/conferences/finish_time_validator.rb +20 -0
  51. data/app/validators/spina/admin/conferences/http_url_validator.rb +28 -0
  52. data/app/views/layouts/spina/admin/conferences/conferences.html.haml +19 -0
  53. data/app/views/layouts/spina/admin/conferences/delegates.html.haml +10 -0
  54. data/app/views/layouts/spina/admin/conferences/dietary_requirements.html.haml +10 -0
  55. data/app/views/layouts/spina/admin/conferences/institutions.html.haml +17 -0
  56. data/app/views/layouts/spina/admin/conferences/presentation_attachment_types.html.haml +10 -0
  57. data/app/views/layouts/spina/admin/conferences/presentations.html.haml +10 -0
  58. data/app/views/spina/admin/conferences/application/_empty_list.html.haml +3 -0
  59. data/app/views/spina/admin/conferences/application/_errors.html.haml +11 -0
  60. data/app/views/spina/admin/conferences/application/_errors.js.erb +2 -0
  61. data/app/views/spina/admin/conferences/conferences/_conference.html.haml +9 -0
  62. data/app/views/spina/admin/conferences/conferences/_event_fields.html.haml +39 -0
  63. data/app/views/spina/admin/conferences/conferences/_event_row.html.haml +5 -0
  64. data/app/views/spina/admin/conferences/conferences/_form.html.haml +23 -0
  65. data/app/views/spina/admin/conferences/conferences/_form_conference_details.html.haml +29 -0
  66. data/app/views/spina/admin/conferences/conferences/_form_delegates.html.haml +12 -0
  67. data/app/views/spina/admin/conferences/conferences/_form_parts.html.haml +15 -0
  68. data/app/views/spina/admin/conferences/conferences/_form_presentation_types.html.haml +12 -0
  69. data/app/views/spina/admin/conferences/conferences/_form_presentations.html.haml +12 -0
  70. data/app/views/spina/admin/conferences/conferences/_form_rooms.html.haml +12 -0
  71. data/app/views/spina/admin/conferences/conferences/_form_structure.html.haml +19 -0
  72. data/app/views/spina/admin/conferences/conferences/_form_structure_item.html.haml +13 -0
  73. data/app/views/spina/admin/conferences/conferences/edit.html.haml +1 -0
  74. data/app/views/spina/admin/conferences/conferences/index.html.haml +18 -0
  75. data/app/views/spina/admin/conferences/conferences/new.html.haml +1 -0
  76. data/app/views/spina/admin/conferences/delegates/_delegate.html.haml +13 -0
  77. data/app/views/spina/admin/conferences/delegates/_form.html.haml +23 -0
  78. data/app/views/spina/admin/conferences/delegates/_form_conferences.html.haml +14 -0
  79. data/app/views/spina/admin/conferences/delegates/_form_delegate_details.html.haml +38 -0
  80. data/app/views/spina/admin/conferences/delegates/_form_presentations.html.haml +12 -0
  81. data/app/views/spina/admin/conferences/delegates/edit.html.haml +1 -0
  82. data/app/views/spina/admin/conferences/delegates/index.html.haml +23 -0
  83. data/app/views/spina/admin/conferences/delegates/new.html.haml +1 -0
  84. data/app/views/spina/admin/conferences/dietary_requirements/_dietary_requirement.html.haml +6 -0
  85. data/app/views/spina/admin/conferences/dietary_requirements/_form.html.haml +23 -0
  86. data/app/views/spina/admin/conferences/dietary_requirements/_form_delegates.html.haml +12 -0
  87. data/app/views/spina/admin/conferences/dietary_requirements/_form_dietary_requirement_details.html.haml +9 -0
  88. data/app/views/spina/admin/conferences/dietary_requirements/edit.html.haml +1 -0
  89. data/app/views/spina/admin/conferences/dietary_requirements/index.html.haml +15 -0
  90. data/app/views/spina/admin/conferences/dietary_requirements/new.html.haml +1 -0
  91. data/app/views/spina/admin/conferences/events/new.js.erb +27 -0
  92. data/app/views/spina/admin/conferences/institutions/_form.html.haml +23 -0
  93. data/app/views/spina/admin/conferences/institutions/_form_delegates.html.haml +12 -0
  94. data/app/views/spina/admin/conferences/institutions/_form_institution_details.html.haml +35 -0
  95. data/app/views/spina/admin/conferences/institutions/_form_rooms.html.haml +12 -0
  96. data/app/views/spina/admin/conferences/institutions/_institution.html.haml +7 -0
  97. data/app/views/spina/admin/conferences/institutions/edit.html.haml +1 -0
  98. data/app/views/spina/admin/conferences/institutions/index.html.haml +16 -0
  99. data/app/views/spina/admin/conferences/institutions/new.html.haml +1 -0
  100. data/app/views/spina/admin/conferences/presentation_attachment_types/_form.html.haml +23 -0
  101. data/app/views/spina/admin/conferences/presentation_attachment_types/_presentation_attachment_type.html.haml +6 -0
  102. data/app/views/spina/admin/conferences/presentation_attachment_types/edit.html.haml +1 -0
  103. data/app/views/spina/admin/conferences/presentation_attachment_types/index.html.haml +15 -0
  104. data/app/views/spina/admin/conferences/presentation_attachment_types/new.html.haml +1 -0
  105. data/app/views/spina/admin/conferences/presentation_attachments/new.js.erb +21 -0
  106. data/app/views/spina/admin/conferences/presentation_types/_form.html.haml +23 -0
  107. data/app/views/spina/admin/conferences/presentation_types/_form_presentation_type_details.html.haml +20 -0
  108. data/app/views/spina/admin/conferences/presentation_types/_form_presentations.html.haml +12 -0
  109. data/app/views/spina/admin/conferences/presentation_types/_form_sessions.html.haml +12 -0
  110. data/app/views/spina/admin/conferences/presentation_types/_presentation_type.html.haml +8 -0
  111. data/app/views/spina/admin/conferences/presentation_types/edit.html.haml +1 -0
  112. data/app/views/spina/admin/conferences/presentation_types/index.html.haml +17 -0
  113. data/app/views/spina/admin/conferences/presentation_types/new.html.haml +1 -0
  114. data/app/views/spina/admin/conferences/presentations/_attachment_fields.html.haml +27 -0
  115. data/app/views/spina/admin/conferences/presentations/_attachment_row.html.haml +2 -0
  116. data/app/views/spina/admin/conferences/presentations/_form.html.haml +23 -0
  117. data/app/views/spina/admin/conferences/presentations/_form_presentation_details.html.haml +55 -0
  118. data/app/views/spina/admin/conferences/presentations/_form_presenters.html.haml +12 -0
  119. data/app/views/spina/admin/conferences/presentations/_presentation.html.haml +11 -0
  120. data/app/views/spina/admin/conferences/presentations/edit.html.haml +1 -0
  121. data/app/views/spina/admin/conferences/presentations/index.html.haml +23 -0
  122. data/app/views/spina/admin/conferences/presentations/new.html.haml +1 -0
  123. data/app/views/spina/admin/conferences/rooms/_form.html.haml +23 -0
  124. data/app/views/spina/admin/conferences/rooms/_form_presentations.html.haml +12 -0
  125. data/app/views/spina/admin/conferences/rooms/_form_room_details.html.haml +18 -0
  126. data/app/views/spina/admin/conferences/rooms/_room.html.haml +8 -0
  127. data/app/views/spina/admin/conferences/rooms/edit.html.haml +1 -0
  128. data/app/views/spina/admin/conferences/rooms/index.html.haml +17 -0
  129. data/app/views/spina/admin/conferences/rooms/new.html.haml +1 -0
  130. data/app/views/spina/admin/conferences/sessions/_form.html.haml +23 -0
  131. data/app/views/spina/admin/conferences/sessions/_form_presentations.html.haml +12 -0
  132. data/app/views/spina/admin/conferences/sessions/_form_session_details.html.haml +23 -0
  133. data/app/views/spina/admin/conferences/sessions/_session.html.haml +8 -0
  134. data/app/views/spina/admin/conferences/sessions/edit.html.haml +1 -0
  135. data/app/views/spina/admin/conferences/sessions/index.html.haml +17 -0
  136. data/app/views/spina/admin/conferences/sessions/new.html.haml +1 -0
  137. data/app/views/spina/admin/hooks/conferences/_head.html.haml +2 -0
  138. data/app/views/spina/admin/hooks/conferences/_primary_navigation.html.haml +20 -0
  139. data/app/views/spina/admin/hooks/conferences/_settings_secondary_navigation.html.haml +4 -0
  140. data/app/views/spina/admin/partables/admin/conferences/date_parts/_form.html.haml +5 -0
  141. data/app/views/spina/admin/partables/admin/conferences/email_address_parts/_form.html.haml +5 -0
  142. data/app/views/spina/admin/partables/admin/conferences/time_parts/_form.html.haml +7 -0
  143. data/app/views/spina/admin/partables/admin/conferences/url_parts/_form.html.haml +5 -0
  144. data/config/initializers/assets.rb +13 -0
  145. data/config/initializers/types.rb +13 -0
  146. data/config/locales/en.yml +354 -0
  147. data/config/routes.rb +27 -0
  148. data/db/migrate/20180907141228_create_spina_dates.rb +11 -0
  149. data/db/migrate/20180907141229_create_spina_email_addresses.rb +11 -0
  150. data/db/migrate/20180907141230_create_spina_urls.rb +11 -0
  151. data/db/migrate/20180907141231_create_spina_conferences_institutions.rb +12 -0
  152. data/db/migrate/20180907141232_create_spina_conferences_delegates.rb +15 -0
  153. data/db/migrate/20180907141234_create_spina_conferences_dietary_requirements.rb +11 -0
  154. data/db/migrate/20180907141235_create_spina_conferences_conferences.rb +12 -0
  155. data/db/migrate/20180907141236_create_spina_conferences_presentation_types.rb +13 -0
  156. data/db/migrate/20180907141238_create_join_table_spina_conferences_conference_delegate.rb +7 -0
  157. data/db/migrate/20180907141239_create_join_table_spina_conferences_delegate_presentation.rb +7 -0
  158. data/db/migrate/20180907141240_create_join_table_spina_conferences_delegate_dietary_requirement.rb +7 -0
  159. data/db/migrate/20180907141242_add_type_to_spina_pages.rb +7 -0
  160. data/db/migrate/20180907141243_create_spina_conferences_conference_page_parts.rb +14 -0
  161. data/db/migrate/20180914121905_add_logo_ref_to_spina_conferences_institutions.rb +7 -0
  162. data/db/migrate/20180916135431_create_spina_conferences_rooms.rb +13 -0
  163. data/db/migrate/20180916135432_create_spina_conferences_room_possessions.rb +10 -0
  164. data/db/migrate/20180916135433_create_spina_conferences_room_uses.rb +10 -0
  165. data/db/migrate/20180916135434_create_spina_conferences_presentations.rb +15 -0
  166. data/db/migrate/20181009122503_create_spina_times.rb +11 -0
  167. data/db/migrate/20181009130631_rename_spina_page_parts.rb +10 -0
  168. data/db/migrate/20181012190811_rename_spina_conferences_page_parts.rb +10 -0
  169. data/db/migrate/20181012213049_change_start_time_in_spina_conferences_conferences.rb +15 -0
  170. data/db/migrate/20181012214813_rename_start_time_in_spina_conferences_conferences.rb +7 -0
  171. data/db/migrate/20181017155705_add_dependent_option_to_foreign_keys.rb +36 -0
  172. data/db/migrate/20190408131354_change_spina_resources.rb +56 -0
  173. data/db/migrate/20190622131423_create_spina_conferences_parts.rb +12 -0
  174. data/db/migrate/20190701174807_remove_spina_conference_pages.rb +199 -0
  175. data/db/migrate/20190704135524_add_constraints_to_columns.rb +24 -0
  176. data/db/migrate/20200126034441_create_spina_conferences_presentation_attachment_types.rb +11 -0
  177. data/db/migrate/20200126213718_create_spina_conferences_presentation_attachments.rb +19 -0
  178. data/db/migrate/20200420104603_create_spina_conferences_dietary_requirement_name_translations.rb +19 -0
  179. data/db/migrate/20200420104740_create_spina_conferences_institution_name_and_city_translations.rb +20 -0
  180. data/db/migrate/20200420105057_create_spina_conferences_presentation_title_and_abstract_translations.rb +20 -0
  181. data/db/migrate/20200420105144_create_spina_conferences_presentation_attachment_type_name_translations.rb +20 -0
  182. data/db/migrate/20200420105201_create_spina_conferences_presentation_type_name_translations.rb +19 -0
  183. data/db/migrate/20200420105458_create_spina_conferences_room_building_and_number_translations.rb +20 -0
  184. data/db/migrate/20200420110407_move_attributes_to_translation_tables.rb +161 -0
  185. data/db/migrate/20200420120706_remove_name_from_spina_conferences_dietary_requirements.rb +7 -0
  186. data/db/migrate/20200420120759_remove_name_and_city_from_spina_conferences_institutions.rb +8 -0
  187. data/db/migrate/20200420120946_remove_title_and_abstract_from_spina_conferences_presentations.rb +8 -0
  188. data/db/migrate/20200420121310_remove_name_from_spina_conferences_presentation_attachment_types.rb +7 -0
  189. data/db/migrate/20200420121321_remove_name_from_spina_conferences_presentation_types.rb +7 -0
  190. data/db/migrate/20200420121443_remove_building_and_number_from_spina_conferences_rooms.rb +8 -0
  191. data/db/migrate/20200502133408_create_spina_conferences_conference_name_translations.rb +19 -0
  192. data/db/migrate/20200502183409_set_name_for_spina_conferences_conferences.rb +47 -0
  193. data/db/migrate/20200502183410_remove_institution_from_spina_conferences_conferences.rb +8 -0
  194. data/db/migrate/20200502183719_remove_spina_conferences_room_possessions.rb +60 -0
  195. data/db/migrate/20200503230732_rename_spina_conferences_room_uses.rb +8 -0
  196. data/db/migrate/20200510125131_create_spina_conferences_session_name_translations.rb +19 -0
  197. data/db/migrate/20200802165242_create_spina_conferences_events.rb +13 -0
  198. data/db/migrate/20200802184921_create_spina_conferences_event_name_description_and_location_translations.rb +21 -0
  199. data/db/migrate/20200911161632_update_spina_conferences_parts.rb +23 -0
  200. data/db/migrate/20200911161651_update_spina_parts.rb +37 -0
  201. data/db/migrate/20200911161726_move_presentation_parts.rb +101 -0
  202. data/db/migrate/20200911161739_move_conference_parts.rb +219 -0
  203. data/db/migrate/20201002122517_remove_spina_conferences_conference_page_parts.rb +14 -0
  204. data/db/migrate/20201007125625_add_timestamps_to_spina_conferences_parts.rb +9 -0
  205. data/lib/spina/admin/conferences.rb +20 -0
  206. data/lib/spina/admin/conferences/engine.rb +26 -0
  207. data/lib/spina/admin/conferences/migration/renaming.rb +81 -0
  208. data/lib/spina/admin/conferences/railtie.rb +34 -0
  209. data/lib/spina/admin/conferences/types/interval_type.rb +29 -0
  210. data/lib/spina/admin/conferences/version.rb +10 -0
  211. data/lib/tasks/spina/admin/conferences_tasks.rake +4 -0
  212. metadata +561 -0
@@ -0,0 +1,216 @@
1
+ /* global Stimulus */
2
+
3
+ /**
4
+ * @external Stimulus
5
+ * @see {@link https://stimulusjs.org}
6
+ */
7
+
8
+ /**
9
+ * @classdesc Controller that links related select elements.
10
+ */
11
+ class SelectOptionsController extends Stimulus.Controller { // eslint-disable-line no-unused-vars
12
+ // noinspection JSUnusedGlobalSymbols
13
+ static get targets() {
14
+ return [
15
+ /**
16
+ * @private
17
+ * @property {HTMLElement[]} selectTargets - The select elements.
18
+ */
19
+ 'select'
20
+ ]
21
+ }
22
+ // noinspection JSUnusedGlobalSymbols
23
+ static get values() {
24
+ return {
25
+ /**
26
+ * @private
27
+ * @property {Array} recordValues - The record values.
28
+ */
29
+ record: Array,
30
+ }
31
+ }
32
+
33
+ // noinspection JSUnusedGlobalSymbols
34
+ /**
35
+ * Hook to ensure that the initial value of each select element is used to set visibility of the corresponding records.
36
+ * @private
37
+ */
38
+ connect() {
39
+ this.selectTargets.forEach(target => target.dispatchEvent(new Event('change')))
40
+ }
41
+
42
+ // noinspection JSUnusedGlobalSymbols
43
+ /**
44
+ * Hook to update select targets when record values change.
45
+ * @private
46
+ */
47
+ recordValuesChanged() {
48
+ this.selectTargets.forEach(target => {
49
+ if (!(target instanceof HTMLSelectElement)) return
50
+ this.updateOptions(target)
51
+ })
52
+ }
53
+
54
+ // noinspection JSUnusedGlobalSymbols
55
+ /**
56
+ * Updates the visibility of records according to the selected option of a select element.
57
+ * @param {Event} event - The event fired by the select element.
58
+ */
59
+ setVisibility(event) {
60
+ const target = event.currentTarget
61
+ if (!(target instanceof HTMLSelectElement)) return
62
+ const recordFilter = new SelectOptionsController.RecordFilter(this.recordValues, target)
63
+ this.recordValues = recordFilter.setVisibility()
64
+ }
65
+
66
+ /**
67
+ * Updates the options of a select element.
68
+ * @private
69
+ * @param {HTMLSelectElement} target - The select element to update the options of.
70
+ */
71
+ updateOptions(target) {
72
+ let newRecords = this.recordValues
73
+ if ('keyPath' in target.dataset) {
74
+ const keyPath = new SelectOptionsController.KeyPath(target.dataset.keyPath)
75
+ newRecords = keyPath.visibleObjectsAtKeyPath(newRecords)
76
+ }
77
+ const textKey = target.dataset.textKey
78
+ if (!('valueKey' in target.dataset)) target.dataset.valueKey = 'id'
79
+ const valueKey = target.dataset.valueKey
80
+ const newOptions = newRecords.map(record => new Option(record[textKey], record[valueKey] ))
81
+ const oldOptions = Array.from(target.options).filter(option => option.value !== '')
82
+ oldOptions.filter(option => !newOptions.some(newOption => newOption.value === option.value))
83
+ .forEach(option => target.remove(option.index))
84
+ newOptions.filter(newOption => !oldOptions.some(option => option.value === newOption.value)).forEach(option => target.add(option))
85
+ }
86
+ }
87
+
88
+ /**
89
+ * @private
90
+ * @classdesc A series of property keys, used to traverse objects. A parent object or array of objects is recursively mapped to child
91
+ * objects identified by each property key.
92
+ */
93
+ SelectOptionsController.KeyPath = class {
94
+ /**
95
+ * Creates a new key path.
96
+ * @private
97
+ * @constructor
98
+ * @param {String} string - The string used to create the key path. Should be in the format 'segment:segment:segment'.
99
+ */
100
+ constructor(string) {
101
+ if (typeof string !== 'string') throw TypeError(`${string} is not a string`)
102
+ /**
103
+ * @property {string[]} segments - The segments of the key path.
104
+ */
105
+ this.segments = string.split(':')
106
+ }
107
+
108
+ /**
109
+ * Traverses an object or array of objects by recursively mapping objects to the objects identified by the series of key path segments.
110
+ * @private
111
+ * @param {Object|Object[]} object - The object or array of objects to be traversed.
112
+ * @param {Boolean} filter - Whether, when encountering an array within the top-level object or array,
113
+ * elements marked as invisible should be ignored.
114
+ * @return Any
115
+ */
116
+ traverseObject(object, filter = false) {
117
+ let childObject = object
118
+ this.segments.forEach(key => {
119
+ switch (childObject.constructor) {
120
+ case Array:
121
+ if (filter) childObject = childObject.filter(object => object.hidden !== true)
122
+ childObject = childObject.flatMap(object => object[key])
123
+ break
124
+ case String:
125
+ case Number:
126
+ case Boolean:
127
+ break
128
+ case Object:
129
+ childObject = childObject[key]
130
+ break
131
+ }
132
+ })
133
+ return childObject ? childObject : []
134
+ }
135
+
136
+ /**
137
+ * Traverses an object or array of objects by recursively mapping objects to the objects identified by the series of key path segments.
138
+ * When encountering an array within the top-level object or array, elements marked as invisible are ignored.
139
+ * @private
140
+ * @param {Object|Object[]} object - The object or array of objects to be traversed.
141
+ * @return Any
142
+ */
143
+ visibleObjectsAtKeyPath(object) {
144
+ return this.traverseObject(object, true)
145
+ }
146
+
147
+ /**
148
+ * Traverses an object or array of objects by recursively mapping objects to the objects identified by the series of key path segments.
149
+ * @private
150
+ * @param {Object|Object[]} object - The object or array of objects to be traversed.
151
+ * @return Any
152
+ */
153
+ objectsAtKeyPath(object) {
154
+ return this.traverseObject(object)
155
+ }
156
+ }
157
+
158
+ /**
159
+ * @private
160
+ * @classdesc Identifies a set of records presented as options by a select element target within a possibly larger set of records.
161
+ * The visibility of each target record can be set by comparing the value of the target to the value of some property of each target record.
162
+ */
163
+ SelectOptionsController.RecordFilter = class {
164
+ /**
165
+ * @private
166
+ * @property {Object[]} targetRecords - If a key path exists, the records within the raw records identified by the key path.
167
+ * Else the raw records.
168
+ */
169
+ get targetRecords() {
170
+ return this.keyPath ? this.keyPath.objectsAtKeyPath(this.rawRecords) : this.rawRecords
171
+ }
172
+
173
+ /**
174
+ * Creates a new record filter.
175
+ * @private
176
+ * @constructor
177
+ * @param {Object[]} rawRecords - The records to be filtered.
178
+ * @param {HTMLSelectElement} target - The target providing the value.
179
+ * @param {String} [target.dataset.keyPath] - The key path.
180
+ * @param {String} [target.dataset.valueKey=id] - The value key.
181
+ */
182
+ constructor(rawRecords, target) {
183
+ /**
184
+ * @private
185
+ * @property {Object[]} rawRecords - The entire set of records.
186
+ */
187
+ this.rawRecords = Array.from(rawRecords)
188
+ if ('keyPath' in target.dataset) {
189
+ /**
190
+ * @private
191
+ * @property {KeyPath} [keyPath] - The key path used to filter records.
192
+ */
193
+ this.keyPath = new SelectOptionsController.KeyPath(target.dataset.keyPath)
194
+ }
195
+ if (!('valueKey' in target.dataset)) target.dataset.valueKey = 'id'
196
+ /**
197
+ * @private
198
+ * @property {String} valueKey - The property of the target records to be compared to the target value.
199
+ */
200
+ this.valueKey = target.dataset.valueKey
201
+ /**
202
+ * @private
203
+ * @property {String} targetValue - The value of the target.
204
+ */
205
+ this.targetValue = target.value
206
+ }
207
+
208
+ /**
209
+ * Marks the target record whose property value matches the value of the target as visible and others as invisible.
210
+ * @returns {Object[]} The raw records, with the target records marked for visibility.
211
+ */
212
+ setVisibility() {
213
+ this.targetRecords.forEach(record => record.hidden = record[this.valueKey].toString() !== this.targetValue)
214
+ return this.rawRecords
215
+ }
216
+ }
@@ -0,0 +1,14 @@
1
+ input::-webkit-calendar-picker-indicator
2
+ display: none
3
+ input[type="date"]::-webkit-outer-spin-button, input[type="date"]::-webkit-inner-spin-button, input[type="time"]::-webkit-inner-spin-button, input[type="time"]::-webkit-inner-spin-button
4
+ display: none
5
+ input[type="date"]
6
+ padding-right: 30px
7
+
8
+ .multiple.select-dropdown
9
+ z-index: 0
10
+ &:before
11
+ content: none
12
+ select
13
+ padding: 11px 10px
14
+ width: 100%
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Conferences
6
+ # @abstract Subclass to implement a custom controller.
7
+ class ApplicationController < ::Spina::Admin::AdminController
8
+ add_flash_types :success
9
+
10
+ layout :admin_layout, only: %i[new edit]
11
+
12
+ private
13
+
14
+ def admin_layout
15
+ 'spina/admin/admin'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Conferences
6
+ # Controller for {Conference} objects.
7
+ # @see Conference
8
+ class ConferencesController < ApplicationController
9
+ PARTS = [
10
+ { name: 'text', title: 'Text', partable_type: 'Spina::Text' },
11
+ { name: 'submission_url', title: 'Submission URL', partable_type: 'Spina::Admin::Conferences::UrlPart' },
12
+ { name: 'submission_email_address', title: 'Submission email address',
13
+ partable_type: 'Spina::Admin::Conferences::EmailAddressPart' },
14
+ { name: 'submission_date', title: 'Submission date', partable_type: 'Spina::Admin::Conferences::DatePart' },
15
+ { name: 'submission_text', title: 'Submission text', partable_type: 'Spina::Line' },
16
+ { name: 'gallery', title: 'Gallery', partable_type: 'Spina::ImageCollection' },
17
+ { name: 'sponsors', title: 'Sponsors', partable_type: 'Spina::Structure' }
18
+ ].freeze
19
+
20
+ before_action :set_conference, only: %i[edit update destroy]
21
+ before_action :set_conferences_breadcrumb
22
+ before_action :set_tabs
23
+ before_action :set_institutions, :set_parts_attributes, only: %i[new edit]
24
+ before_action :build_parts, only: :edit
25
+
26
+ # @!group Actions
27
+
28
+ # Renders a list of conferences.
29
+ # @return [void]
30
+ def index
31
+ @conferences = Conference.sorted
32
+ end
33
+
34
+ # Renders a form for a new conference.
35
+ # @return [void]
36
+ def new
37
+ @conference = Conference.new
38
+ build_parts
39
+ add_breadcrumb t('.new')
40
+ end
41
+
42
+ # Renders a form for an existing conference.
43
+ # @return [void]
44
+ def edit
45
+ add_breadcrumb @conference.name
46
+ end
47
+
48
+ # Creates a conference.
49
+ # @return [void]
50
+ def create # rubocop:disable Metrics/MethodLength
51
+ @conference = Conference.new(conference_params)
52
+
53
+ if @conference.save
54
+ redirect_to admin_conferences_conferences_path, success: t('.saved')
55
+ else
56
+ respond_to do |format|
57
+ format.html do
58
+ add_breadcrumb t('.new')
59
+ render :new
60
+ end
61
+ format.js { render partial: 'errors', locals: { errors: @conference.errors } }
62
+ end
63
+ end
64
+ end
65
+
66
+ # Updates a conference.
67
+ # @return [void]
68
+ def update # rubocop:disable Metrics/MethodLength
69
+ if @conference.update(conference_params)
70
+ redirect_to admin_conferences_conferences_path, success: t('.saved')
71
+ else
72
+ respond_to do |format|
73
+ format.html do
74
+ add_breadcrumb @conference.name
75
+ render :edit
76
+ end
77
+ format.js { render partial: 'errors', locals: { errors: @conference.errors } }
78
+ end
79
+ end
80
+ end
81
+
82
+ # Destroys a conference.
83
+ # @return [void]
84
+ def destroy # rubocop:disable Metrics/MethodLength
85
+ if @conference.destroy
86
+ redirect_to admin_conferences_conferences_path, success: t('.destroyed')
87
+ else
88
+ respond_to do |format|
89
+ format.html do
90
+ add_breadcrumb @conference.name
91
+ render :edit
92
+ end
93
+ format.js { render partial: 'errors', locals: { errors: @conference.errors } }
94
+ end
95
+ end
96
+ end
97
+
98
+ # @!endgroup
99
+
100
+ private
101
+
102
+ def set_conference
103
+ @conference = Conference.find params[:id]
104
+ end
105
+
106
+ def set_institutions
107
+ @institutions = Institution.all.to_json include: { rooms: { methods: [:name] } }
108
+ end
109
+
110
+ def set_conferences_breadcrumb
111
+ add_breadcrumb Conference.model_name.human(count: 0), admin_conferences_conferences_path
112
+ end
113
+
114
+ def set_tabs
115
+ @tabs = %w[conference_details parts delegates presentation_types rooms presentations]
116
+ end
117
+
118
+ def set_parts_attributes
119
+ @parts_attributes = PARTS
120
+ end
121
+
122
+ def build_parts
123
+ return unless @parts_attributes.is_a? Array
124
+
125
+ @conference.parts = @parts_attributes.collect do |part_attributes|
126
+ @conference.parts.where(name: part_attributes[:name]).first_or_initialize(**part_attributes)
127
+ .tap { |part| part.partable ||= part.partable_type.constantize.new }
128
+ end
129
+ end
130
+
131
+ def conference_params # rubocop:disable Metrics/MethodLength
132
+ params.require(:admin_conferences_conference).permit(:start_date, :finish_date, :name,
133
+ events_attributes:
134
+ %i[id name date start_time finish_time description location],
135
+ parts_attributes:
136
+ [:id, :title, :name, :partable_type, :partable_id,
137
+ { partable_attributes:
138
+ [:id, :content, :image_tokens, :image_positions, :date, :time,
139
+ { structure_items_attributes:
140
+ [:id, :position, :_destroy,
141
+ { structure_parts_attributes:
142
+ [:id, :title, :structure_partable_type, :name, :partable_id,
143
+ { partable_attributes: {} }] }] }] }])
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spina
4
+ module Admin
5
+ module Conferences
6
+ # Controller for {Delegate} objects.
7
+ # @see Delegate
8
+ class DelegatesController < ApplicationController
9
+ before_action :set_delegate, only: %i[edit update destroy]
10
+ before_action :set_breadcrumb
11
+ before_action :set_tabs
12
+
13
+ # @!group Actions
14
+
15
+ # Renders a list of delegates.
16
+ # @return [void]
17
+ def index
18
+ @delegates = Delegate.sorted
19
+ end
20
+
21
+ # Renders a form for a new delegate.
22
+ # @return [void]
23
+ def new
24
+ @delegate = Delegate.new
25
+ add_breadcrumb t('.new')
26
+ end
27
+
28
+ # Renders a form for an existing delegate.
29
+ # @return [void]
30
+ def edit
31
+ add_breadcrumb @delegate.full_name
32
+ end
33
+
34
+ # Creates a delegate.
35
+ # @return [void]
36
+ def create # rubocop:disable Metrics/MethodLength
37
+ @delegate = Delegate.new(delegate_params)
38
+
39
+ if @delegate.save
40
+ redirect_to admin_conferences_delegates_path, success: t('.saved')
41
+ else
42
+ respond_to do |format|
43
+ format.html do
44
+ add_breadcrumb t('.new')
45
+ render :new
46
+ end
47
+ format.js { render partial: 'errors', locals: { errors: @delegate.errors } }
48
+ end
49
+ end
50
+ end
51
+
52
+ # Updates a delegate.
53
+ # @return [void]
54
+ def update # rubocop:disable Metrics/MethodLength
55
+ if @delegate.update(delegate_params)
56
+ redirect_to admin_conferences_delegates_path, success: t('.saved')
57
+ else
58
+ respond_to do |format|
59
+ format.html do
60
+ add_breadcrumb @delegate.full_name
61
+ render :edit
62
+ end
63
+ format.js { render partial: 'errors', locals: { errors: @delegate.errors } }
64
+ end
65
+ end
66
+ end
67
+
68
+ # Destroys a delegate.
69
+ # @return [void]
70
+ def destroy # rubocop:disable Metrics/MethodLength
71
+ if @delegate.destroy
72
+ redirect_to admin_conferences_delegates_path, success: t('.destroyed')
73
+ else
74
+ respond_to do |format|
75
+ format.html do
76
+ add_breadcrumb @delegate.full_name
77
+ render :edit
78
+ end
79
+ format.js { render partial: 'errors', locals: { errors: @delegate.errors } }
80
+ end
81
+ end
82
+ end
83
+
84
+ # Imports a delegate.
85
+ # @return [void]
86
+ # @see Delegate#import
87
+ def import
88
+ Delegate.import params[:file]
89
+ end
90
+
91
+ # @!endgroup
92
+
93
+ private
94
+
95
+ def set_delegate
96
+ @delegate = Delegate.find params[:id]
97
+ end
98
+
99
+ def set_breadcrumb
100
+ add_breadcrumb Delegate.model_name.human(count: 0), admin_conferences_delegates_path
101
+ end
102
+
103
+ def set_tabs
104
+ @tabs = %w[delegate_details conferences presentations]
105
+ end
106
+
107
+ def delegate_params
108
+ params.require(:admin_conferences_delegate).permit(:first_name, :last_name, :email_address, :url,
109
+ :institution_id,
110
+ conference_ids: [],
111
+ dietary_requirement_ids: [])
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end