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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +8 -0
- data/app/assets/builds/easy_admin.base.js +43505 -0
- data/app/assets/builds/easy_admin.base.js.map +7 -0
- data/app/assets/builds/easy_admin.css +6141 -0
- data/app/assets/config/easy_admin_manifest.js +1 -0
- data/app/assets/images/jsoneditor-icons.svg +749 -0
- data/app/assets/stylesheets/easy_admin/application.tailwind.css +390 -0
- data/app/components/easy_admin/base_component.rb +35 -0
- data/app/components/easy_admin/batch_action_bar_component.rb +125 -0
- data/app/components/easy_admin/batch_action_form_component.rb +124 -0
- data/app/components/easy_admin/combined_filters_component.rb +232 -0
- data/app/components/easy_admin/confirmation_modal_component.rb +61 -0
- data/app/components/easy_admin/context_menu_component.rb +161 -0
- data/app/components/easy_admin/dashboards/base_card_component.rb +152 -0
- data/app/components/easy_admin/dashboards/card_error_component.rb +23 -0
- data/app/components/easy_admin/dashboards/card_factory.rb +90 -0
- data/app/components/easy_admin/dashboards/card_stream_component.rb +22 -0
- data/app/components/easy_admin/dashboards/cards/base_card_component.rb +54 -0
- data/app/components/easy_admin/dashboards/cards/chart_card_component.rb +175 -0
- data/app/components/easy_admin/dashboards/cards/custom_card_component.rb +50 -0
- data/app/components/easy_admin/dashboards/cards/metric_card_component.rb +164 -0
- data/app/components/easy_admin/dashboards/cards/table_card_component.rb +148 -0
- data/app/components/easy_admin/dashboards/chart_card_component.rb +44 -0
- data/app/components/easy_admin/dashboards/metric_card_component.rb +56 -0
- data/app/components/easy_admin/dashboards/refresh_stream_component.rb +279 -0
- data/app/components/easy_admin/dashboards/show_component.rb +163 -0
- data/app/components/easy_admin/dashboards/table_card_component.rb +52 -0
- data/app/components/easy_admin/date_picker_component.rb +188 -0
- data/app/components/easy_admin/fields/base_component.rb +101 -0
- data/app/components/easy_admin/fields/belongs_to_edit_modal_component.rb +117 -0
- data/app/components/easy_admin/fields/form/belongs_to_component.rb +82 -0
- data/app/components/easy_admin/fields/form/boolean_component.rb +100 -0
- data/app/components/easy_admin/fields/form/date_component.rb +55 -0
- data/app/components/easy_admin/fields/form/datetime_component.rb +55 -0
- data/app/components/easy_admin/fields/form/email_component.rb +55 -0
- data/app/components/easy_admin/fields/form/file_component.rb +190 -0
- data/app/components/easy_admin/fields/form/has_many_component.rb +416 -0
- data/app/components/easy_admin/fields/form/json_component.rb +81 -0
- data/app/components/easy_admin/fields/form/number_component.rb +55 -0
- data/app/components/easy_admin/fields/form/select_component.rb +326 -0
- data/app/components/easy_admin/fields/form/text_component.rb +55 -0
- data/app/components/easy_admin/fields/form/textarea_component.rb +54 -0
- data/app/components/easy_admin/fields/index/belongs_to_component.rb +93 -0
- data/app/components/easy_admin/fields/index/boolean_component.rb +29 -0
- data/app/components/easy_admin/fields/index/date_component.rb +13 -0
- data/app/components/easy_admin/fields/index/datetime_component.rb +13 -0
- data/app/components/easy_admin/fields/index/email_component.rb +24 -0
- data/app/components/easy_admin/fields/index/filters/base_component.rb +48 -0
- data/app/components/easy_admin/fields/index/filters/boolean_component.rb +96 -0
- data/app/components/easy_admin/fields/index/filters/date_component.rb +182 -0
- data/app/components/easy_admin/fields/index/filters/number_component.rb +30 -0
- data/app/components/easy_admin/fields/index/filters/select_component.rb +101 -0
- data/app/components/easy_admin/fields/index/filters/string_component.rb +32 -0
- data/app/components/easy_admin/fields/index/json_component.rb +23 -0
- data/app/components/easy_admin/fields/index/number_component.rb +20 -0
- data/app/components/easy_admin/fields/index/select_component.rb +25 -0
- data/app/components/easy_admin/fields/index/text_component.rb +20 -0
- data/app/components/easy_admin/fields/inline_edit_modal_component.rb +135 -0
- data/app/components/easy_admin/fields/inline_edit_trigger_component.rb +144 -0
- data/app/components/easy_admin/fields/show/belongs_to_component.rb +93 -0
- data/app/components/easy_admin/fields/show/boolean_component.rb +21 -0
- data/app/components/easy_admin/fields/show/date_component.rb +13 -0
- data/app/components/easy_admin/fields/show/datetime_component.rb +13 -0
- data/app/components/easy_admin/fields/show/email_component.rb +19 -0
- data/app/components/easy_admin/fields/show/file_component.rb +304 -0
- data/app/components/easy_admin/fields/show/has_many_component.rb +192 -0
- data/app/components/easy_admin/fields/show/json_component.rb +45 -0
- data/app/components/easy_admin/fields/show/number_component.rb +20 -0
- data/app/components/easy_admin/fields/show/select_component.rb +25 -0
- data/app/components/easy_admin/fields/show/text_component.rb +17 -0
- data/app/components/easy_admin/fields/show/textarea_component.rb +26 -0
- data/app/components/easy_admin/filters_component.rb +120 -0
- data/app/components/easy_admin/form_tabs_component.rb +166 -0
- data/app/components/easy_admin/infinite_scroll_component.rb +82 -0
- data/app/components/easy_admin/lazy_chart_card_component.rb +128 -0
- data/app/components/easy_admin/lazy_metric_card_component.rb +76 -0
- data/app/components/easy_admin/modal_frame_component.rb +26 -0
- data/app/components/easy_admin/navbar_component.rb +226 -0
- data/app/components/easy_admin/notification_component.rb +83 -0
- data/app/components/easy_admin/pagination_component.rb +188 -0
- data/app/components/easy_admin/quick_filters_component.rb +65 -0
- data/app/components/easy_admin/resource_pagination_component.rb +14 -0
- data/app/components/easy_admin/resources/index_component.rb +211 -0
- data/app/components/easy_admin/resources/index_frame_component.rb +88 -0
- data/app/components/easy_admin/resources/show_page_actions_component.rb +324 -0
- data/app/components/easy_admin/resources/table_cell_component.rb +145 -0
- data/app/components/easy_admin/resources/table_component.rb +206 -0
- data/app/components/easy_admin/resources/table_row_component.rb +160 -0
- data/app/components/easy_admin/row_action_form_component.rb +127 -0
- data/app/components/easy_admin/scopes_component.rb +224 -0
- data/app/components/easy_admin/settings_sidebar_component.rb +140 -0
- data/app/components/easy_admin/show_layout_component.rb +600 -0
- data/app/components/easy_admin/sidebar_component.rb +174 -0
- data/app/components/easy_admin/turbo/response_component.rb +40 -0
- data/app/components/easy_admin/turbo/stream_component.rb +28 -0
- data/app/controllers/easy_admin/application_controller.rb +66 -0
- data/app/controllers/easy_admin/batch_actions_controller.rb +166 -0
- data/app/controllers/easy_admin/confirmation_modal_controller.rb +20 -0
- data/app/controllers/easy_admin/dashboard_controller.rb +6 -0
- data/app/controllers/easy_admin/dashboards_controller.rb +123 -0
- data/app/controllers/easy_admin/passwords_controller.rb +15 -0
- data/app/controllers/easy_admin/registrations_controller.rb +52 -0
- data/app/controllers/easy_admin/resources_controller.rb +907 -0
- data/app/controllers/easy_admin/row_actions_controller.rb +216 -0
- data/app/controllers/easy_admin/sessions_controller.rb +32 -0
- data/app/controllers/easy_admin/settings_controller.rb +94 -0
- data/app/helpers/easy_admin/application_helper.rb +4 -0
- data/app/helpers/easy_admin/dashboards_helper.rb +121 -0
- data/app/helpers/easy_admin/fields_helper.rb +27 -0
- data/app/helpers/easy_admin/pagy_helper.rb +30 -0
- data/app/helpers/easy_admin/resources_helper.rb +39 -0
- data/app/javascript/easy_admin/application.js +12 -0
- data/app/javascript/easy_admin/controllers/batch_modal_controller.js +66 -0
- data/app/javascript/easy_admin/controllers/batch_selection_controller.js +223 -0
- data/app/javascript/easy_admin/controllers/chart_controller.js +216 -0
- data/app/javascript/easy_admin/controllers/collapsible_filters_controller.js +118 -0
- data/app/javascript/easy_admin/controllers/confirmation_modal_controller.js +64 -0
- data/app/javascript/easy_admin/controllers/context_menu_controller.js +227 -0
- data/app/javascript/easy_admin/controllers/date_picker_controller.js +309 -0
- data/app/javascript/easy_admin/controllers/dropdown_controller.js +63 -0
- data/app/javascript/easy_admin/controllers/event_emitter_controller.js +19 -0
- data/app/javascript/easy_admin/controllers/file_controller.js +121 -0
- data/app/javascript/easy_admin/controllers/form_tabs_controller.js +100 -0
- data/app/javascript/easy_admin/controllers/has_many_search_controller.js +76 -0
- data/app/javascript/easy_admin/controllers/infinite_scroll_controller.js +174 -0
- data/app/javascript/easy_admin/controllers/ios_alert_controller.js +195 -0
- data/app/javascript/easy_admin/controllers/jsoneditor_controller.js +88 -0
- data/app/javascript/easy_admin/controllers/modal_controller.js +75 -0
- data/app/javascript/easy_admin/controllers/navbar_scroll_controller.js +76 -0
- data/app/javascript/easy_admin/controllers/notification_controller.js +48 -0
- data/app/javascript/easy_admin/controllers/row_action_controller.js +124 -0
- data/app/javascript/easy_admin/controllers/row_modal_controller.js +59 -0
- data/app/javascript/easy_admin/controllers/select_field_controller.js +618 -0
- data/app/javascript/easy_admin/controllers/settings_button_controller.js +8 -0
- data/app/javascript/easy_admin/controllers/settings_sidebar_controller.js +186 -0
- data/app/javascript/easy_admin/controllers/sidebar_controller.js +102 -0
- data/app/javascript/easy_admin/controllers/sidebar_mobile_controller.js +23 -0
- data/app/javascript/easy_admin/controllers/sidebar_nav_controller.js +96 -0
- data/app/javascript/easy_admin/controllers/table_controller.js +28 -0
- data/app/javascript/easy_admin/controllers/table_row_controller.js +16 -0
- data/app/javascript/easy_admin/controllers/toggle_switch_controller.js +22 -0
- data/app/javascript/easy_admin/controllers/turbo_stream_redirect.js +9 -0
- data/app/javascript/easy_admin/controllers.js +54 -0
- data/app/javascript/easy_admin.base.js +4 -0
- data/app/models/easy_admin/admin_user.rb +53 -0
- data/app/models/easy_admin/application_record.rb +5 -0
- data/app/views/easy_admin/dashboard/index.html.erb +3 -0
- data/app/views/easy_admin/dashboards/show.html.erb +7 -0
- data/app/views/easy_admin/passwords/edit.html.erb +42 -0
- data/app/views/easy_admin/passwords/new.html.erb +41 -0
- data/app/views/easy_admin/registrations/new.html.erb +65 -0
- data/app/views/easy_admin/resources/_redirect.turbo_stream.erb +3 -0
- data/app/views/easy_admin/resources/_table_rows.html.erb +46 -0
- data/app/views/easy_admin/resources/edit.html.erb +151 -0
- data/app/views/easy_admin/resources/index.html.erb +12 -0
- data/app/views/easy_admin/resources/index.turbo_stream.erb +139 -0
- data/app/views/easy_admin/resources/index_frame.html.erb +142 -0
- data/app/views/easy_admin/resources/new.html.erb +100 -0
- data/app/views/easy_admin/resources/show.html.erb +31 -0
- data/app/views/easy_admin/sessions/new.html.erb +55 -0
- data/app/views/easy_admin/settings/_form.html.erb +51 -0
- data/app/views/easy_admin/settings/index.html.erb +53 -0
- data/app/views/layouts/easy_admin/application.html.erb +48 -0
- data/app/views/layouts/easy_admin/auth.html.erb +34 -0
- data/config/initializers/easy_admin_card_factory.rb +27 -0
- data/config/initializers/pagy.rb +15 -0
- data/config/initializers/rack_mini_profiler.rb +67 -0
- data/config/routes.rb +70 -0
- data/db/migrate/20250101000001_create_easy_admin_admin_users.rb +45 -0
- data/lib/easy-admin.rb +32 -0
- data/lib/easy_admin/action.rb +159 -0
- data/lib/easy_admin/batch_action.rb +134 -0
- data/lib/easy_admin/configuration.rb +75 -0
- data/lib/easy_admin/dashboard.rb +110 -0
- data/lib/easy_admin/dashboard_registry.rb +30 -0
- data/lib/easy_admin/delete_action.rb +22 -0
- data/lib/easy_admin/engine.rb +54 -0
- data/lib/easy_admin/field.rb +118 -0
- data/lib/easy_admin/resource.rb +806 -0
- data/lib/easy_admin/resource_registry.rb +22 -0
- data/lib/easy_admin/types/json_type.rb +25 -0
- data/lib/easy_admin/version.rb +3 -0
- data/lib/generators/easy_admin/auth_generator.rb +69 -0
- data/lib/generators/easy_admin/card/card_generator.rb +94 -0
- data/lib/generators/easy_admin/card/templates/card_component.rb.erb +127 -0
- data/lib/generators/easy_admin/card/templates/card_component_spec.rb.erb +122 -0
- data/lib/generators/easy_admin/install/templates/easy_admin.rb +31 -0
- data/lib/generators/easy_admin/install_generator.rb +25 -0
- data/lib/generators/easy_admin/rbac/rbac_generator.rb +244 -0
- data/lib/generators/easy_admin/rbac/templates/add_rbac_to_admin_users.rb +23 -0
- data/lib/generators/easy_admin/rbac/templates/super_admin.rb +34 -0
- data/lib/generators/easy_admin/resource_generator.rb +43 -0
- data/lib/generators/easy_admin/templates/AUTH_README +35 -0
- data/lib/generators/easy_admin/templates/README +27 -0
- data/lib/generators/easy_admin/templates/create_easy_admin_admin_users.rb +45 -0
- data/lib/generators/easy_admin/templates/devise.rb +267 -0
- data/lib/generators/easy_admin/templates/easy_admin.rb +24 -0
- data/lib/generators/easy_admin/templates/resource.rb +29 -0
- data/lib/tasks/easy_admin_tasks.rake +4 -0
- metadata +445 -0
@@ -0,0 +1,390 @@
|
|
1
|
+
@import "jsoneditor/dist/jsoneditor.css";
|
2
|
+
|
3
|
+
@tailwind base;
|
4
|
+
@tailwind components;
|
5
|
+
@tailwind utilities;
|
6
|
+
|
7
|
+
/* Custom EasyAdmin component styles using Tailwind */
|
8
|
+
@layer components {
|
9
|
+
/* Sidebar Collapsed State Styles */
|
10
|
+
.sidebar--collapsed .sidebar-logo-container {
|
11
|
+
@apply justify-center w-full;
|
12
|
+
}
|
13
|
+
|
14
|
+
.sidebar--collapsed .sidebar-app-name {
|
15
|
+
@apply hidden;
|
16
|
+
}
|
17
|
+
|
18
|
+
.sidebar--collapsed .sidebar-logo {
|
19
|
+
@apply w-10 h-10;
|
20
|
+
}
|
21
|
+
|
22
|
+
/* Select Field Component Styles */
|
23
|
+
.select-dropdown {
|
24
|
+
@apply absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-auto opacity-0 invisible transform scale-95 transition-all duration-200 ease-out;
|
25
|
+
}
|
26
|
+
|
27
|
+
.select-dropdown.show {
|
28
|
+
@apply opacity-100 visible scale-100;
|
29
|
+
}
|
30
|
+
|
31
|
+
.select-option {
|
32
|
+
@apply px-3 py-2 text-sm text-gray-900 cursor-pointer hover:bg-gray-50 transition-colors duration-150;
|
33
|
+
}
|
34
|
+
|
35
|
+
.select-option:hover {
|
36
|
+
@apply bg-blue-50 text-blue-900;
|
37
|
+
}
|
38
|
+
|
39
|
+
.selected-item {
|
40
|
+
@apply inline-flex items-center px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded cursor-pointer hover:bg-blue-200 transition-colors duration-150;
|
41
|
+
}
|
42
|
+
|
43
|
+
.remove-item {
|
44
|
+
@apply ml-1 text-blue-600 hover:text-blue-800 font-medium;
|
45
|
+
}
|
46
|
+
|
47
|
+
.select-clear {
|
48
|
+
@apply absolute top-2 right-2 p-1 text-gray-400 hover:text-gray-600 bg-white rounded transition-colors duration-150 z-10;
|
49
|
+
}
|
50
|
+
|
51
|
+
.select-no-results {
|
52
|
+
@apply px-3 py-2 text-sm text-gray-500 text-center;
|
53
|
+
}
|
54
|
+
|
55
|
+
/* Toggle Switch Styles */
|
56
|
+
.toggle-switch {
|
57
|
+
@apply relative inline-block w-11 h-6;
|
58
|
+
}
|
59
|
+
|
60
|
+
.toggle-switch input {
|
61
|
+
@apply opacity-0 w-0 h-0;
|
62
|
+
}
|
63
|
+
|
64
|
+
.toggle-slider {
|
65
|
+
@apply absolute cursor-pointer top-0 left-0 right-0 bottom-0 bg-gray-300 rounded-full transition-all duration-300;
|
66
|
+
}
|
67
|
+
|
68
|
+
.toggle-slider:before {
|
69
|
+
@apply absolute content-[''] h-5 w-5 left-0.5 bottom-0.5 bg-white rounded-full transition-all duration-300;
|
70
|
+
}
|
71
|
+
|
72
|
+
input:checked + .toggle-switch .toggle-slider {
|
73
|
+
@apply bg-blue-600;
|
74
|
+
}
|
75
|
+
|
76
|
+
input:focus + .toggle-switch .toggle-slider {
|
77
|
+
@apply ring-4 ring-blue-300 ring-opacity-50;
|
78
|
+
}
|
79
|
+
|
80
|
+
input:checked + .toggle-switch .toggle-slider:before {
|
81
|
+
@apply transform translate-x-5;
|
82
|
+
}
|
83
|
+
|
84
|
+
/* Animation utilities */
|
85
|
+
@keyframes fadeIn {
|
86
|
+
from {
|
87
|
+
opacity: 0;
|
88
|
+
transform: translateY(-4px);
|
89
|
+
}
|
90
|
+
to {
|
91
|
+
opacity: 1;
|
92
|
+
transform: translateY(0);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
.animate-fadeIn {
|
97
|
+
animation: fadeIn 0.15s ease-out;
|
98
|
+
}
|
99
|
+
|
100
|
+
/* Sidebar Styles */
|
101
|
+
.sidebar-overlay--visible {
|
102
|
+
@apply opacity-100 pointer-events-auto;
|
103
|
+
}
|
104
|
+
|
105
|
+
.sidebar--open {
|
106
|
+
@apply translate-x-0;
|
107
|
+
}
|
108
|
+
|
109
|
+
.sidebar--collapsed {
|
110
|
+
@apply -translate-x-full lg:translate-x-0;
|
111
|
+
}
|
112
|
+
|
113
|
+
/* Table Styles */
|
114
|
+
.table-striped tbody tr:nth-child(even) {
|
115
|
+
@apply bg-gray-50;
|
116
|
+
}
|
117
|
+
|
118
|
+
.table-striped tbody tr:hover {
|
119
|
+
@apply bg-gray-100 transition-colors duration-150;
|
120
|
+
}
|
121
|
+
|
122
|
+
/* Mobile table responsive behavior */
|
123
|
+
@media (max-width: 640px) {
|
124
|
+
.mobile-table-wrapper {
|
125
|
+
@apply -mx-6;
|
126
|
+
}
|
127
|
+
|
128
|
+
.mobile-table-wrapper table {
|
129
|
+
@apply min-w-full;
|
130
|
+
}
|
131
|
+
|
132
|
+
.mobile-table-wrapper th,
|
133
|
+
.mobile-table-wrapper td {
|
134
|
+
@apply px-3 py-2;
|
135
|
+
}
|
136
|
+
|
137
|
+
/* Hide less important columns on mobile */
|
138
|
+
.mobile-hide {
|
139
|
+
@apply hidden;
|
140
|
+
}
|
141
|
+
|
142
|
+
/* Stack action buttons vertically on very small screens */
|
143
|
+
.mobile-actions {
|
144
|
+
@apply flex flex-col space-y-1 space-x-0;
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
/* Responsive sidebar behavior */
|
149
|
+
@media (min-width: 1024px) {
|
150
|
+
.sidebar--collapsed .sidebar {
|
151
|
+
@apply w-16;
|
152
|
+
}
|
153
|
+
|
154
|
+
/* Hide all text content in collapsed sidebar */
|
155
|
+
.sidebar--collapsed .sidebar .truncate {
|
156
|
+
@apply hidden;
|
157
|
+
}
|
158
|
+
|
159
|
+
.sidebar--collapsed .sidebar h2 {
|
160
|
+
@apply hidden;
|
161
|
+
}
|
162
|
+
|
163
|
+
/* Center the logo in collapsed state */
|
164
|
+
.sidebar--collapsed .sidebar header > div {
|
165
|
+
@apply justify-center;
|
166
|
+
}
|
167
|
+
|
168
|
+
/* Hide only the submenu containers, not the parent items */
|
169
|
+
.sidebar--collapsed .sidebar [data-sidebar-nav-target="submenu"] {
|
170
|
+
@apply hidden;
|
171
|
+
}
|
172
|
+
|
173
|
+
/* Adjust active item background to not show border-r */
|
174
|
+
.sidebar--collapsed .sidebar .bg-blue-50,
|
175
|
+
.sidebar--collapsed .sidebar .border-r-2 {
|
176
|
+
@apply border-r-0;
|
177
|
+
}
|
178
|
+
|
179
|
+
/* Center all navigation items in collapsed sidebar */
|
180
|
+
.sidebar--collapsed .sidebar nav a,
|
181
|
+
.sidebar--collapsed .sidebar nav button {
|
182
|
+
@apply justify-center px-0 w-full;
|
183
|
+
}
|
184
|
+
|
185
|
+
/* Ensure parent expandable buttons are also centered */
|
186
|
+
.sidebar--collapsed .sidebar nav button.group {
|
187
|
+
@apply justify-center;
|
188
|
+
}
|
189
|
+
|
190
|
+
/* Hide chevrons in collapsed sidebar */
|
191
|
+
.sidebar--collapsed .sidebar [data-sidebar-nav-target="chevron"] {
|
192
|
+
@apply hidden;
|
193
|
+
}
|
194
|
+
|
195
|
+
/* Remove horizontal padding from nav in collapsed state */
|
196
|
+
.sidebar--collapsed .sidebar nav {
|
197
|
+
@apply px-2;
|
198
|
+
}
|
199
|
+
|
200
|
+
/* Center the icon containers and show only icons in collapsed state */
|
201
|
+
.sidebar--collapsed .sidebar .flex-shrink-0 {
|
202
|
+
@apply mr-0;
|
203
|
+
}
|
204
|
+
|
205
|
+
/* Make icons centered and properly sized when sidebar is collapsed */
|
206
|
+
.sidebar--collapsed .sidebar nav a span.flex-shrink-0,
|
207
|
+
.sidebar--collapsed .sidebar nav button span.flex-shrink-0 {
|
208
|
+
@apply mr-0 text-xl w-8 h-8;
|
209
|
+
}
|
210
|
+
|
211
|
+
/* Adjust main content and navbar when sidebar is collapsed */
|
212
|
+
.sidebar--collapsed main {
|
213
|
+
@apply ml-16;
|
214
|
+
}
|
215
|
+
|
216
|
+
.sidebar--collapsed header {
|
217
|
+
@apply ml-16;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
/* iOS-style Card Components */
|
222
|
+
.card-base {
|
223
|
+
@apply bg-white rounded-2xl shadow-sm border border-gray-100 h-full mb-4 md:mb-0 overflow-hidden backdrop-blur-sm;
|
224
|
+
}
|
225
|
+
|
226
|
+
.card-primary {
|
227
|
+
@apply bg-blue-50/30;
|
228
|
+
}
|
229
|
+
|
230
|
+
.card-secondary {
|
231
|
+
@apply bg-gray-50;
|
232
|
+
}
|
233
|
+
|
234
|
+
.card-success {
|
235
|
+
@apply bg-green-50/30;
|
236
|
+
}
|
237
|
+
|
238
|
+
.card-danger {
|
239
|
+
@apply bg-red-50/30;
|
240
|
+
}
|
241
|
+
|
242
|
+
.card-warning {
|
243
|
+
@apply bg-yellow-50/30;
|
244
|
+
}
|
245
|
+
|
246
|
+
.card-info {
|
247
|
+
@apply bg-cyan-50/30;
|
248
|
+
}
|
249
|
+
|
250
|
+
.card-dark {
|
251
|
+
@apply bg-gray-100;
|
252
|
+
}
|
253
|
+
|
254
|
+
.card-light {
|
255
|
+
@apply bg-white;
|
256
|
+
}
|
257
|
+
|
258
|
+
/* Metric Card Styles */
|
259
|
+
.metric-card-base {
|
260
|
+
@apply rounded-2xl shadow-sm border border-gray-100 h-full mb-4 md:mb-0 overflow-hidden backdrop-blur-sm;
|
261
|
+
}
|
262
|
+
|
263
|
+
.metric-card-primary {
|
264
|
+
@apply bg-blue-500 text-white;
|
265
|
+
}
|
266
|
+
|
267
|
+
.metric-card-secondary {
|
268
|
+
@apply bg-gray-500 text-white;
|
269
|
+
}
|
270
|
+
|
271
|
+
.metric-card-success {
|
272
|
+
@apply bg-green-500 text-white;
|
273
|
+
}
|
274
|
+
|
275
|
+
.metric-card-danger {
|
276
|
+
@apply bg-red-500 text-white;
|
277
|
+
}
|
278
|
+
|
279
|
+
.metric-card-warning {
|
280
|
+
@apply bg-orange-500 text-white;
|
281
|
+
}
|
282
|
+
|
283
|
+
.metric-card-info {
|
284
|
+
@apply bg-blue-400 text-white;
|
285
|
+
}
|
286
|
+
|
287
|
+
.metric-card-dark {
|
288
|
+
@apply bg-gray-800 text-white;
|
289
|
+
}
|
290
|
+
|
291
|
+
.metric-card-light {
|
292
|
+
@apply bg-gray-100 text-gray-900;
|
293
|
+
}
|
294
|
+
|
295
|
+
/* Context Menu Styles */
|
296
|
+
.context-menu {
|
297
|
+
@apply bg-white rounded-lg shadow-lg border border-gray-200 py-1 min-w-48 opacity-0 scale-95 transition-all duration-200 ease-out;
|
298
|
+
}
|
299
|
+
|
300
|
+
.context-menu.visible {
|
301
|
+
@apply opacity-100 scale-100;
|
302
|
+
}
|
303
|
+
|
304
|
+
.context-menu.hiding {
|
305
|
+
@apply opacity-0 scale-95;
|
306
|
+
}
|
307
|
+
|
308
|
+
.context-menu-item {
|
309
|
+
@apply px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer transition-colors duration-150;
|
310
|
+
}
|
311
|
+
|
312
|
+
.context-menu-item:hover {
|
313
|
+
@apply bg-gray-100 text-gray-900;
|
314
|
+
}
|
315
|
+
|
316
|
+
.context-menu-item.danger {
|
317
|
+
@apply text-red-600;
|
318
|
+
}
|
319
|
+
|
320
|
+
.context-menu-item.danger:hover {
|
321
|
+
@apply bg-red-50 text-red-700;
|
322
|
+
}
|
323
|
+
|
324
|
+
.context-menu-divider {
|
325
|
+
@apply border-t border-gray-200 my-1;
|
326
|
+
}
|
327
|
+
|
328
|
+
/* Pagination Styles */
|
329
|
+
.pagination-container {
|
330
|
+
@apply bg-white border-t border-gray-200;
|
331
|
+
}
|
332
|
+
|
333
|
+
.pagination-mobile {
|
334
|
+
@apply flex flex-1 justify-between items-center px-4 py-3;
|
335
|
+
}
|
336
|
+
|
337
|
+
.pagination-desktop {
|
338
|
+
@apply hidden sm:flex sm:flex-1 sm:items-center sm:justify-between px-6 py-3;
|
339
|
+
}
|
340
|
+
|
341
|
+
.pagination-button {
|
342
|
+
@apply relative inline-flex items-center justify-center w-10 h-10 rounded-lg font-medium transition-all duration-200;
|
343
|
+
}
|
344
|
+
|
345
|
+
.pagination-button--active {
|
346
|
+
@apply bg-blue-600 text-white border border-blue-600 shadow-sm;
|
347
|
+
}
|
348
|
+
|
349
|
+
.pagination-button--inactive {
|
350
|
+
@apply bg-white text-gray-700 border border-gray-300 hover:bg-blue-50 hover:text-blue-700 hover:border-blue-300;
|
351
|
+
}
|
352
|
+
|
353
|
+
.pagination-button--disabled {
|
354
|
+
@apply bg-gray-100 text-gray-400 border border-gray-300 cursor-not-allowed;
|
355
|
+
}
|
356
|
+
|
357
|
+
.pagination-nav-button {
|
358
|
+
@apply relative inline-flex items-center justify-center w-10 h-10 rounded-lg text-gray-500 border border-gray-300 transition-all duration-200;
|
359
|
+
}
|
360
|
+
|
361
|
+
.pagination-nav-button:hover:not(.pagination-nav-button--disabled) {
|
362
|
+
@apply bg-gray-50 text-gray-700;
|
363
|
+
}
|
364
|
+
|
365
|
+
.pagination-nav-button--disabled {
|
366
|
+
@apply bg-gray-100 text-gray-400 cursor-not-allowed;
|
367
|
+
}
|
368
|
+
|
369
|
+
.pagination-info {
|
370
|
+
@apply text-sm text-gray-700;
|
371
|
+
}
|
372
|
+
|
373
|
+
.pagination-stats {
|
374
|
+
@apply text-sm text-gray-700;
|
375
|
+
}
|
376
|
+
|
377
|
+
/* Mobile-specific pagination adjustments */
|
378
|
+
@media (max-width: 640px) {
|
379
|
+
.pagination-container {
|
380
|
+
@apply px-4 py-3;
|
381
|
+
}
|
382
|
+
|
383
|
+
.pagination-gap {
|
384
|
+
@apply hidden;
|
385
|
+
}
|
386
|
+
}
|
387
|
+
.jsoneditor-menu > button, .jsoneditor-menu > button {
|
388
|
+
background-image: url('/assets/jsoneditor-icons.svg') !important;
|
389
|
+
}
|
390
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
class BaseComponent < Phlex::HTML
|
3
|
+
# Register Turbo custom elements
|
4
|
+
register_element :turbo_frame
|
5
|
+
register_element :turbo_stream
|
6
|
+
register_element :template
|
7
|
+
|
8
|
+
# Include Rails helpers for form building, URL generation, and text manipulation
|
9
|
+
include ActionView::Helpers::DateHelper
|
10
|
+
include ActionView::Helpers::TextHelper
|
11
|
+
include ActionView::Helpers::UrlHelper
|
12
|
+
include ActionView::Helpers::NumberHelper
|
13
|
+
include EasyAdmin::DashboardsHelper
|
14
|
+
include EasyAdmin::FieldsHelper
|
15
|
+
|
16
|
+
# Add method to access all Rails helpers if needed
|
17
|
+
def helpers
|
18
|
+
@helpers ||= Class.new do
|
19
|
+
include ActionView::Helpers
|
20
|
+
include Rails.application.routes.url_helpers
|
21
|
+
include EasyAdmin::Engine.routes.url_helpers
|
22
|
+
end.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# Direct access to EasyAdmin URL helpers
|
26
|
+
def easy_admin_url_helpers
|
27
|
+
@easy_admin_url_helpers ||= EasyAdmin::Engine.routes.url_helpers
|
28
|
+
end
|
29
|
+
|
30
|
+
# Direct access to Rails URL helpers
|
31
|
+
def rails_url_helpers
|
32
|
+
@rails_url_helpers ||= Rails.application.routes.url_helpers
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
class BatchActionBarComponent < BaseComponent
|
3
|
+
def initialize(resource_class:, current_user: nil)
|
4
|
+
@resource_class = resource_class
|
5
|
+
@current_user = current_user
|
6
|
+
end
|
7
|
+
|
8
|
+
def view_template
|
9
|
+
# iOS-style floating action bar
|
10
|
+
div(
|
11
|
+
class: "fixed bottom-0 inset-x-0 pb-safe z-50 pointer-events-none",
|
12
|
+
data: { batch_selection_target: "actionBar" }
|
13
|
+
) do
|
14
|
+
div(class: "px-4 pb-4 pointer-events-auto") do
|
15
|
+
div(
|
16
|
+
class: "bg-white/95 backdrop-blur-xl rounded-2xl shadow-2xl border border-gray-200/50 transform translate-y-full transition-all duration-300 ease-out opacity-0",
|
17
|
+
style: "transform: translateY(100%); opacity: 0;",
|
18
|
+
data: { batch_selection_target: "actionBarContent" }
|
19
|
+
) do
|
20
|
+
# Top section with selection count and cancel
|
21
|
+
div(class: "flex items-center justify-between px-5 py-3 border-b border-gray-200/50") do
|
22
|
+
div(class: "flex items-center space-x-3") do
|
23
|
+
# Selection indicator with animated background
|
24
|
+
div(class: "relative") do
|
25
|
+
div(class: "absolute inset-0 bg-blue-500 rounded-full animate-pulse opacity-20")
|
26
|
+
div(class: "relative bg-blue-500 text-white text-sm font-semibold rounded-full px-3 py-1.5 min-w-[2rem] text-center") do
|
27
|
+
span(data: { batch_selection_target: "counter" }) { "0" }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
span(class: "text-sm font-medium text-gray-700") do
|
32
|
+
"selected"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Cancel button
|
37
|
+
button(
|
38
|
+
type: "button",
|
39
|
+
class: "text-sm font-medium text-gray-500 hover:text-gray-700 transition-colors",
|
40
|
+
data: { action: "click->batch-selection#clearSelection" }
|
41
|
+
) do
|
42
|
+
"Cancel"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Actions section
|
47
|
+
div(class: "p-3") do
|
48
|
+
div(class: "flex flex-col sm:flex-row sm:flex-wrap gap-2") do
|
49
|
+
available_batch_actions.each do |action_class|
|
50
|
+
render_ios_action_button(action_class)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def available_batch_actions
|
62
|
+
context = OpenStruct.new(current_user: @current_user)
|
63
|
+
@resource_class.available_batch_actions(context)
|
64
|
+
end
|
65
|
+
|
66
|
+
def render_ios_action_button(action_class)
|
67
|
+
button(
|
68
|
+
type: "button",
|
69
|
+
class: ios_button_classes(action_class.style),
|
70
|
+
data: {
|
71
|
+
action: "click->batch-selection#executeAction",
|
72
|
+
action_class: action_class.name,
|
73
|
+
execution_mode: action_class.execution_mode,
|
74
|
+
confirm: action_class.confirm_message
|
75
|
+
}
|
76
|
+
) do
|
77
|
+
div(class: "flex items-center justify-center space-x-2") do
|
78
|
+
if action_class.icon
|
79
|
+
render_icon(action_class.icon)
|
80
|
+
end
|
81
|
+
span(class: "font-medium") { action_class.label }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def ios_button_classes(style)
|
87
|
+
base = "w-full sm:flex-1 sm:min-w-[100px] px-4 py-2.5 rounded-xl font-medium text-sm transition-all duration-200 transform active:scale-95"
|
88
|
+
|
89
|
+
case style
|
90
|
+
when :danger
|
91
|
+
"#{base} bg-red-500 hover:bg-red-600 text-white shadow-sm hover:shadow-md"
|
92
|
+
when :secondary
|
93
|
+
"#{base} bg-gray-100 hover:bg-gray-200 text-gray-700"
|
94
|
+
else # :primary
|
95
|
+
"#{base} bg-blue-500 hover:bg-blue-600 text-white shadow-sm hover:shadow-md"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def render_icon(icon_name)
|
100
|
+
span(class: "mr-1.5") do
|
101
|
+
case icon_name
|
102
|
+
when "trash"
|
103
|
+
unsafe_raw <<~SVG
|
104
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
105
|
+
<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"/>
|
106
|
+
</svg>
|
107
|
+
SVG
|
108
|
+
when "edit"
|
109
|
+
unsafe_raw <<~SVG
|
110
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
111
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
112
|
+
</svg>
|
113
|
+
SVG
|
114
|
+
else
|
115
|
+
# Default icon
|
116
|
+
unsafe_raw <<~SVG
|
117
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
118
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"/>
|
119
|
+
</svg>
|
120
|
+
SVG
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module EasyAdmin
|
2
|
+
class BatchActionFormComponent < BaseComponent
|
3
|
+
def initialize(resource_class:, action_class:, selected_records:, selected_ids:, submit_url:)
|
4
|
+
@resource_class = resource_class
|
5
|
+
@action_class = action_class
|
6
|
+
@selected_records = selected_records
|
7
|
+
@selected_ids = selected_ids
|
8
|
+
@submit_url = submit_url
|
9
|
+
end
|
10
|
+
|
11
|
+
def view_template
|
12
|
+
div(
|
13
|
+
id: "modal",
|
14
|
+
class: "fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50",
|
15
|
+
data: {
|
16
|
+
controller: "batch-modal",
|
17
|
+
batch_modal_submit_url_value: @submit_url,
|
18
|
+
batch_modal_selected_ids_value: @selected_ids.join(','),
|
19
|
+
batch_modal_action_class_value: @action_class.name
|
20
|
+
}
|
21
|
+
) do
|
22
|
+
div(class: "relative top-20 mx-auto p-5 border w-11/12 max-w-md shadow-lg rounded-md bg-white") do
|
23
|
+
# Header
|
24
|
+
div(class: "flex items-center justify-between pb-4 mb-4 border-b border-gray-200") do
|
25
|
+
h3(class: "text-lg font-semibold text-gray-900") do
|
26
|
+
@action_class.modal_title || @action_class.label || @action_class.name.humanize
|
27
|
+
end
|
28
|
+
button(
|
29
|
+
type: "button",
|
30
|
+
class: "text-gray-400 hover:text-gray-600 text-xl",
|
31
|
+
data: { action: "click->batch-modal#close" }
|
32
|
+
) { "×" }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Description
|
36
|
+
if @action_class.modal_description.present?
|
37
|
+
p(class: "text-sm text-gray-600 mb-4") { @action_class.modal_description }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Records count
|
41
|
+
div(class: "mb-4 p-3 bg-gray-50 rounded") do
|
42
|
+
div(class: "text-sm text-gray-600") do
|
43
|
+
"#{@selected_records.count} record(s) selected"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Simple form fields
|
48
|
+
div(class: "space-y-4", data: { batch_modal_target: "form" }) do
|
49
|
+
render_simple_fields
|
50
|
+
end
|
51
|
+
|
52
|
+
# Action buttons
|
53
|
+
div(class: "flex justify-end space-x-3 pt-4 border-t border-gray-200 mt-4") do
|
54
|
+
button(
|
55
|
+
type: "button",
|
56
|
+
class: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50",
|
57
|
+
data: { action: "click->batch-modal#close" }
|
58
|
+
) { "Cancel" }
|
59
|
+
|
60
|
+
button(
|
61
|
+
type: "button",
|
62
|
+
class: "px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700",
|
63
|
+
data: { action: "click->batch-modal#submit" }
|
64
|
+
) { @action_class.label }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def render_simple_fields
|
73
|
+
@action_class.attribute_types.each do |attr_name, attr_type|
|
74
|
+
next if attr_name.in?(['selected_records', 'current_user'])
|
75
|
+
|
76
|
+
field_config = {
|
77
|
+
name: attr_name,
|
78
|
+
type: determine_field_type(attr_type),
|
79
|
+
label: attr_name.humanize,
|
80
|
+
form_name: "batch_action"
|
81
|
+
}
|
82
|
+
|
83
|
+
# Create a simple record-like object for the field component
|
84
|
+
field_record = OpenStruct.new(attr_name => nil)
|
85
|
+
|
86
|
+
# Create a mock form object
|
87
|
+
mock_form = OpenStruct.new(
|
88
|
+
object: OpenStruct.new(class: OpenStruct.new(name: OpenStruct.new(underscore: 'batch_action')))
|
89
|
+
)
|
90
|
+
|
91
|
+
component = field_component(
|
92
|
+
field_config,
|
93
|
+
action: :form,
|
94
|
+
value: nil,
|
95
|
+
record: field_record,
|
96
|
+
form: mock_form
|
97
|
+
)
|
98
|
+
|
99
|
+
render component
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def determine_field_type(attr_type)
|
104
|
+
case attr_type.type
|
105
|
+
when :string
|
106
|
+
:text
|
107
|
+
when :text
|
108
|
+
:textarea
|
109
|
+
when :boolean
|
110
|
+
:boolean
|
111
|
+
when :integer, :float
|
112
|
+
:number
|
113
|
+
when :date
|
114
|
+
:date
|
115
|
+
when :datetime
|
116
|
+
:datetime
|
117
|
+
when :json
|
118
|
+
:json
|
119
|
+
else
|
120
|
+
:text
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|