flexi_admin 0.0.5

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 (127) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +42 -0
  3. data/Rakefile +7 -0
  4. data/lib/flexi_admin/components/actions/checkbox_component.html.slim +8 -0
  5. data/lib/flexi_admin/components/actions/checkbox_component.rb +13 -0
  6. data/lib/flexi_admin/components/actions/select_component.html.slim +11 -0
  7. data/lib/flexi_admin/components/actions/select_component.rb +17 -0
  8. data/lib/flexi_admin/components/base_component.rb +19 -0
  9. data/lib/flexi_admin/components/form/field_component.rb +11 -0
  10. data/lib/flexi_admin/components/form/label_component.html.slim +2 -0
  11. data/lib/flexi_admin/components/form/label_component.rb +13 -0
  12. data/lib/flexi_admin/components/form/rows_component.rb +9 -0
  13. data/lib/flexi_admin/components/form/text_input_component.html.slim +5 -0
  14. data/lib/flexi_admin/components/form/text_input_component.rb +17 -0
  15. data/lib/flexi_admin/components/helpers/action_button_helper.rb +28 -0
  16. data/lib/flexi_admin/components/helpers/action_helper.rb +12 -0
  17. data/lib/flexi_admin/components/helpers/icon_helper.rb +11 -0
  18. data/lib/flexi_admin/components/helpers/link_helper.rb +8 -0
  19. data/lib/flexi_admin/components/helpers/resource_helper.rb +71 -0
  20. data/lib/flexi_admin/components/helpers/selectable.rb +11 -0
  21. data/lib/flexi_admin/components/helpers/url_helper.rb +8 -0
  22. data/lib/flexi_admin/components/helpers/value_formatter.rb +32 -0
  23. data/lib/flexi_admin/components/nav/floating_toc_component.html.slim +2 -0
  24. data/lib/flexi_admin/components/nav/floating_toc_component.rb +6 -0
  25. data/lib/flexi_admin/components/resource/autocomplete_component.html.slim +30 -0
  26. data/lib/flexi_admin/components/resource/autocomplete_component.rb +83 -0
  27. data/lib/flexi_admin/components/resource/button_select_component.html.slim +13 -0
  28. data/lib/flexi_admin/components/resource/button_select_component.rb +19 -0
  29. data/lib/flexi_admin/components/resource/form_component.rb +18 -0
  30. data/lib/flexi_admin/components/resource/form_element_component.html.slim +3 -0
  31. data/lib/flexi_admin/components/resource/form_element_component.rb +31 -0
  32. data/lib/flexi_admin/components/resource/form_mixin.rb +287 -0
  33. data/lib/flexi_admin/components/resource/link_action_component.html.slim +8 -0
  34. data/lib/flexi_admin/components/resource/link_action_component.rb +16 -0
  35. data/lib/flexi_admin/components/resource/show_page_component.rb +21 -0
  36. data/lib/flexi_admin/components/resource/view_component.html.slim +26 -0
  37. data/lib/flexi_admin/components/resource/view_component.rb +22 -0
  38. data/lib/flexi_admin/components/resources/bulk_action/button_component.html.slim +7 -0
  39. data/lib/flexi_admin/components/resources/bulk_action/button_component.rb +29 -0
  40. data/lib/flexi_admin/components/resources/bulk_action/modal_component.html.slim +32 -0
  41. data/lib/flexi_admin/components/resources/bulk_action/modal_component.rb +54 -0
  42. data/lib/flexi_admin/components/resources/grid_view/card_component.html.slim +18 -0
  43. data/lib/flexi_admin/components/resources/grid_view/card_component.rb +65 -0
  44. data/lib/flexi_admin/components/resources/grid_view/grid_component.html.slim +10 -0
  45. data/lib/flexi_admin/components/resources/grid_view/grid_component.rb +16 -0
  46. data/lib/flexi_admin/components/resources/grid_view_component.rb +89 -0
  47. data/lib/flexi_admin/components/resources/index_page_component.html.slim +13 -0
  48. data/lib/flexi_admin/components/resources/index_page_component.rb +19 -0
  49. data/lib/flexi_admin/components/resources/list_view/cell_component.html.slim +2 -0
  50. data/lib/flexi_admin/components/resources/list_view/cell_component.rb +26 -0
  51. data/lib/flexi_admin/components/resources/list_view/table_component.html.slim +17 -0
  52. data/lib/flexi_admin/components/resources/list_view/table_component.rb +21 -0
  53. data/lib/flexi_admin/components/resources/list_view_component.rb +80 -0
  54. data/lib/flexi_admin/components/resources/pagination_component.html.slim +42 -0
  55. data/lib/flexi_admin/components/resources/pagination_component.rb +64 -0
  56. data/lib/flexi_admin/components/resources/resources_component.rb +34 -0
  57. data/lib/flexi_admin/components/resources/switch_view_component.html.slim +16 -0
  58. data/lib/flexi_admin/components/resources/switch_view_component.rb +45 -0
  59. data/lib/flexi_admin/components/resources/view_component.html.slim +12 -0
  60. data/lib/flexi_admin/components/resources/view_component.rb +15 -0
  61. data/lib/flexi_admin/components/shared/alert_component.html.slim +2 -0
  62. data/lib/flexi_admin/components/shared/alert_component.rb +11 -0
  63. data/lib/flexi_admin/components/shared/autocomplete/results_component.html.slim +15 -0
  64. data/lib/flexi_admin/components/shared/autocomplete/results_component.rb +50 -0
  65. data/lib/flexi_admin/components/shared/autocomplete.rb +6 -0
  66. data/lib/flexi_admin/components/shared/datalist_component.html.slim +24 -0
  67. data/lib/flexi_admin/components/shared/datalist_component.rb +20 -0
  68. data/lib/flexi_admin/components/shared/link_component.html.slim +3 -0
  69. data/lib/flexi_admin/components/shared/link_component.rb +15 -0
  70. data/lib/flexi_admin/components/shared/medium_component.html.slim +7 -0
  71. data/lib/flexi_admin/components/shared/medium_component.rb +51 -0
  72. data/lib/flexi_admin/components/shared/table/header_item_component.html.slim +9 -0
  73. data/lib/flexi_admin/components/shared/table/header_item_component.rb +78 -0
  74. data/lib/flexi_admin/components/shared/trix_component.html.slim +22 -0
  75. data/lib/flexi_admin/components/shared/trix_component.rb +21 -0
  76. data/lib/flexi_admin/components.rb +87 -0
  77. data/lib/flexi_admin/config.rb +24 -0
  78. data/lib/flexi_admin/controllers/modals_controller.rb +13 -0
  79. data/lib/flexi_admin/controllers/resources_controller.rb +240 -0
  80. data/lib/flexi_admin/controllers.rb +9 -0
  81. data/lib/flexi_admin/engine.rb +34 -0
  82. data/lib/flexi_admin/helpers/application_helper.rb +4 -0
  83. data/lib/flexi_admin/helpers.rb +8 -0
  84. data/lib/flexi_admin/javascript/controllers/application.js +9 -0
  85. data/lib/flexi_admin/javascript/controllers/autocomplete_controller.js +142 -0
  86. data/lib/flexi_admin/javascript/controllers/bulk_action_controller.js +158 -0
  87. data/lib/flexi_admin/javascript/controllers/button_select_controller.js +32 -0
  88. data/lib/flexi_admin/javascript/controllers/datalist_controller.js +104 -0
  89. data/lib/flexi_admin/javascript/controllers/floating_toc_controller.js +39 -0
  90. data/lib/flexi_admin/javascript/controllers/form_controller.js +17 -0
  91. data/lib/flexi_admin/javascript/controllers/form_validation_controller.js +86 -0
  92. data/lib/flexi_admin/javascript/controllers/index.js +44 -0
  93. data/lib/flexi_admin/javascript/controllers/pagination_controller.js +13 -0
  94. data/lib/flexi_admin/javascript/controllers/sorting_controller.js +17 -0
  95. data/lib/flexi_admin/javascript/controllers/switch_view_controller.js +15 -0
  96. data/lib/flexi_admin/javascript/controllers/toast_controller.js +18 -0
  97. data/lib/flexi_admin/javascript/controllers/trix_controller.js +32 -0
  98. data/lib/flexi_admin/javascript/controllers/uploads_controller.js +164 -0
  99. data/lib/flexi_admin/javascript/flexi_admin.js +4 -0
  100. data/lib/flexi_admin/javascript/utils.js +26 -0
  101. data/lib/flexi_admin/models/concerns/application_resource.rb +25 -0
  102. data/lib/flexi_admin/models/concerns/parentable.rb +17 -0
  103. data/lib/flexi_admin/models/context_params.rb +127 -0
  104. data/lib/flexi_admin/models/resources/context.rb +59 -0
  105. data/lib/flexi_admin/models/struct.rb +29 -0
  106. data/lib/flexi_admin/models/toast.rb +23 -0
  107. data/lib/flexi_admin/models.rb +20 -0
  108. data/lib/flexi_admin/prompts/codegen-system-prompt.md +50 -0
  109. data/lib/flexi_admin/railtie.rb +78 -0
  110. data/lib/flexi_admin/routes.rb +15 -0
  111. data/lib/flexi_admin/services/code_gen/code_export.rb +22 -0
  112. data/lib/flexi_admin/services/code_gen/gemini.rb +104 -0
  113. data/lib/flexi_admin/services/code_gen/gpt.rb +68 -0
  114. data/lib/flexi_admin/services/code_gen/runner.rb +210 -0
  115. data/lib/flexi_admin/services/code_gen.rb +14 -0
  116. data/lib/flexi_admin/services/create_resource.rb +32 -0
  117. data/lib/flexi_admin/services/update_resource.rb +30 -0
  118. data/lib/flexi_admin/services.rb +10 -0
  119. data/lib/flexi_admin/version.rb +10 -0
  120. data/lib/flexi_admin/views/shared/_redirect.slim +2 -0
  121. data/lib/flexi_admin/views/shared/_reload.slim +2 -0
  122. data/lib/flexi_admin/views/shared/_toasts.slim +15 -0
  123. data/lib/flexi_admin/views/shared/not_authorized.slim +11 -0
  124. data/lib/flexi_admin.rb +58 -0
  125. data/lib/tasks/flexi_admin.rake +49 -0
  126. data/lib/tasks/semantic.rake +57 -0
  127. metadata +333 -0
@@ -0,0 +1,287 @@
1
+ # frozen_string_literal: true
2
+
3
+ # with single field (inline: false)
4
+ # form-row
5
+ # col-12 col-md-3
6
+ # label
7
+ # col-12 col-md-9
8
+ # field-wrapper
9
+ # field
10
+ # validation-error (if resource.errors[attr_name].present?)
11
+
12
+ # with multiple fields (inline: true)
13
+ # form-row
14
+ # col-12 col-md-3
15
+ # label
16
+ # col-12 col-md-9 inline-field-wrapper
17
+ # field-wrapper
18
+ # field
19
+ # validation-error (if resource.errors[attr_name].present?)
20
+ # field-wrapper
21
+ # field
22
+ # validation-error (if resource.errors[attr_name].present?)
23
+
24
+ module FlexiAdmin::Components::Resource
25
+ module FormMixin
26
+ include FlexiAdmin::Components::Helpers::ResourceHelper
27
+
28
+ attr_reader :resource, :disabled, :inline, :parent_resource
29
+
30
+ # Form does not render; fields do.
31
+ def form(url: resource_path(resource), css_class: 'myForm section', method: :patch, **html_options, &block)
32
+ render FlexiAdmin::Components::Resource::FormElementComponent.new(resource, url:, css_class:, method:, **html_options) do |component|
33
+ component.with_fields do
34
+ capture(&block)
35
+ end
36
+ end
37
+ end
38
+
39
+ def select_field(attr_name, options:, label: nil, value: nil, **html_options)
40
+ field = render_select(attr_name, value:, options:, **html_options)
41
+ field_wrapper = render_field_wrapper(field, attr_name)
42
+
43
+ inline ? field_wrapper : render_form_row(attr_name, field_wrapper, label:)
44
+ end
45
+
46
+ def button_select_field(attr_name, options:, label: nil, value: nil, **html_options)
47
+ field = render_button_select(attr_name, options:, value:, disabled:, **html_options)
48
+ field_wrapper = render_field_wrapper(field, attr_name)
49
+
50
+ inline ? field_wrapper : render_form_row(attr_name, field_wrapper, label:)
51
+ end
52
+
53
+ def text_field(attr_name, label: nil, value: nil, **html_options)
54
+ field = text_field_tag(attr_name, value:, **html_options)
55
+ field_wrapper = render_field_wrapper(field, attr_name)
56
+
57
+ inline ? field_wrapper : render_form_row(attr_name, field_wrapper, label:)
58
+ end
59
+
60
+ def text_field_tag(attr_name, value: nil, **html_options)
61
+ render_standard_field(:text, attr_name, value, html_options)
62
+ end
63
+
64
+ def number_field(attr_name, label: nil, value: nil, **html_options)
65
+ render_standard_field(:number, attr_name, value, html_options)
66
+ end
67
+
68
+ def checkbox_field(attr_name, label: nil, checked: nil, **html_options)
69
+ checkbox = checkbox_field_tag(attr_name, checked:, **html_options)
70
+ field_wrapper = render_field_wrapper(checkbox, attr_name)
71
+
72
+ inline ? field_wrapper : render_form_row(attr_name, field_wrapper, label:)
73
+ end
74
+
75
+ def checkbox_field_tag(attr_name, checked: nil, **html_options)
76
+ val = checked.is_a?(Proc) ? checked.call : resource.try(attr_name) || checked
77
+ hidden__field = hidden_field(attr_name, value: 0)
78
+
79
+ options = {
80
+ type: 'checkbox',
81
+ name: attr_name,
82
+ class: 'form-check-input',
83
+ value: 1,
84
+ checked: val,
85
+ disabled:
86
+ }.merge(html_options)
87
+
88
+ checkbox = content_tag(:input, nil, options)
89
+
90
+ hidden__field + checkbox
91
+ end
92
+
93
+ def hidden_field(attr_name, value: nil, **html_options)
94
+ html_options = { type: 'hidden', name: attr_name, value: }.merge(html_options)
95
+ content_tag(:input, nil, html_options)
96
+ end
97
+
98
+ def date_field(attr_name, label: nil, value: nil, **html_options)
99
+ field = render_standard_field(:date, attr_name, value, html_options.merge(style: 'max-width: 180px;'))
100
+ field_wrapper = render_field_wrapper(field, attr_name)
101
+
102
+ inline ? field_wrapper : render_form_row(attr_name, field_wrapper, label:)
103
+ end
104
+
105
+ def datetime_field(attr_name, label: nil, value: nil, **html_options)
106
+ field = render_standard_field(:datetime, attr_name, value, html_options.merge(style: 'max-width: 180px;'))
107
+ field_wrapper = render_field_wrapper(field, attr_name)
108
+
109
+ inline ? field_wrapper : render_form_row(attr_name, field_wrapper, label:)
110
+ end
111
+
112
+ # Trix field
113
+ def html_field(attr_name, label: nil, value: nil, **html_options)
114
+ custom_field(FlexiAdmin::Components::Shared::TrixComponent.new(attr_name:, value:, disabled:), label:, **html_options)
115
+ end
116
+
117
+ def header(label, description: nil)
118
+ header_and_description = []
119
+ header_and_description << content_tag(:div, class: 'header') { label }
120
+ header_and_description << content_tag(:div, class: 'description') { description } if description.present?
121
+
122
+ content_tag(:div, class: 'form-row d-flex flex-column gap-2') do
123
+ concat content_tag(:div, class: 'col-12') { header_and_description.join.html_safe }
124
+ end
125
+ end
126
+
127
+ def custom_field(view_component_instance, label: nil, **html_options, &block)
128
+ label ||= view_component_instance.class.name.demodulize.humanize unless label == false
129
+ value = block_given? ? capture(&block) : view_component_instance
130
+
131
+ render_custom_field(view_component_instance, label, html_options, value)
132
+ end
133
+
134
+ def submit(label = 'Uložit', cancel_button: true, cancel_button_url: nil, icon: nil, classes: '')
135
+ submit_button = if icon.present?
136
+ helpers.content_tag(:button, class: 'btn btn-primary ' + classes, disabled: disabled) do
137
+ content = []
138
+ content << helpers.content_tag(:i, '', class: "bi bi-#{icon} me-2")
139
+ content << label
140
+ content.join.html_safe
141
+ end
142
+ else
143
+ helpers.submit_tag(label, class: 'btn btn-primary ' + classes, disabled:)
144
+ end
145
+
146
+ if cancel_button
147
+ cancel_btn_path = cancel_button_url || edit_resource_path(resource, fa_form_disabled: true)
148
+ cancel_btn = content_tag(:button, 'Zrušit', class: 'btn btn-outline-secondary',
149
+ disabled:,
150
+ data: { controller: 'form',
151
+ action: 'click->form#disable',
152
+ 'form-resource-path-value': cancel_btn_path })
153
+ end
154
+
155
+ content_tag(:div, class: 'form-row d-flex gap-3') do
156
+ concat submit_button
157
+ concat cancel_btn if cancel_button
158
+ end
159
+ end
160
+
161
+ def with_resource(resource)
162
+ previous_resource = @resource
163
+ @resource = resource
164
+ yield
165
+ ensure
166
+ @resource = previous_resource
167
+ end
168
+
169
+ private
170
+
171
+ def render_standard_field(type, attr_name, value, html_options)
172
+ val = value.is_a?(Proc) ? value.call : resource.try(attr_name) || value
173
+ data = {
174
+ action: 'blur->form-validation#handleInvalid keyup->form-validation#handleInvalid'
175
+ }
176
+ merge_model_error!(data, attr_name) unless type == :submit
177
+
178
+ content = []
179
+ input_classes = 'form-control'
180
+ input_classes += ' is-invalid' if resource.present? && attr_name.present? && resource.errors[attr_name].present?
181
+ if type != :submit
182
+ content << content_tag(:input, nil,
183
+ {
184
+ type:,
185
+ name: attr_name,
186
+ value: val,
187
+ class: input_classes,
188
+ data:
189
+ }.merge(html_options)
190
+ .merge(disabled:))
191
+ end
192
+
193
+ content.join.html_safe
194
+ end
195
+
196
+ def render_field_wrapper(field, attr_name)
197
+ content_tag(:div, class: inline ? 'inline-field' : 'field-wrapper') do
198
+ content = []
199
+ content << field
200
+ if resource.present? && attr_name.present?
201
+ content << content_tag(:div, class: 'invalid-feedback') do
202
+ resource.errors[attr_name].join(' ')
203
+ end
204
+ end
205
+
206
+ content.join.html_safe
207
+ end
208
+ end
209
+
210
+ def render_select(attr_name, options:, value: nil, **html_options)
211
+ data = {
212
+ action: 'blur->form-validation#handleInvalid change->form-validation#handleInvalid'
213
+ }
214
+ merge_model_error!(data, attr_name)
215
+
216
+ selected = value.is_a?(Proc) ? value.call : resource.try(attr_name) || value
217
+
218
+ content = []
219
+ content << content_tag(:select,
220
+ {
221
+ name: attr_name,
222
+ class: 'form-select',
223
+ data: data,
224
+ disabled:
225
+ }.merge(html_options)) do
226
+ options_for_select(options, selected: selected, disabled: 'disabled')
227
+ end
228
+ if resource.present? && attr_name.present?
229
+ content << content_tag(:div, class: 'invalid-feedback') do
230
+ resource.errors[attr_name].join(' ')
231
+ end
232
+ end
233
+
234
+ content.join.html_safe
235
+ end
236
+
237
+ def field(attr_name, label: nil, inline: false, **html_options, &block)
238
+ label ||= attr_name.to_s.humanize unless label == false
239
+ @inline = inline
240
+ content = capture(&block)
241
+
242
+ render_form_row(attr_name, content, label:, **html_options)
243
+ end
244
+
245
+ def render_button_select(attr_name, options:, value: nil, resource: nil, **html_options)
246
+ res = resource.present? ? resource : @resource
247
+ val = value.is_a?(Proc) ? value.call : res.try(attr_name) || value
248
+
249
+ render(FlexiAdmin::Components::Resource::ButtonSelectComponent.new(res,
250
+ attr_name,
251
+ options,
252
+ disabled:,
253
+ form: @form,
254
+ value: val,
255
+ **html_options))
256
+ end
257
+
258
+ def render_form_row(attr_name, field_html, label:, **html_options)
259
+ label ||= attr_name.to_s.humanize unless label == false
260
+
261
+ content_tag(:div, html_options.merge(class: 'form-row')) do
262
+ concat content_tag(:div, class: 'col-12 col-md-3') { label_tag(attr_name, label) } unless label == false
263
+ concat content_tag(:div, class: inline ? 'col-12 col-md-9 inline-field-wrapper' : 'col-12 col-md-9') {
264
+ field_html
265
+ }
266
+ end
267
+ end
268
+
269
+ def render_custom_field(view_component_instance, label, html_options, _value)
270
+ content_tag(:div, html_options.merge(class: 'form-row')) do
271
+ unless label == false
272
+ concat content_tag(:div, class: 'col-12 col-md-3') {
273
+ label_tag('autocomplete', label)
274
+ }
275
+ end
276
+ concat content_tag(:div, class: 'col-12 col-md-9') { render view_component_instance }
277
+ end
278
+ end
279
+
280
+ def merge_model_error!(data, attr_name)
281
+ return if resource.blank?
282
+ return if attr_name.blank?
283
+
284
+ data.merge!(validation_error: resource.errors[attr_name].join(' ')) if resource.errors.present?
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,8 @@
1
+ - if icon.present?
2
+ = link_to path, **options.merge(data: { turbo_frame: '_top' }, class: 'dropdown-item') do
3
+ i.bi.me-2 class=icon_class
4
+ = label
5
+ /a href="#{path}" data-turbo-frame="_top" class="dropdown-item"
6
+ - else
7
+ = link_to label, path, **options.merge(data: { turbo_frame: '_top' }, class: 'dropdown-item')
8
+ /a href="#{path}" data-turbo-frame="_top" class="dropdown-item" = label
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlexiAdmin::Components::Resource
4
+ class LinkActionComponent < FlexiAdmin::Components::BaseComponent
5
+ extend FlexiAdmin::Components::Helpers::ActionButtonHelper
6
+ include FlexiAdmin::Components::Helpers::IconHelper
7
+
8
+ attr_reader :label, :path, :options
9
+
10
+ def initialize(label, path, **options)
11
+ @label = label
12
+ @path = path
13
+ @options = options
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlexiAdmin::Components::Resource
4
+ class ShowPageComponent < FlexiAdmin::Components::BaseComponent
5
+ include FlexiAdmin::Components::Helpers::ResourceHelper
6
+ include FlexiAdmin::Components::Helpers::ActionHelper
7
+
8
+ attr_reader :resource, :context_params, :disabled
9
+
10
+ def initialize(resource, context_params:, scope: nil, disabled: true)
11
+ @resource = resource
12
+ @scope = scope
13
+ @context_params = context_params
14
+ @disabled = disabled
15
+ end
16
+
17
+ def context
18
+ @context ||= FlexiAdmin::Models::Resources::Context.from_params(context_params.merge(scope: @scope), resource)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+
2
+ // Header
3
+ .d-flex.justify-content-between.align-items-center.sticky-top.top-0.py-3.bg-white
4
+ .d-flex.align-items-center
5
+ .d-flex.flex-column
6
+ h1 = resource.title
7
+ / = render FlexiAdmin::Components::Nav::FloatingTocComponent.new
8
+
9
+ // Floating navigation
10
+ // Akce
11
+ div.actions
12
+ .d-flex.gap-2
13
+ = actions
14
+ .btn-group
15
+ .btn.btn-outline-primary [data-controller="form"
16
+ data-form-resource-path-value="#{edit_resource_path(resource)}"
17
+ data-action="click->form#enable"]
18
+ i.bi.bi-pencil-square
19
+ .btn.btn-outline-danger
20
+ i.bi.bi-trash
21
+
22
+ .row.g-0.my-2.mb-4
23
+ .col-12.col-md-8
24
+ = form
25
+
26
+ = content
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlexiAdmin::Components::Resource
4
+ class ViewComponent < FlexiAdmin::Components::BaseComponent
5
+ include FlexiAdmin::Components::Helpers::ResourceHelper
6
+ include FlexiAdmin::Components::Helpers::ActionHelper
7
+
8
+ attr_reader :context, :resource
9
+
10
+ renders_one :form
11
+ renders_one :actions
12
+
13
+ def initialize(context)
14
+ @context = context
15
+ @resource = context.resource
16
+ end
17
+
18
+ def divider
19
+ content_tag :div, "", class: "dropdown-divider"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ button.dropdown-item.bulk-action [class="#{css_utility_classes}"
2
+ data-action="click->bulk-action#requestModal"
3
+ data-scope="#{context.scope}"
4
+ data-url-with-id="#{scoped_url_with_modal_id}"]
5
+ - if modal_class.button_icon.present?
6
+ i.bi.me-2 class="#{modal_class.button_icon_class}"
7
+ = modal_class.button_text
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlexiAdmin::Components::Resources::BulkAction
4
+ class ButtonComponent < FlexiAdmin::Components::BaseComponent
5
+ include FlexiAdmin::Components::Helpers::UrlHelper
6
+ include FlexiAdmin::Components::Helpers::IconHelper
7
+
8
+ attr_reader :context, :modal_class, :disabled, :selection_dependent, :options
9
+
10
+ def initialize(context, modal_class, disabled: true, selection_dependent: true, **options)
11
+ @context = context
12
+ @modal_class = modal_class
13
+ @disabled = selection_dependent && disabled
14
+ @selection_dependent = selection_dependent
15
+ @options = options
16
+ end
17
+
18
+ def css_utility_classes
19
+ classes = []
20
+ classes << "disabled" if disabled
21
+ classes << "selection-dependent" if selection_dependent
22
+ classes.compact.join(" ")
23
+ end
24
+
25
+ def scoped_url_with_modal_id
26
+ main_app.admin_modals_path(kind: modal_class.modal_id, **context.to_params)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ div.modal.fade id="#{context.options[:modal_id]}" tabindex="-1"
2
+ div.modal-dialog
3
+ div.modal-content
4
+ div.modal-header
5
+ h1.modal-title.fs-5 = context.options[:title]
6
+ button.btn-close type="button" data-bs-dismiss="modal" aria-label="Close"
7
+
8
+ div.modal-body
9
+ = modal_form
10
+
11
+ div.modal-footer.d-flex.justify-content-between.align-items-center
12
+ .d-flex.justify-content-start.gap-3
13
+ button.btn.btn-primary [type="submit"
14
+ data-action="click->bulk-action#submitForm"]
15
+ | Spustit
16
+ button.btn.btn-secondary [type="button"
17
+ data-bs-dismiss="modal"]
18
+ | Zrušit
19
+ span
20
+ |> vybraných položek:
21
+ span.count
22
+
23
+ javascript:
24
+ var modal = new window.bootstrap.Modal(document.getElementById('#{context.options[:modal_id]}'));
25
+ modal.show();
26
+ document.dispatchEvent(new CustomEvent('bulk-action-modal-opened',
27
+ { detail:
28
+ {
29
+ modalId: '#{context.options[:modal_id]}',
30
+ scope: '#{context.scope}'
31
+ }
32
+ }));
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlexiAdmin::Components::Resources::BulkAction
4
+ class ModalComponent < FlexiAdmin::Components::Resource::FormComponent
5
+ include FlexiAdmin::Components::Resource::FormMixin
6
+ extend FlexiAdmin::Components::Helpers::ActionButtonHelper
7
+
8
+ class << self
9
+ attr_accessor :class_name
10
+ end
11
+
12
+ attr_reader :context
13
+
14
+ renders_one :modal_form
15
+
16
+ def initialize(context = nil)
17
+ @context = context
18
+
19
+ add_context_options(@context)
20
+ super(nil, disabled: false)
21
+ end
22
+
23
+ def modal(context, &block)
24
+ render FlexiAdmin::Components::Resources::BulkAction::ModalComponent.new(context) do |component|
25
+ component.with_modal_form(&block)
26
+ end
27
+ end
28
+
29
+ def form(url: self.class.path, method: :post, **html_options, &block)
30
+ super(url:, css_class: 'modalForm section', method:, **html_options, &block)
31
+ end
32
+
33
+ # /observation_images/bulk_action
34
+ def self.path
35
+ resource = (self.class_name&.to_s || to_s).split('::').first.underscore.gsub('/', '-')
36
+ "/#{resource.pluralize}/bulk_action"
37
+ end
38
+
39
+ def self.modal_id
40
+ to_s.underscore.gsub('/', '-')
41
+ end
42
+
43
+ def add_context_options(context)
44
+ # Means it's been already added by the parent component
45
+ return if context.options[:title].present?
46
+
47
+ context.options ||= {}
48
+ context.options[:title] = self.class.title_text
49
+ context.options[:modal_id] = self.class.modal_id
50
+ context.options[:action_path] = self.class.path
51
+ context.options[:class_name] = self.class.to_s
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,18 @@
1
+ .card style="width: 18rem;"
2
+ = render(FlexiAdmin::Components::Actions::CheckboxComponent.new(id: resource.id, scope: resource.class.name.downcase))
3
+ div
4
+ - if media?
5
+ = render_media
6
+ - else
7
+ .d-flex.w-100.h-100.justify-content-center.align-items-center.bg-light style="min-height: 180px;"
8
+ i.bi.bi-card-image.h2.text-secondary
9
+ .card-body
10
+ - if header
11
+ h6.card-subtitle.mb-2.text-muted
12
+ = header_value
13
+
14
+ - if title
15
+ h5.card-title
16
+ = navigate_to title_value, resource
17
+
18
+ p.card-text = description_value
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlexiAdmin::Components::Resources::GridView
4
+ class CardComponent < FlexiAdmin::Components::BaseComponent
5
+ include FlexiAdmin::Components::Helpers::LinkHelper
6
+
7
+ attr_accessor :resource, :title, :header, :description, :image
8
+
9
+ def initialize(resource, title, header, description, image)
10
+ @resource = resource
11
+ @title = title
12
+ @header = header
13
+ @description = description
14
+ @image = image
15
+ end
16
+
17
+ def media_attachment
18
+ @media_attachment ||= image.value[resource]
19
+ end
20
+
21
+ def image_src
22
+ return unless image
23
+ return if media_attachment.blank?
24
+
25
+ image.src(media_attachment, variant: :thumb)
26
+ end
27
+
28
+ def media?
29
+ return false unless image
30
+ return false if media_attachment.blank?
31
+
32
+ true
33
+ end
34
+
35
+ def render_media
36
+ case media_attachment.content_type.split("/").first
37
+ when "image"
38
+ content_tag :img, nil, src: image_src, style: "object-fit: cover; width: 100%; height: 180px;"
39
+ when "video"
40
+ content_tag :video, nil, src: image_src, style: "object-fit: cover; width: 100%; height: 180px;",
41
+ controls: true,
42
+ onmouseover: "this.play()",
43
+ onmouseout: "this.pause()"
44
+ end
45
+ end
46
+
47
+ def title_value
48
+ return unless title
49
+
50
+ title.formatted_value(title.value[resource])
51
+ end
52
+
53
+ def header_value
54
+ return unless header
55
+
56
+ header.formatted_value(header.value[resource])
57
+ end
58
+
59
+ def description_value
60
+ return unless description
61
+
62
+ description.formatted_value(description.value[resource])
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,10 @@
1
+ /div.pb-2
2
+ = render Actions::CheckboxComponent.new(id: 'all', scope: context.scope, select_all: true)
3
+ = label_tag 'all', 'vybrat vše', class: 'form-check-label text-muted text-nowrap', for: 'checkbox-all'
4
+ .grid-view.row.g-0.row-cols-2.gap-4.row-cols-lg-3.row-cols-xl-4.row-cols-xxl-5
5
+ - resources.each do |resource|
6
+ = render FlexiAdmin::Components::Resources::GridView::CardComponent.new(resource,
7
+ title_element,
8
+ header_element,
9
+ description_element,
10
+ image_element)
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FlexiAdmin::Components::Resources::GridView
4
+ class GridComponent < FlexiAdmin::Components::BaseComponent
5
+ attr_reader :resources, :title_element, :header_element, :description_element, :image_element, :context
6
+
7
+ def initialize(resources, title_element, header_element, description_element, image_element, context)
8
+ @resources = resources
9
+ @title_element = title_element
10
+ @header_element = header_element
11
+ @description_element = description_element
12
+ @image_element = image_element
13
+ @context = context
14
+ end
15
+ end
16
+ end