easy-admin-rails 0.1.0

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 (203) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/builds/easy_admin.base.js +43505 -0
  6. data/app/assets/builds/easy_admin.base.js.map +7 -0
  7. data/app/assets/builds/easy_admin.css +6141 -0
  8. data/app/assets/config/easy_admin_manifest.js +1 -0
  9. data/app/assets/images/jsoneditor-icons.svg +749 -0
  10. data/app/assets/stylesheets/easy_admin/application.tailwind.css +390 -0
  11. data/app/components/easy_admin/base_component.rb +35 -0
  12. data/app/components/easy_admin/batch_action_bar_component.rb +125 -0
  13. data/app/components/easy_admin/batch_action_form_component.rb +124 -0
  14. data/app/components/easy_admin/combined_filters_component.rb +232 -0
  15. data/app/components/easy_admin/confirmation_modal_component.rb +61 -0
  16. data/app/components/easy_admin/context_menu_component.rb +161 -0
  17. data/app/components/easy_admin/dashboards/base_card_component.rb +152 -0
  18. data/app/components/easy_admin/dashboards/card_error_component.rb +23 -0
  19. data/app/components/easy_admin/dashboards/card_factory.rb +90 -0
  20. data/app/components/easy_admin/dashboards/card_stream_component.rb +22 -0
  21. data/app/components/easy_admin/dashboards/cards/base_card_component.rb +54 -0
  22. data/app/components/easy_admin/dashboards/cards/chart_card_component.rb +175 -0
  23. data/app/components/easy_admin/dashboards/cards/custom_card_component.rb +50 -0
  24. data/app/components/easy_admin/dashboards/cards/metric_card_component.rb +164 -0
  25. data/app/components/easy_admin/dashboards/cards/table_card_component.rb +148 -0
  26. data/app/components/easy_admin/dashboards/chart_card_component.rb +44 -0
  27. data/app/components/easy_admin/dashboards/metric_card_component.rb +56 -0
  28. data/app/components/easy_admin/dashboards/refresh_stream_component.rb +279 -0
  29. data/app/components/easy_admin/dashboards/show_component.rb +163 -0
  30. data/app/components/easy_admin/dashboards/table_card_component.rb +52 -0
  31. data/app/components/easy_admin/date_picker_component.rb +188 -0
  32. data/app/components/easy_admin/fields/base_component.rb +101 -0
  33. data/app/components/easy_admin/fields/belongs_to_edit_modal_component.rb +117 -0
  34. data/app/components/easy_admin/fields/form/belongs_to_component.rb +82 -0
  35. data/app/components/easy_admin/fields/form/boolean_component.rb +100 -0
  36. data/app/components/easy_admin/fields/form/date_component.rb +55 -0
  37. data/app/components/easy_admin/fields/form/datetime_component.rb +55 -0
  38. data/app/components/easy_admin/fields/form/email_component.rb +55 -0
  39. data/app/components/easy_admin/fields/form/file_component.rb +190 -0
  40. data/app/components/easy_admin/fields/form/has_many_component.rb +416 -0
  41. data/app/components/easy_admin/fields/form/json_component.rb +81 -0
  42. data/app/components/easy_admin/fields/form/number_component.rb +55 -0
  43. data/app/components/easy_admin/fields/form/select_component.rb +326 -0
  44. data/app/components/easy_admin/fields/form/text_component.rb +55 -0
  45. data/app/components/easy_admin/fields/form/textarea_component.rb +54 -0
  46. data/app/components/easy_admin/fields/index/belongs_to_component.rb +93 -0
  47. data/app/components/easy_admin/fields/index/boolean_component.rb +29 -0
  48. data/app/components/easy_admin/fields/index/date_component.rb +13 -0
  49. data/app/components/easy_admin/fields/index/datetime_component.rb +13 -0
  50. data/app/components/easy_admin/fields/index/email_component.rb +24 -0
  51. data/app/components/easy_admin/fields/index/filters/base_component.rb +48 -0
  52. data/app/components/easy_admin/fields/index/filters/boolean_component.rb +96 -0
  53. data/app/components/easy_admin/fields/index/filters/date_component.rb +182 -0
  54. data/app/components/easy_admin/fields/index/filters/number_component.rb +30 -0
  55. data/app/components/easy_admin/fields/index/filters/select_component.rb +101 -0
  56. data/app/components/easy_admin/fields/index/filters/string_component.rb +32 -0
  57. data/app/components/easy_admin/fields/index/json_component.rb +23 -0
  58. data/app/components/easy_admin/fields/index/number_component.rb +20 -0
  59. data/app/components/easy_admin/fields/index/select_component.rb +25 -0
  60. data/app/components/easy_admin/fields/index/text_component.rb +20 -0
  61. data/app/components/easy_admin/fields/inline_edit_modal_component.rb +135 -0
  62. data/app/components/easy_admin/fields/inline_edit_trigger_component.rb +144 -0
  63. data/app/components/easy_admin/fields/show/belongs_to_component.rb +93 -0
  64. data/app/components/easy_admin/fields/show/boolean_component.rb +21 -0
  65. data/app/components/easy_admin/fields/show/date_component.rb +13 -0
  66. data/app/components/easy_admin/fields/show/datetime_component.rb +13 -0
  67. data/app/components/easy_admin/fields/show/email_component.rb +19 -0
  68. data/app/components/easy_admin/fields/show/file_component.rb +304 -0
  69. data/app/components/easy_admin/fields/show/has_many_component.rb +192 -0
  70. data/app/components/easy_admin/fields/show/json_component.rb +45 -0
  71. data/app/components/easy_admin/fields/show/number_component.rb +20 -0
  72. data/app/components/easy_admin/fields/show/select_component.rb +25 -0
  73. data/app/components/easy_admin/fields/show/text_component.rb +17 -0
  74. data/app/components/easy_admin/fields/show/textarea_component.rb +26 -0
  75. data/app/components/easy_admin/filters_component.rb +120 -0
  76. data/app/components/easy_admin/form_tabs_component.rb +166 -0
  77. data/app/components/easy_admin/infinite_scroll_component.rb +82 -0
  78. data/app/components/easy_admin/lazy_chart_card_component.rb +128 -0
  79. data/app/components/easy_admin/lazy_metric_card_component.rb +76 -0
  80. data/app/components/easy_admin/modal_frame_component.rb +26 -0
  81. data/app/components/easy_admin/navbar_component.rb +226 -0
  82. data/app/components/easy_admin/notification_component.rb +83 -0
  83. data/app/components/easy_admin/pagination_component.rb +188 -0
  84. data/app/components/easy_admin/quick_filters_component.rb +65 -0
  85. data/app/components/easy_admin/resource_pagination_component.rb +14 -0
  86. data/app/components/easy_admin/resources/index_component.rb +211 -0
  87. data/app/components/easy_admin/resources/index_frame_component.rb +88 -0
  88. data/app/components/easy_admin/resources/show_page_actions_component.rb +324 -0
  89. data/app/components/easy_admin/resources/table_cell_component.rb +145 -0
  90. data/app/components/easy_admin/resources/table_component.rb +206 -0
  91. data/app/components/easy_admin/resources/table_row_component.rb +160 -0
  92. data/app/components/easy_admin/row_action_form_component.rb +127 -0
  93. data/app/components/easy_admin/scopes_component.rb +224 -0
  94. data/app/components/easy_admin/settings_sidebar_component.rb +140 -0
  95. data/app/components/easy_admin/show_layout_component.rb +600 -0
  96. data/app/components/easy_admin/sidebar_component.rb +174 -0
  97. data/app/components/easy_admin/turbo/response_component.rb +40 -0
  98. data/app/components/easy_admin/turbo/stream_component.rb +28 -0
  99. data/app/controllers/easy_admin/application_controller.rb +66 -0
  100. data/app/controllers/easy_admin/batch_actions_controller.rb +166 -0
  101. data/app/controllers/easy_admin/confirmation_modal_controller.rb +20 -0
  102. data/app/controllers/easy_admin/dashboard_controller.rb +6 -0
  103. data/app/controllers/easy_admin/dashboards_controller.rb +123 -0
  104. data/app/controllers/easy_admin/passwords_controller.rb +15 -0
  105. data/app/controllers/easy_admin/registrations_controller.rb +52 -0
  106. data/app/controllers/easy_admin/resources_controller.rb +907 -0
  107. data/app/controllers/easy_admin/row_actions_controller.rb +216 -0
  108. data/app/controllers/easy_admin/sessions_controller.rb +32 -0
  109. data/app/controllers/easy_admin/settings_controller.rb +94 -0
  110. data/app/helpers/easy_admin/application_helper.rb +4 -0
  111. data/app/helpers/easy_admin/dashboards_helper.rb +121 -0
  112. data/app/helpers/easy_admin/fields_helper.rb +27 -0
  113. data/app/helpers/easy_admin/pagy_helper.rb +30 -0
  114. data/app/helpers/easy_admin/resources_helper.rb +39 -0
  115. data/app/javascript/easy_admin/application.js +12 -0
  116. data/app/javascript/easy_admin/controllers/batch_modal_controller.js +66 -0
  117. data/app/javascript/easy_admin/controllers/batch_selection_controller.js +223 -0
  118. data/app/javascript/easy_admin/controllers/chart_controller.js +216 -0
  119. data/app/javascript/easy_admin/controllers/collapsible_filters_controller.js +118 -0
  120. data/app/javascript/easy_admin/controllers/confirmation_modal_controller.js +64 -0
  121. data/app/javascript/easy_admin/controllers/context_menu_controller.js +227 -0
  122. data/app/javascript/easy_admin/controllers/date_picker_controller.js +309 -0
  123. data/app/javascript/easy_admin/controllers/dropdown_controller.js +63 -0
  124. data/app/javascript/easy_admin/controllers/event_emitter_controller.js +19 -0
  125. data/app/javascript/easy_admin/controllers/file_controller.js +121 -0
  126. data/app/javascript/easy_admin/controllers/form_tabs_controller.js +100 -0
  127. data/app/javascript/easy_admin/controllers/has_many_search_controller.js +76 -0
  128. data/app/javascript/easy_admin/controllers/infinite_scroll_controller.js +174 -0
  129. data/app/javascript/easy_admin/controllers/ios_alert_controller.js +195 -0
  130. data/app/javascript/easy_admin/controllers/jsoneditor_controller.js +88 -0
  131. data/app/javascript/easy_admin/controllers/modal_controller.js +75 -0
  132. data/app/javascript/easy_admin/controllers/navbar_scroll_controller.js +76 -0
  133. data/app/javascript/easy_admin/controllers/notification_controller.js +48 -0
  134. data/app/javascript/easy_admin/controllers/row_action_controller.js +124 -0
  135. data/app/javascript/easy_admin/controllers/row_modal_controller.js +59 -0
  136. data/app/javascript/easy_admin/controllers/select_field_controller.js +618 -0
  137. data/app/javascript/easy_admin/controllers/settings_button_controller.js +8 -0
  138. data/app/javascript/easy_admin/controllers/settings_sidebar_controller.js +186 -0
  139. data/app/javascript/easy_admin/controllers/sidebar_controller.js +102 -0
  140. data/app/javascript/easy_admin/controllers/sidebar_mobile_controller.js +23 -0
  141. data/app/javascript/easy_admin/controllers/sidebar_nav_controller.js +96 -0
  142. data/app/javascript/easy_admin/controllers/table_controller.js +28 -0
  143. data/app/javascript/easy_admin/controllers/table_row_controller.js +16 -0
  144. data/app/javascript/easy_admin/controllers/toggle_switch_controller.js +22 -0
  145. data/app/javascript/easy_admin/controllers/turbo_stream_redirect.js +9 -0
  146. data/app/javascript/easy_admin/controllers.js +54 -0
  147. data/app/javascript/easy_admin.base.js +4 -0
  148. data/app/models/easy_admin/admin_user.rb +53 -0
  149. data/app/models/easy_admin/application_record.rb +5 -0
  150. data/app/views/easy_admin/dashboard/index.html.erb +3 -0
  151. data/app/views/easy_admin/dashboards/show.html.erb +7 -0
  152. data/app/views/easy_admin/passwords/edit.html.erb +42 -0
  153. data/app/views/easy_admin/passwords/new.html.erb +41 -0
  154. data/app/views/easy_admin/registrations/new.html.erb +65 -0
  155. data/app/views/easy_admin/resources/_redirect.turbo_stream.erb +3 -0
  156. data/app/views/easy_admin/resources/_table_rows.html.erb +46 -0
  157. data/app/views/easy_admin/resources/edit.html.erb +151 -0
  158. data/app/views/easy_admin/resources/index.html.erb +12 -0
  159. data/app/views/easy_admin/resources/index.turbo_stream.erb +139 -0
  160. data/app/views/easy_admin/resources/index_frame.html.erb +142 -0
  161. data/app/views/easy_admin/resources/new.html.erb +100 -0
  162. data/app/views/easy_admin/resources/show.html.erb +31 -0
  163. data/app/views/easy_admin/sessions/new.html.erb +55 -0
  164. data/app/views/easy_admin/settings/_form.html.erb +51 -0
  165. data/app/views/easy_admin/settings/index.html.erb +53 -0
  166. data/app/views/layouts/easy_admin/application.html.erb +48 -0
  167. data/app/views/layouts/easy_admin/auth.html.erb +34 -0
  168. data/config/initializers/easy_admin_card_factory.rb +27 -0
  169. data/config/initializers/pagy.rb +15 -0
  170. data/config/initializers/rack_mini_profiler.rb +67 -0
  171. data/config/routes.rb +70 -0
  172. data/db/migrate/20250101000001_create_easy_admin_admin_users.rb +45 -0
  173. data/lib/easy-admin.rb +32 -0
  174. data/lib/easy_admin/action.rb +159 -0
  175. data/lib/easy_admin/batch_action.rb +134 -0
  176. data/lib/easy_admin/configuration.rb +75 -0
  177. data/lib/easy_admin/dashboard.rb +110 -0
  178. data/lib/easy_admin/dashboard_registry.rb +30 -0
  179. data/lib/easy_admin/delete_action.rb +22 -0
  180. data/lib/easy_admin/engine.rb +54 -0
  181. data/lib/easy_admin/field.rb +118 -0
  182. data/lib/easy_admin/resource.rb +806 -0
  183. data/lib/easy_admin/resource_registry.rb +22 -0
  184. data/lib/easy_admin/types/json_type.rb +25 -0
  185. data/lib/easy_admin/version.rb +3 -0
  186. data/lib/generators/easy_admin/auth_generator.rb +69 -0
  187. data/lib/generators/easy_admin/card/card_generator.rb +94 -0
  188. data/lib/generators/easy_admin/card/templates/card_component.rb.erb +127 -0
  189. data/lib/generators/easy_admin/card/templates/card_component_spec.rb.erb +122 -0
  190. data/lib/generators/easy_admin/install/templates/easy_admin.rb +31 -0
  191. data/lib/generators/easy_admin/install_generator.rb +25 -0
  192. data/lib/generators/easy_admin/rbac/rbac_generator.rb +244 -0
  193. data/lib/generators/easy_admin/rbac/templates/add_rbac_to_admin_users.rb +23 -0
  194. data/lib/generators/easy_admin/rbac/templates/super_admin.rb +34 -0
  195. data/lib/generators/easy_admin/resource_generator.rb +43 -0
  196. data/lib/generators/easy_admin/templates/AUTH_README +35 -0
  197. data/lib/generators/easy_admin/templates/README +27 -0
  198. data/lib/generators/easy_admin/templates/create_easy_admin_admin_users.rb +45 -0
  199. data/lib/generators/easy_admin/templates/devise.rb +267 -0
  200. data/lib/generators/easy_admin/templates/easy_admin.rb +24 -0
  201. data/lib/generators/easy_admin/templates/resource.rb +29 -0
  202. data/lib/tasks/easy_admin_tasks.rake +4 -0
  203. metadata +445 -0
@@ -0,0 +1,55 @@
1
+ module EasyAdmin
2
+ module Fields
3
+ module Form
4
+ class DatetimeComponent < BaseComponent
5
+ def view_template
6
+ div(class: "mb-4") do
7
+ label(for: field_id, class: label_classes) do
8
+ plain field_label
9
+ if required?
10
+ span(class: "text-red-500 ml-1") { "*" }
11
+ end
12
+ end
13
+ input(
14
+ type: "datetime-local",
15
+ name: form_field_name,
16
+ id: field_id,
17
+ value: current_value,
18
+ class: input_classes,
19
+ required: required?
20
+ )
21
+ if field[:help_text]
22
+ p(class: "mt-1 text-sm text-gray-500") { field[:help_text] }
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def label_classes
30
+ "block text-sm font-medium text-gray-700 mb-1"
31
+ end
32
+
33
+ def input_classes
34
+ base_classes = "block w-full px-3 py-2 border rounded-md shadow-sm text-sm"
35
+ state_classes = "border-gray-300 placeholder-gray-400"
36
+ focus_classes = "focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
37
+ hover_classes = "hover:border-gray-400"
38
+ transition_classes = "transition-colors duration-200"
39
+
40
+ "#{base_classes} #{state_classes} #{focus_classes} #{hover_classes} #{transition_classes}"
41
+ end
42
+
43
+ def form_field_name
44
+ model_name = form.object.class.name.underscore
45
+ "#{model_name}[#{field_name}]"
46
+ end
47
+
48
+ def current_value
49
+ value = form.object.public_send(field_name) if form.object.respond_to?(field_name)
50
+ value.respond_to?(:strftime) ? value.strftime("%Y-%m-%dT%H:%M") : value
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,55 @@
1
+ module EasyAdmin
2
+ module Fields
3
+ module Form
4
+ class EmailComponent < BaseComponent
5
+ def view_template
6
+ div(class: "mb-4") do
7
+ label(for: field_id, class: label_classes) do
8
+ plain field_label
9
+ if required?
10
+ span(class: "text-red-500 ml-1") { "*" }
11
+ end
12
+ end
13
+ input(
14
+ type: "email",
15
+ name: form_field_name,
16
+ id: field_id,
17
+ value: current_value,
18
+ class: input_classes,
19
+ required: required?,
20
+ placeholder: field[:placeholder] || "Enter #{field_label.downcase}"
21
+ )
22
+ if field[:help_text]
23
+ p(class: "mt-1 text-sm text-gray-500") { field[:help_text] }
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def label_classes
31
+ "block text-sm font-medium text-gray-700 mb-1"
32
+ end
33
+
34
+ def input_classes
35
+ base_classes = "block w-full px-3 py-2 border rounded-md shadow-sm text-sm"
36
+ state_classes = "border-gray-300 placeholder-gray-400"
37
+ focus_classes = "focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
38
+ hover_classes = "hover:border-gray-400"
39
+ transition_classes = "transition-colors duration-200"
40
+
41
+ "#{base_classes} #{state_classes} #{focus_classes} #{hover_classes} #{transition_classes}"
42
+ end
43
+
44
+ def form_field_name
45
+ model_name = form.object.class.name.underscore
46
+ "#{model_name}[#{field_name}]"
47
+ end
48
+
49
+ def current_value
50
+ form.object.public_send(field_name) if form.object.respond_to?(field_name)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,190 @@
1
+ module EasyAdmin
2
+ module Fields
3
+ module Form
4
+ class FileComponent < BaseComponent
5
+ private
6
+
7
+ def view_template
8
+ div(class: "mb-6", data: { controller: "file" }) do
9
+ render_label if field_label
10
+ render_file_input
11
+ render_preview_area
12
+ render_help_text if field[:help_text]
13
+ render_current_file if current_file_exists?
14
+ end
15
+ end
16
+
17
+ def render_label
18
+ label(for: field_id, class: "block text-sm font-medium text-gray-700 mb-2") do
19
+ plain field_label
20
+ if required?
21
+ span(class: "text-red-500 ml-1") { "*" }
22
+ end
23
+ end
24
+ end
25
+
26
+ def render_file_input
27
+ input(
28
+ type: "file",
29
+ id: field_id,
30
+ name: form_field_name,
31
+ class: css_classes("block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 file:me-4 file:py-2 file:px-4 file:rounded-s-lg file:border-0 file:text-sm file:font-medium file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100", size_class, validation_classes),
32
+ data: { file_target: "input" },
33
+ **file_input_attributes
34
+ )
35
+ end
36
+
37
+ def render_preview_area
38
+ div(data: { file_target: "preview" }, class: "mt-2")
39
+ end
40
+
41
+ def render_help_text
42
+ div(class: "mt-1 text-sm text-gray-500") do
43
+ plain field[:help_text]
44
+ end
45
+ end
46
+
47
+ def render_current_file
48
+ if current_file_url
49
+ div(class: "mt-4", data: { file_target: "currentFile" }) do
50
+ div(class: "flex items-center p-4 bg-gray-50 border border-gray-200 rounded-lg") do
51
+ div(class: "mr-4") do
52
+ if image_file?
53
+ img(src: current_file_url, alt: "Current file", class: "w-16 h-16 object-cover rounded-lg border border-gray-300")
54
+ else
55
+ div(class: "flex items-center justify-center w-16 h-16 bg-blue-100 text-blue-600 rounded-lg") do
56
+ unsafe_raw %(<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
57
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
58
+ </svg>)
59
+ end
60
+ end
61
+ end
62
+ div(class: "flex-1") do
63
+ div(class: "text-sm font-medium text-gray-900") { plain current_file_name }
64
+ div(class: "text-xs text-gray-500") { plain file_size_text }
65
+ end
66
+ div(class: "flex items-center space-x-2") do
67
+ a(href: current_file_url, target: "_blank", class: "inline-flex items-center px-3 py-1.5 border border-blue-300 text-sm font-medium rounded-md text-blue-700 bg-blue-50 hover:bg-blue-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500") do
68
+ unsafe_raw %(<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
69
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
70
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
71
+ </svg>)
72
+ plain "View"
73
+ end
74
+ if field[:allow_remove] != false
75
+ button(type: "button", class: "inline-flex items-center px-3 py-1.5 border border-red-300 text-sm font-medium rounded-md text-red-700 bg-red-50 hover:bg-red-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500",
76
+ data: { action: "click->file#removeFile" }) do
77
+ unsafe_raw %(<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
78
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
79
+ </svg>)
80
+ plain "Remove"
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ def file_input_attributes
90
+ attributes = {}
91
+ attributes[:accept] = field[:accept] if field[:accept]
92
+ attributes[:multiple] = true if field[:multiple]
93
+ attributes[:disabled] = true if readonly?
94
+ attributes[:required] = true if required?
95
+ attributes
96
+ end
97
+
98
+ def form_field_name
99
+ model_name = form.object.class.name.underscore
100
+ if field[:multiple]
101
+ "#{model_name}[#{field_name}][]"
102
+ else
103
+ "#{model_name}[#{field_name}]"
104
+ end
105
+ end
106
+
107
+ def size_class
108
+ case field[:size]
109
+ when :small then "text-xs py-1.5 px-3"
110
+ when :large then "text-base py-3 px-4"
111
+ else ""
112
+ end
113
+ end
114
+
115
+ def validation_classes
116
+ return "" unless form&.object&.errors
117
+
118
+ if form.object.errors[field_name].any?
119
+ "border-red-300 focus:border-red-500 focus:ring-red-500"
120
+ else
121
+ ""
122
+ end
123
+ end
124
+
125
+ def current_file_exists?
126
+ current_value = value || (form.object.respond_to?(field_name) ? form.object.public_send(field_name) : nil)
127
+ current_value.present? && (current_value.respond_to?(:attached?) ? current_value.attached? : true)
128
+ end
129
+
130
+ def current_file_url
131
+ return nil unless current_file_exists?
132
+
133
+ current_value = value || form.object.public_send(field_name)
134
+ if current_value.respond_to?(:url)
135
+ current_value.url
136
+ elsif current_value.respond_to?(:service_url)
137
+ current_value.service_url
138
+ elsif current_value.is_a?(String)
139
+ current_value
140
+ end
141
+ rescue
142
+ nil
143
+ end
144
+
145
+ def current_file_name
146
+ return "Uploaded file" unless current_file_exists?
147
+
148
+ current_value = value || form.object.public_send(field_name)
149
+ if current_value.respond_to?(:filename)
150
+ current_value.filename.to_s
151
+ elsif current_value.respond_to?(:original_filename)
152
+ current_value.original_filename
153
+ elsif current_value.is_a?(String)
154
+ File.basename(current_value)
155
+ else
156
+ "Uploaded file"
157
+ end
158
+ end
159
+
160
+ def file_size_text
161
+ return "" unless current_file_exists?
162
+
163
+ current_value = value || form.object.public_send(field_name)
164
+ if current_value.respond_to?(:byte_size)
165
+ size = current_value.byte_size
166
+ elsif current_value.respond_to?(:size)
167
+ size = current_value.size
168
+ else
169
+ return ""
170
+ end
171
+
172
+ if size < 1024
173
+ "#{size} bytes"
174
+ elsif size < 1024 * 1024
175
+ "#{(size / 1024.0).round(1)} KB"
176
+ else
177
+ "#{(size / (1024.0 * 1024)).round(1)} MB"
178
+ end
179
+ end
180
+
181
+ def image_file?
182
+ return false unless current_file_exists?
183
+
184
+ filename = current_file_name.downcase
185
+ %w[.jpg .jpeg .png .gif .webp .svg].any? { |ext| filename.end_with?(ext) }
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end