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,88 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import JSONEditor from "jsoneditor"
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static values = {
|
6
|
+
mode: String,
|
7
|
+
data: Object
|
8
|
+
}
|
9
|
+
static targets = ["hiddenField", "editor"]
|
10
|
+
|
11
|
+
connect() {
|
12
|
+
this.initializeEditor()
|
13
|
+
|
14
|
+
// Listen for form submission to ensure hidden field is updated
|
15
|
+
const form = this.element.closest('form')
|
16
|
+
if (form) {
|
17
|
+
form.addEventListener('submit', () => {
|
18
|
+
this.updateHiddenField()
|
19
|
+
})
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
disconnect() {
|
24
|
+
if (this.editor) {
|
25
|
+
this.editor.destroy()
|
26
|
+
this.editor = null
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
initializeEditor() {
|
31
|
+
const options = {
|
32
|
+
mode: this.modeValue || 'view',
|
33
|
+
modes: this.modeValue === 'code' ? ['code', 'tree', 'view'] : ['view'],
|
34
|
+
search: this.modeValue !== 'view',
|
35
|
+
history: this.modeValue === 'code',
|
36
|
+
navigationBar: this.modeValue !== 'view',
|
37
|
+
enableSort: true,
|
38
|
+
enableTransform: true,
|
39
|
+
onChange: () => {
|
40
|
+
this.updateHiddenField()
|
41
|
+
},
|
42
|
+
onModeChange: (newMode, oldMode) => {
|
43
|
+
// Update when switching between modes
|
44
|
+
setTimeout(() => this.updateHiddenField(), 100)
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
const container = this.hasEditorTarget ? this.editorTarget : this.element
|
49
|
+
this.editor = new JSONEditor(container, options)
|
50
|
+
|
51
|
+
if (this.hasDataValue) {
|
52
|
+
this.editor.set(this.dataValue)
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
updateHiddenField() {
|
57
|
+
if (this.editor && this.hasHiddenFieldTarget) {
|
58
|
+
try {
|
59
|
+
const jsonData = this.editor.get()
|
60
|
+
const jsonString = JSON.stringify(jsonData)
|
61
|
+
this.hiddenFieldTarget.value = jsonString
|
62
|
+
console.log('JSON Editor: Updated hidden field with:', jsonString)
|
63
|
+
} catch (error) {
|
64
|
+
console.warn('JSON Editor: Invalid JSON, not updating hidden field:', error)
|
65
|
+
// In case of error, try to get the text content
|
66
|
+
try {
|
67
|
+
if (this.editor.mode === 'code') {
|
68
|
+
const text = this.editor.getText()
|
69
|
+
this.hiddenFieldTarget.value = text
|
70
|
+
console.log('JSON Editor: Updated hidden field with raw text:', text)
|
71
|
+
}
|
72
|
+
} catch (textError) {
|
73
|
+
console.warn('JSON Editor: Could not get text content:', textError)
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
getData() {
|
80
|
+
return this.editor ? this.editor.get() : null
|
81
|
+
}
|
82
|
+
|
83
|
+
setData(data) {
|
84
|
+
if (this.editor) {
|
85
|
+
this.editor.set(data)
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["backdrop", "content"]
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
// Show modal when turbo-frame loads content
|
8
|
+
this.element.addEventListener("turbo:frame-load", this.show.bind(this))
|
9
|
+
|
10
|
+
// Hide modal on successful form submission
|
11
|
+
this.element.addEventListener("turbo:submit-end", this.handleSubmitEnd.bind(this))
|
12
|
+
|
13
|
+
// Handle escape key
|
14
|
+
this.handleKeydown = this.handleKeydown.bind(this)
|
15
|
+
document.addEventListener("keydown", this.handleKeydown)
|
16
|
+
}
|
17
|
+
|
18
|
+
disconnect() {
|
19
|
+
document.removeEventListener("keydown", this.handleKeydown)
|
20
|
+
}
|
21
|
+
|
22
|
+
show() {
|
23
|
+
const backdrop = document.getElementById("modal-backdrop")
|
24
|
+
if (backdrop) {
|
25
|
+
backdrop.style.display = "block"
|
26
|
+
|
27
|
+
// Trigger animation after display
|
28
|
+
requestAnimationFrame(() => {
|
29
|
+
backdrop.classList.remove("opacity-0")
|
30
|
+
backdrop.classList.add("opacity-100")
|
31
|
+
})
|
32
|
+
|
33
|
+
// Prevent body scroll
|
34
|
+
document.body.style.overflow = "hidden"
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
close() {
|
39
|
+
const backdrop = document.getElementById("modal-backdrop")
|
40
|
+
if (backdrop) {
|
41
|
+
backdrop.classList.remove("opacity-100")
|
42
|
+
backdrop.classList.add("opacity-0")
|
43
|
+
|
44
|
+
// Hide after animation and clear turbo frame
|
45
|
+
setTimeout(() => {
|
46
|
+
// Clear the modal turbo frame
|
47
|
+
const modalFrame = document.getElementById("modal")
|
48
|
+
if (modalFrame) {
|
49
|
+
modalFrame.innerHTML = ""
|
50
|
+
}
|
51
|
+
document.body.style.overflow = ""
|
52
|
+
}, 300)
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
closeOnBackdrop(event) {
|
57
|
+
// Only close if clicked on backdrop, not on modal content
|
58
|
+
if (event.target === event.currentTarget) {
|
59
|
+
this.close()
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
handleKeydown(event) {
|
64
|
+
if (event.key === "Escape") {
|
65
|
+
this.close()
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
handleSubmitEnd(event) {
|
70
|
+
// Close modal on successful form submission
|
71
|
+
if (event.detail.success) {
|
72
|
+
this.close()
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["navbar", "content", "body"]
|
5
|
+
|
6
|
+
connect() {
|
7
|
+
this.scrollThreshold = 60
|
8
|
+
this.isScrolled = false
|
9
|
+
this.handleScroll = this.handleScroll.bind(this)
|
10
|
+
window.addEventListener('scroll', this.handleScroll, { passive: true })
|
11
|
+
}
|
12
|
+
|
13
|
+
disconnect() {
|
14
|
+
window.removeEventListener('scroll', this.handleScroll)
|
15
|
+
}
|
16
|
+
|
17
|
+
handleScroll() {
|
18
|
+
const currentScrollY = window.scrollY
|
19
|
+
|
20
|
+
if (currentScrollY > this.scrollThreshold && !this.isScrolled) {
|
21
|
+
this.shrinkNavbar()
|
22
|
+
} else if (currentScrollY <= this.scrollThreshold && this.isScrolled) {
|
23
|
+
this.expandNavbar()
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
shrinkNavbar() {
|
28
|
+
this.isScrolled = true
|
29
|
+
// Reduce navbar padding from py-4 to py-2
|
30
|
+
this.navbarTarget.classList.remove('py-4')
|
31
|
+
this.navbarTarget.classList.add('py-2')
|
32
|
+
|
33
|
+
// Reduce body padding from pt-20 to pt-16
|
34
|
+
if (this.hasBodyTarget) {
|
35
|
+
this.bodyTarget.classList.remove('pt-20')
|
36
|
+
this.bodyTarget.classList.add('pt-16')
|
37
|
+
}
|
38
|
+
|
39
|
+
// Smoothly hide content elements with width transition
|
40
|
+
this.contentTargets.forEach(content => {
|
41
|
+
// Add transition classes first
|
42
|
+
content.classList.add('transition-all', 'duration-300', 'overflow-hidden')
|
43
|
+
|
44
|
+
// Then animate to zero width and opacity
|
45
|
+
setTimeout(() => {
|
46
|
+
content.classList.add('w-0', 'opacity-0', 'max-w-0')
|
47
|
+
content.classList.remove('md:block')
|
48
|
+
}, 10)
|
49
|
+
})
|
50
|
+
}
|
51
|
+
|
52
|
+
expandNavbar() {
|
53
|
+
this.isScrolled = false
|
54
|
+
// Restore original navbar padding
|
55
|
+
this.navbarTarget.classList.remove('py-2')
|
56
|
+
this.navbarTarget.classList.add('py-4')
|
57
|
+
|
58
|
+
// Restore original body padding
|
59
|
+
if (this.hasBodyTarget) {
|
60
|
+
this.bodyTarget.classList.remove('pt-16')
|
61
|
+
this.bodyTarget.classList.add('pt-20')
|
62
|
+
}
|
63
|
+
|
64
|
+
// Smoothly show content elements
|
65
|
+
this.contentTargets.forEach(content => {
|
66
|
+
// Remove width constraints and restore opacity
|
67
|
+
content.classList.remove('w-0', 'opacity-0', 'max-w-0')
|
68
|
+
content.classList.add('md:block')
|
69
|
+
|
70
|
+
// Clean up transition classes after animation
|
71
|
+
setTimeout(() => {
|
72
|
+
content.classList.remove('transition-all', 'duration-300', 'overflow-hidden')
|
73
|
+
}, 300)
|
74
|
+
})
|
75
|
+
}
|
76
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
static targets = ["notification"]
|
5
|
+
static values = {
|
6
|
+
type: String,
|
7
|
+
message: String,
|
8
|
+
duration: { type: Number, default: 4000 }
|
9
|
+
}
|
10
|
+
|
11
|
+
connect() {
|
12
|
+
this.show()
|
13
|
+
}
|
14
|
+
|
15
|
+
show() {
|
16
|
+
// Add the notification to the page
|
17
|
+
this.element.classList.remove('translate-x-full', 'opacity-0')
|
18
|
+
this.element.classList.add('translate-x-0', 'opacity-100')
|
19
|
+
|
20
|
+
// Auto-hide after duration
|
21
|
+
if (this.durationValue > 0) {
|
22
|
+
this.timeout = setTimeout(() => {
|
23
|
+
this.hide()
|
24
|
+
}, this.durationValue)
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
hide() {
|
29
|
+
if (this.timeout) {
|
30
|
+
clearTimeout(this.timeout)
|
31
|
+
}
|
32
|
+
|
33
|
+
// Animate out
|
34
|
+
this.element.classList.remove('translate-x-0', 'opacity-100')
|
35
|
+
this.element.classList.add('translate-x-full', 'opacity-0')
|
36
|
+
|
37
|
+
// Remove from DOM after animation
|
38
|
+
setTimeout(() => {
|
39
|
+
if (this.element.parentNode) {
|
40
|
+
this.element.remove()
|
41
|
+
}
|
42
|
+
}, 300)
|
43
|
+
}
|
44
|
+
|
45
|
+
close() {
|
46
|
+
this.hide()
|
47
|
+
}
|
48
|
+
}
|
@@ -0,0 +1,124 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import { get, post } from "@rails/request.js"
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static values = {
|
6
|
+
actionClass: String,
|
7
|
+
executionMode: String,
|
8
|
+
recordId: String,
|
9
|
+
resourceName: String,
|
10
|
+
confirm: String
|
11
|
+
}
|
12
|
+
|
13
|
+
execute(event) {
|
14
|
+
event.preventDefault()
|
15
|
+
event.stopPropagation()
|
16
|
+
|
17
|
+
// Hide the context menu first
|
18
|
+
this.hideContextMenu()
|
19
|
+
|
20
|
+
// Handle based on execution mode
|
21
|
+
if (this.executionModeValue === 'modal') {
|
22
|
+
this.showModal()
|
23
|
+
} else if (this.executionModeValue === 'instant') {
|
24
|
+
this.executeInstant()
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
executeInstant() {
|
29
|
+
// Show confirmation modal if confirm message is provided
|
30
|
+
if (this.confirmValue) {
|
31
|
+
this.showConfirmationModal()
|
32
|
+
} else {
|
33
|
+
// Execute immediately if no confirmation needed
|
34
|
+
this.performAction()
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
showModal() {
|
39
|
+
this.loadModalForm()
|
40
|
+
}
|
41
|
+
|
42
|
+
async showConfirmationModal() {
|
43
|
+
try {
|
44
|
+
// Store action details for when confirmation is received
|
45
|
+
this.pendingAction = {
|
46
|
+
resourceName: this.resourceNameValue,
|
47
|
+
recordId: this.recordIdValue,
|
48
|
+
actionClass: this.actionClassValue
|
49
|
+
}
|
50
|
+
|
51
|
+
// Listen for confirmation modal events
|
52
|
+
document.addEventListener('confirmation-modal:confirmed', this.handleConfirmation.bind(this), { once: true })
|
53
|
+
document.addEventListener('confirmation-modal:cancelled', this.handleCancellation.bind(this), { once: true })
|
54
|
+
|
55
|
+
// Load confirmation modal
|
56
|
+
const response = await get('/admin/confirmation_modal', {
|
57
|
+
responseKind: 'turbo-stream',
|
58
|
+
query: {
|
59
|
+
message: this.confirmValue,
|
60
|
+
title: 'Confirm Action',
|
61
|
+
confirm_text: 'Execute',
|
62
|
+
danger: 'true'
|
63
|
+
}
|
64
|
+
})
|
65
|
+
} catch (error) {
|
66
|
+
console.error('Failed to load confirmation modal:', error)
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
handleConfirmation() {
|
71
|
+
if (this.pendingAction) {
|
72
|
+
const { resourceName, recordId, actionClass } = this.pendingAction
|
73
|
+
this.performActionWithDetails(resourceName, recordId, actionClass)
|
74
|
+
this.pendingAction = null
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
handleCancellation() {
|
79
|
+
this.pendingAction = null
|
80
|
+
}
|
81
|
+
|
82
|
+
async performAction() {
|
83
|
+
this.performActionWithDetails(
|
84
|
+
this.resourceNameValue,
|
85
|
+
this.recordIdValue,
|
86
|
+
this.actionClassValue
|
87
|
+
)
|
88
|
+
}
|
89
|
+
|
90
|
+
async performActionWithDetails(resourceName, recordId, actionClass) {
|
91
|
+
try {
|
92
|
+
const formData = new FormData()
|
93
|
+
|
94
|
+
// Add CSRF token
|
95
|
+
const csrfToken = document.querySelector('[name="csrf-token"]').content
|
96
|
+
formData.append('authenticity_token', csrfToken)
|
97
|
+
|
98
|
+
const response = await post(`/admin/${resourceName}/${recordId}/actions/${actionClass}`, {
|
99
|
+
responseKind: 'turbo-stream',
|
100
|
+
body: formData
|
101
|
+
})
|
102
|
+
} catch (error) {
|
103
|
+
console.error('Failed to execute action:', error)
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
async loadModalForm() {
|
108
|
+
try {
|
109
|
+
const response = await get(`/admin/${this.resourceNameValue}/${this.recordIdValue}/actions/${this.actionClassValue}/form`, {
|
110
|
+
responseKind: 'turbo-stream'
|
111
|
+
})
|
112
|
+
} catch (error) {
|
113
|
+
console.error('Failed to load modal form:', error)
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
hideContextMenu() {
|
118
|
+
// Find and hide any existing context menu
|
119
|
+
const contextMenu = document.getElementById('context-menu-container')
|
120
|
+
if (contextMenu) {
|
121
|
+
contextMenu.remove()
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import { post } from "@rails/request.js"
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = ["form"]
|
6
|
+
static values = {
|
7
|
+
submitUrl: String,
|
8
|
+
recordId: String,
|
9
|
+
actionClass: String
|
10
|
+
}
|
11
|
+
|
12
|
+
close() {
|
13
|
+
this.element.remove()
|
14
|
+
}
|
15
|
+
|
16
|
+
submit() {
|
17
|
+
const formData = this.collectFormData()
|
18
|
+
|
19
|
+
post(this.submitUrlValue, {
|
20
|
+
body: formData,
|
21
|
+
responseKind: 'turbo-stream'
|
22
|
+
}).then(response => {
|
23
|
+
if (response.ok) {
|
24
|
+
this.close()
|
25
|
+
}
|
26
|
+
}).catch(error => {
|
27
|
+
console.error('Row action submission failed:', error)
|
28
|
+
})
|
29
|
+
}
|
30
|
+
|
31
|
+
collectFormData() {
|
32
|
+
const formData = new FormData()
|
33
|
+
|
34
|
+
// Add CSRF token
|
35
|
+
const csrfToken = document.querySelector('[name="csrf-token"]').content
|
36
|
+
formData.append('authenticity_token', csrfToken)
|
37
|
+
|
38
|
+
// Add action class and execution mode
|
39
|
+
formData.append('action_class', this.actionClassValue)
|
40
|
+
formData.append('execution_mode', 'modal')
|
41
|
+
|
42
|
+
// Collect form field data from the rendered form components
|
43
|
+
const formElement = this.formTarget
|
44
|
+
const inputs = formElement.querySelectorAll('input, select, textarea')
|
45
|
+
|
46
|
+
inputs.forEach(input => {
|
47
|
+
const name = input.name
|
48
|
+
if (name && name.startsWith('row_action[')) {
|
49
|
+
if (input.type === 'checkbox') {
|
50
|
+
formData.append(name, input.checked ? '1' : '0')
|
51
|
+
} else {
|
52
|
+
formData.append(name, input.value || '')
|
53
|
+
}
|
54
|
+
}
|
55
|
+
})
|
56
|
+
|
57
|
+
return formData
|
58
|
+
}
|
59
|
+
}
|