dbdoc_engine 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/README.md +331 -0
- data/Rakefile +8 -0
- data/app/assets/builds/dbdoc_engine/application.css +5 -0
- data/app/assets/images/dbdoc_engine/arrowdown.svg +3 -0
- data/app/assets/images/dbdoc_engine/arrowhorizontal.svg +3 -0
- data/app/assets/images/dbdoc_engine/arrowleft.svg +3 -0
- data/app/assets/images/dbdoc_engine/changelog.svg +3 -0
- data/app/assets/images/dbdoc_engine/column_stats_dbdocs.svg +23 -0
- data/app/assets/images/dbdoc_engine/diagram.svg +3 -0
- data/app/assets/images/dbdoc_engine/double_arrow.svg +4 -0
- data/app/assets/images/dbdoc_engine/group_bu.svg +3 -0
- data/app/assets/images/dbdoc_engine/japan_circle.png +0 -0
- data/app/assets/images/dbdoc_engine/log_in_image.png +0 -0
- data/app/assets/images/dbdoc_engine/logo.svg +12 -0
- data/app/assets/images/dbdoc_engine/orange_changelog.svg +3 -0
- data/app/assets/images/dbdoc_engine/orange_fields.svg +23 -0
- data/app/assets/images/dbdoc_engine/orange_logo.svg +12 -0
- data/app/assets/images/dbdoc_engine/orange_table.svg +21 -0
- data/app/assets/images/dbdoc_engine/orange_updates.svg +43 -0
- data/app/assets/images/dbdoc_engine/orange_wiki.svg +3 -0
- data/app/assets/images/dbdoc_engine/search.svg +3 -0
- data/app/assets/images/dbdoc_engine/setting.svg +3 -0
- data/app/assets/images/dbdoc_engine/table_dbdocs.svg +21 -0
- data/app/assets/images/dbdoc_engine/uk_circle_transparent.png +0 -0
- data/app/assets/images/dbdoc_engine/update_stats_dbdocs.svg +43 -0
- data/app/assets/images/dbdoc_engine/wiki.svg +3 -0
- data/app/assets/stylesheets/dbdoc_engine/admin.css +176 -0
- data/app/assets/stylesheets/dbdoc_engine/admin_header.css +179 -0
- data/app/assets/stylesheets/dbdoc_engine/application.scss +1 -0
- data/app/assets/stylesheets/dbdoc_engine/changelog.css +173 -0
- data/app/assets/stylesheets/dbdoc_engine/dashboard.css +513 -0
- data/app/assets/stylesheets/dbdoc_engine/dbdoc_application.css +117 -0
- data/app/assets/stylesheets/dbdoc_engine/ecommerce.css +253 -0
- data/app/assets/stylesheets/dbdoc_engine/group_details.css +178 -0
- data/app/assets/stylesheets/dbdoc_engine/header.css +212 -0
- data/app/assets/stylesheets/dbdoc_engine/loading_spinner.css +127 -0
- data/app/assets/stylesheets/dbdoc_engine/login.css +213 -0
- data/app/assets/stylesheets/dbdoc_engine/schema_diagram.css +149 -0
- data/app/assets/stylesheets/dbdoc_engine/sidebar.css +296 -0
- data/app/assets/stylesheets/dbdoc_engine/table_details.css +417 -0
- data/app/controllers/dbdoc_engine/admin/base_controller.rb +23 -0
- data/app/controllers/dbdoc_engine/admin/dashboard_controller.rb +16 -0
- data/app/controllers/dbdoc_engine/admin/data_transfer_controller.rb +63 -0
- data/app/controllers/dbdoc_engine/admin/db_design_dynamic_tables_controller.rb +198 -0
- data/app/controllers/dbdoc_engine/admin/db_design_table_groups_controller.rb +107 -0
- data/app/controllers/dbdoc_engine/application_controller.rb +65 -0
- data/app/controllers/dbdoc_engine/concerns/internationalization.rb +57 -0
- data/app/controllers/dbdoc_engine/db_doc_sessions_controller.rb +33 -0
- data/app/controllers/dbdoc_engine/home_controller.rb +79 -0
- data/app/controllers/dbdoc_engine/schema_diagram_controller.rb +293 -0
- data/app/helper/dbdoc_engine/application_helper.rb +35 -0
- data/app/helpers/dbdoc_engine/application_helper.rb +4 -0
- data/app/helpers/dbdoc_engine/changelogs_helper.rb +27 -0
- data/app/helpers/dbdoc_engine/column_helper.rb +30 -0
- data/app/helpers/dbdoc_engine/db_design_dynamic_tables_helper.rb +15 -0
- data/app/helpers/dbdoc_engine/home_helper.rb +75 -0
- data/app/javascript/dbdoc_engine/application.js +12 -0
- data/app/javascript/dbdoc_engine/controllers/application.js +29 -0
- data/app/javascript/dbdoc_engine/controllers/auto_submit_controller.js +17 -0
- data/app/javascript/dbdoc_engine/controllers/chart_controller.js +58 -0
- data/app/javascript/dbdoc_engine/controllers/column-type_controller.js +149 -0
- data/app/javascript/dbdoc_engine/controllers/column_controller.js +362 -0
- data/app/javascript/dbdoc_engine/controllers/column_search_controller.js +42 -0
- data/app/javascript/dbdoc_engine/controllers/dbdoc_accordion_controller.js +42 -0
- data/app/javascript/dbdoc_engine/controllers/ecommerce_controller.js +73 -0
- data/app/javascript/dbdoc_engine/controllers/group_details_controller.js +88 -0
- data/app/javascript/dbdoc_engine/controllers/import_export_controller.js +200 -0
- data/app/javascript/dbdoc_engine/controllers/index.js +9 -0
- data/app/javascript/dbdoc_engine/controllers/language_controller.js +100 -0
- data/app/javascript/dbdoc_engine/controllers/loading_spinner_controller.js +48 -0
- data/app/javascript/dbdoc_engine/controllers/login_controller.js +75 -0
- data/app/javascript/dbdoc_engine/controllers/notification_controller.js +15 -0
- data/app/javascript/dbdoc_engine/controllers/schema_diagram_controller.js +1129 -0
- data/app/javascript/dbdoc_engine/controllers/select2_controller.js +67 -0
- data/app/javascript/dbdoc_engine/controllers/sidebar_controller.js +943 -0
- data/app/javascript/dbdoc_engine/controllers/table_details_controller.js +245 -0
- data/app/javascript/dbdoc_engine/controllers/table_group_validation_controller.js +148 -0
- data/app/javascript/dbdoc_engine/controllers/table_validation_controller.js +423 -0
- data/app/jobs/dbdoc_engine/application_job.rb +4 -0
- data/app/mailers/dbdoc_engine/application_mailer.rb +6 -0
- data/app/models/dbdoc_engine/application_record.rb +6 -0
- data/app/models/dbdoc_engine/concerns/soft_deletable.rb +30 -0
- data/app/models/dbdoc_engine/db_design_changelog.rb +44 -0
- data/app/models/dbdoc_engine/db_design_dynamic_column.rb +211 -0
- data/app/models/dbdoc_engine/db_design_dynamic_table.rb +124 -0
- data/app/models/dbdoc_engine/db_design_table_group.rb +88 -0
- data/app/models/dbdoc_engine/user.rb +21 -0
- data/app/queries/dbdoc_engine/admin_dashboard_queries.rb +71 -0
- data/app/queries/dbdoc_engine/db_design_changelog_queries.rb +68 -0
- data/app/queries/dbdoc_engine/db_design_dynamic_column_queries.rb +37 -0
- data/app/queries/dbdoc_engine/db_design_dynamic_table_commands.rb +106 -0
- data/app/queries/dbdoc_engine/db_design_dynamic_table_queries.rb +194 -0
- data/app/queries/dbdoc_engine/db_design_table_group_queries.rb +154 -0
- data/app/services/dbdoc_engine/db_design_dynamic_table_export_service.rb +38 -0
- data/app/services/dbdoc_engine/db_design_dynamic_table_handler_service.rb +49 -0
- data/app/services/dbdoc_engine/db_design_dynamic_tables_service.rb +21 -0
- data/app/services/dbdoc_engine/error_handler_service.rb +43 -0
- data/app/services/dbdoc_engine/schema_rb_import_service.rb +194 -0
- data/app/services/dbdoc_engine/schema_rb_parser_service.rb +339 -0
- data/app/services/dbdoc_engine/table_filter_service.rb +35 -0
- data/app/services/dbdoc_engine/table_groups_service.rb +199 -0
- data/app/services/dbdoc_engine/table_management_service.rb +192 -0
- data/app/views/dbdoc_engine/admin/dashboard/_action_badge.html.erb +11 -0
- data/app/views/dbdoc_engine/admin/dashboard/_changelog_rows.html.erb +22 -0
- data/app/views/dbdoc_engine/admin/dashboard/_changelog_table_headers.html.erb +8 -0
- data/app/views/dbdoc_engine/admin/dashboard/_filter_fields.html.erb +43 -0
- data/app/views/dbdoc_engine/admin/dashboard/index.html.erb +159 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_column_fields.html.erb +225 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_deleted_table_index.html.erb +110 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_foreign_key_fields.html.erb +51 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_form.html.erb +75 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_recent_activity.html.erb +39 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_columns.html.erb +127 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_index.html.erb +109 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_information.html.erb +99 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/deleted_tables.html.erb +95 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/edit.html.erb +23 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/export_all_to_excel.xlsx.axlsx +240 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/export_to_excel.xlsx.axlsx +135 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/index.html.erb +109 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/new.html.erb +25 -0
- data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/show_table_info.html.erb +125 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/_deleted_table_groups_list.html.erb +75 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/_form.html.erb +88 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/_table_groups_list.html.erb +82 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/deleted_groups.html.erb +60 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/edit.html.erb +25 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/index.html.erb +85 -0
- data/app/views/dbdoc_engine/admin/db_design_table_groups/new.html.erb +26 -0
- data/app/views/dbdoc_engine/db_doc_sessions/new.html.erb +59 -0
- data/app/views/dbdoc_engine/home/changelog_details.html.erb +80 -0
- data/app/views/dbdoc_engine/home/changelogs.html.erb +20 -0
- data/app/views/dbdoc_engine/home/group_details.html.erb +94 -0
- data/app/views/dbdoc_engine/home/index.html.erb +11 -0
- data/app/views/dbdoc_engine/home/partials/_action_badge.html.erb +11 -0
- data/app/views/dbdoc_engine/home/partials/_breadcrumb_navigation.html.erb +30 -0
- data/app/views/dbdoc_engine/home/partials/_changelog_rows.html.erb +35 -0
- data/app/views/dbdoc_engine/home/partials/_changelog_table_headers.html.erb +16 -0
- data/app/views/dbdoc_engine/home/partials/_column_headers.html.erb +23 -0
- data/app/views/dbdoc_engine/home/partials/_column_row.html.erb +157 -0
- data/app/views/dbdoc_engine/home/partials/_filter_form.html.erb +47 -0
- data/app/views/dbdoc_engine/home/partials/_group_section.html.erb +84 -0
- data/app/views/dbdoc_engine/home/partials/_pagination.html.erb +5 -0
- data/app/views/dbdoc_engine/home/partials/_stats_container.html.erb +46 -0
- data/app/views/dbdoc_engine/home/partials/_table_groups.html.erb +7 -0
- data/app/views/dbdoc_engine/home/partials/_table_information_section.html.erb +50 -0
- data/app/views/dbdoc_engine/home/partials/_table_section.html.erb +48 -0
- data/app/views/dbdoc_engine/home/table_details.html.erb +9 -0
- data/app/views/dbdoc_engine/schema_diagram/index.html.erb +102 -0
- data/app/views/dbdoc_engine/shared/_admin_header.html.erb +78 -0
- data/app/views/dbdoc_engine/shared/_header.html.erb +94 -0
- data/app/views/dbdoc_engine/shared/_js_translations.html.erb +3 -0
- data/app/views/dbdoc_engine/shared/_language_button.html.erb +14 -0
- data/app/views/dbdoc_engine/shared/_sidebar.html.erb +128 -0
- data/app/views/kaminari/dbdoc_engine/_first_page.html.erb +3 -0
- data/app/views/kaminari/dbdoc_engine/_gap.html.erb +3 -0
- data/app/views/kaminari/dbdoc_engine/_last_page.html.erb +3 -0
- data/app/views/kaminari/dbdoc_engine/_next_page.html.erb +3 -0
- data/app/views/kaminari/dbdoc_engine/_page.html.erb +9 -0
- data/app/views/kaminari/dbdoc_engine/_paginator.html.erb +17 -0
- data/app/views/kaminari/dbdoc_engine/_prev_page.html.erb +3 -0
- data/app/views/layouts/dbdoc_engine/application.html.erb +107 -0
- data/app/views/layouts/dbdoc_engine/header.html.erb +108 -0
- data/config/importmap.rb +11 -0
- data/config/locales/en.yml +307 -0
- data/config/locales/ja.yml +306 -0
- data/config/routes.rb +73 -0
- data/db/migrate/rails7/20250227060610_create_db_design_table_groups.rb +15 -0
- data/db/migrate/rails7/20250227094626_create_db_design_dynamic_tables.rb +19 -0
- data/db/migrate/rails7/20250228022732_create_db_design_dynamic_columns.rb +34 -0
- data/db/migrate/rails7/20250401051453_create_db_design_changelogs.rb +26 -0
- data/db/migrate/rails7/20250411040822_create_users.rb +14 -0
- data/db/migrate/rails7/20250421080851_add_missing_indexes_to_dbdoc_tables.rb +23 -0
- data/db/migrate/rails8/20250227060610_create_db_design_table_groups.rb +15 -0
- data/db/migrate/rails8/20250227094626_create_db_design_dynamic_tables.rb +19 -0
- data/db/migrate/rails8/20250228022732_create_db_design_dynamic_columns.rb +34 -0
- data/db/migrate/rails8/20250401051453_create_db_design_changelogs.rb +26 -0
- data/db/migrate/rails8/20250411040822_create_users.rb +14 -0
- data/db/migrate/rails8/20250421080851_add_missing_indexes_to_dbdoc_tables.rb +23 -0
- data/db/seeds.rb +28 -0
- data/lib/dbdoc_engine/engine.rb +57 -0
- data/lib/dbdoc_engine/version.rb +3 -0
- data/lib/dbdoc_engine.rb +9 -0
- data/lib/generators/dbdoc_engine/install/install_generator.rb +245 -0
- data/lib/generators/dbdoc_engine/uninstall/uninstall_generator.rb +196 -0
- data/lib/tasks/dbdoc_engine_tasks.rake +44 -0
- data/public/dbdoc_engine_assets/images/camel_chess_head.png +0 -0
- data/public/dbdoc_engine_assets/images/dblogo.svg +4 -0
- data/public/dbdoc_engine_assets/images/japan_circle.png +0 -0
- data/public/dbdoc_engine_assets/images/king_chess_head.png +0 -0
- data/public/dbdoc_engine_assets/images/login-bg.svg +44 -0
- data/public/dbdoc_engine_assets/images/logo.png +0 -0
- data/public/dbdoc_engine_assets/images/logo.svg +12 -0
- data/public/dbdoc_engine_assets/images/queen_chess_head.png +0 -0
- data/public/dbdoc_engine_assets/images/soldier_chess_headd.png +0 -0
- data/public/dbdoc_engine_assets/images/uk_circle_transparent.png +0 -0
- metadata +415 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = [
|
|
5
|
+
"dataType",
|
|
6
|
+
"length",
|
|
7
|
+
"lengthSuggestion",
|
|
8
|
+
"decimalPrecision", // New target for decimal precision field
|
|
9
|
+
"decimalPrecisionContainer", // Container to show/hide
|
|
10
|
+
"columnNameInput",
|
|
11
|
+
"destroyInput",
|
|
12
|
+
"columnFields",
|
|
13
|
+
"removeButton"
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
// Flag to track if length has been manually set
|
|
17
|
+
manualLengthSet = false
|
|
18
|
+
|
|
19
|
+
// Types that need decimal precision
|
|
20
|
+
decimalTypes = ['real', 'double precision', 'numeric', 'decimal']
|
|
21
|
+
|
|
22
|
+
// PostgreSQL default lengths for specific data types
|
|
23
|
+
dataTypeLengths = {
|
|
24
|
+
'smallint': { default: 16, description: 'Suggested: 16-bit integer' },
|
|
25
|
+
'integer': { default: 32, description: 'Suggested: 32-bit integer' },
|
|
26
|
+
'bigint': { default: 64, description: 'Suggested: 64-bit integer' },
|
|
27
|
+
'numeric': { default: 10, description: 'Suggested: Default precision' },
|
|
28
|
+
'real': { default: 32, description: 'Suggested: 32-bit floating point' },
|
|
29
|
+
'double precision': { default: 64, description: 'Suggested: 64-bit floating point' },
|
|
30
|
+
'varchar': { default: 255, description: 'Suggested: Max length 255' },
|
|
31
|
+
'character': { default: 1, description: 'Suggested: Single character' },
|
|
32
|
+
'char': { default: 1, description: 'Suggested: Single character' },
|
|
33
|
+
'timestamp': { default: 6, description: 'Suggested: Timestamp precision' },
|
|
34
|
+
'timestamptz': { default: 6, description: 'Suggested: Timestamp with timezone precision' },
|
|
35
|
+
'time': { default: 6, description: 'Suggested: Time precision' },
|
|
36
|
+
'timetz': { default: 6, description: 'Suggested: Time with timezone precision' }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Default decimal precisions
|
|
40
|
+
defaultDecimalPrecisions = {
|
|
41
|
+
'real': 6,
|
|
42
|
+
'double precision': 15,
|
|
43
|
+
'numeric': 2,
|
|
44
|
+
'decimal': 2
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
connect() {
|
|
48
|
+
// Only set the default length if this is a new field or the length is empty
|
|
49
|
+
if (!this.lengthTarget.value) {
|
|
50
|
+
this.updateLength()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Add event listener for data type changes
|
|
54
|
+
this.dataTypeTarget.addEventListener('change', this.handleDataTypeChange.bind(this))
|
|
55
|
+
|
|
56
|
+
// Add event listener for manual length changes
|
|
57
|
+
this.lengthTarget.addEventListener('input', this.handleManualLengthChange.bind(this))
|
|
58
|
+
|
|
59
|
+
// Initialize decimal precision field visibility
|
|
60
|
+
this.toggleDecimalPrecisionField()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
disconnect() {
|
|
64
|
+
// Remove event listeners when controller disconnects
|
|
65
|
+
this.dataTypeTarget.removeEventListener('change', this.handleDataTypeChange.bind(this))
|
|
66
|
+
this.lengthTarget.removeEventListener('input', this.handleManualLengthChange.bind(this))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
handleDataTypeChange() {
|
|
70
|
+
// Only update length automatically if it hasn't been manually set
|
|
71
|
+
// or if the field is empty
|
|
72
|
+
if (!this.manualLengthSet || !this.lengthTarget.value) {
|
|
73
|
+
this.updateLength()
|
|
74
|
+
} else {
|
|
75
|
+
// Just update the suggestion text based on the data type
|
|
76
|
+
this.updateSuggestionOnly()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Toggle decimal precision field visibility
|
|
80
|
+
this.toggleDecimalPrecisionField()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
handleManualLengthChange() {
|
|
84
|
+
// If user typed something, mark as manually set
|
|
85
|
+
if (this.lengthTarget.value !== '') {
|
|
86
|
+
this.manualLengthSet = true
|
|
87
|
+
this.clearSuggestion()
|
|
88
|
+
} else {
|
|
89
|
+
// If field was cleared, reset the flag
|
|
90
|
+
this.manualLengthSet = false
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
updateLength() {
|
|
95
|
+
const selectedType = this.dataTypeTarget.value
|
|
96
|
+
|
|
97
|
+
// Reset suggested length text
|
|
98
|
+
this.lengthSuggestionTarget.textContent = ''
|
|
99
|
+
|
|
100
|
+
// If the selected type has a default length
|
|
101
|
+
if (this.dataTypeLengths.hasOwnProperty(selectedType)) {
|
|
102
|
+
// Set a default value and show suggested length
|
|
103
|
+
this.lengthTarget.value = this.dataTypeLengths[selectedType].default
|
|
104
|
+
this.lengthSuggestionTarget.textContent = this.dataTypeLengths[selectedType].description
|
|
105
|
+
} else {
|
|
106
|
+
// Clear the value for types without specific length
|
|
107
|
+
this.lengthTarget.value = ''
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Reset the manual flag when automatically setting length
|
|
111
|
+
this.manualLengthSet = false
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
updateSuggestionOnly() {
|
|
115
|
+
const selectedType = this.dataTypeTarget.value
|
|
116
|
+
|
|
117
|
+
// Only update the suggestion text, not the value
|
|
118
|
+
if (this.dataTypeLengths.hasOwnProperty(selectedType)) {
|
|
119
|
+
this.lengthSuggestionTarget.textContent = this.dataTypeLengths[selectedType].description
|
|
120
|
+
} else {
|
|
121
|
+
this.lengthSuggestionTarget.textContent = ''
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
clearSuggestion() {
|
|
126
|
+
this.lengthSuggestionTarget.textContent = ''
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
toggleDecimalPrecisionField() {
|
|
130
|
+
const selectedType = this.dataTypeTarget.value
|
|
131
|
+
|
|
132
|
+
// Check if this data type needs decimal precision
|
|
133
|
+
if (this.decimalTypes.includes(selectedType)) {
|
|
134
|
+
// Show the decimal precision field
|
|
135
|
+
this.decimalPrecisionContainerTarget.classList.remove('d-none')
|
|
136
|
+
|
|
137
|
+
// Set default value if empty
|
|
138
|
+
if (!this.decimalPrecisionTarget.value && this.defaultDecimalPrecisions[selectedType]) {
|
|
139
|
+
this.decimalPrecisionTarget.value = this.defaultDecimalPrecisions[selectedType]
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
// Hide the decimal precision field
|
|
143
|
+
this.decimalPrecisionContainerTarget.classList.add('d-none')
|
|
144
|
+
|
|
145
|
+
// Clear the value when not needed
|
|
146
|
+
this.decimalPrecisionTarget.value = ''
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stimulus Controller for managing database columns in a table design interface
|
|
5
|
+
* Handles column addition, removal, foreign key relationships, dependency checks,
|
|
6
|
+
* and dynamic UI updates for column management
|
|
7
|
+
*/
|
|
8
|
+
export default class extends Controller {
|
|
9
|
+
static targets = ["columns", "destroyField", "searchInput"]
|
|
10
|
+
|
|
11
|
+
get adminPath() {
|
|
12
|
+
const match = window.location.pathname.match(/(.+?)\/admin/);
|
|
13
|
+
return match ? `${match[1]}/admin` : "/admin";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Lifecycle method called when the controller connects to the DOM
|
|
18
|
+
* Initializes existing foreign key relationships and checks dependencies
|
|
19
|
+
*/
|
|
20
|
+
connect() {
|
|
21
|
+
// Initialize any existing foreign key checkboxes - show foreign key fields if checked
|
|
22
|
+
document.querySelectorAll('.foreign-key-checkbox').forEach(checkbox => {
|
|
23
|
+
if (checkbox.checked) {
|
|
24
|
+
this.showForeignKeyFields(checkbox);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// For existing foreign tables, load their columns
|
|
29
|
+
this.element.querySelectorAll('.foreign-table-dropdown').forEach(select => {
|
|
30
|
+
const selectedTable = select.dataset.columnExistingForeignTable;
|
|
31
|
+
if (selectedTable) {
|
|
32
|
+
this.loadColumns({ target: select }, true);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Check column dependencies on page load to disable removal of referenced columns
|
|
37
|
+
this.checkAllColumnDependencies();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Filters the column list based on search input
|
|
42
|
+
* Shows/hides columns based on whether they match the search text
|
|
43
|
+
*
|
|
44
|
+
* @param {Event} event - Input event from the search field
|
|
45
|
+
*/
|
|
46
|
+
filterColumns(event) {
|
|
47
|
+
const searchText = event.target.value.toLowerCase().trim();
|
|
48
|
+
|
|
49
|
+
this.columnsTarget.querySelectorAll('.column-fields').forEach(columnField => {
|
|
50
|
+
const columnNameInput = columnField.querySelector('[name*="[column_name]"]');
|
|
51
|
+
const columnName = columnNameInput ? columnNameInput.value.toLowerCase() : '';
|
|
52
|
+
|
|
53
|
+
if (searchText === '' || columnName.includes(searchText)) {
|
|
54
|
+
// Show the column if it matches or search is empty
|
|
55
|
+
columnField.style.display = '';
|
|
56
|
+
} else {
|
|
57
|
+
// Hide the column if it doesn't match the search
|
|
58
|
+
columnField.style.display = 'none';
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Checks dependencies for all columns in the table
|
|
65
|
+
* Disables remove buttons for columns that are referenced as foreign keys in other tables
|
|
66
|
+
*/
|
|
67
|
+
checkAllColumnDependencies() {
|
|
68
|
+
document.querySelectorAll('.column-fields').forEach(columnField => {
|
|
69
|
+
const columnInput = columnField.querySelector('[name*="[column_name]"]');
|
|
70
|
+
const tableInput = document.querySelector('[name="db_design_dynamic_table[id]"]');
|
|
71
|
+
const removeButton = columnField.querySelector('button[data-action="column#remove"]');
|
|
72
|
+
|
|
73
|
+
const columnName = columnInput ? columnInput.value.trim() : "";
|
|
74
|
+
const tableId = tableInput ? tableInput.value : null;
|
|
75
|
+
|
|
76
|
+
if (!columnName || !tableId) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.checkColumnDependency(columnName, tableId, removeButton);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Checks if a specific column is referenced by foreign keys in other tables
|
|
86
|
+
* Disables the remove button if the column is referenced
|
|
87
|
+
*
|
|
88
|
+
* @param {string} columnName - Name of the column to check
|
|
89
|
+
* @param {string|number} tableId - ID of the table containing the column
|
|
90
|
+
* @param {HTMLElement} removeButton - Button element for removing the column
|
|
91
|
+
*/
|
|
92
|
+
checkColumnDependency(columnName, tableId, removeButton) {
|
|
93
|
+
fetch(`${this.adminPath}/db_design_dynamic_tables/check_column_dependency?column_name=${columnName}&table_id=${tableId}`)
|
|
94
|
+
.then(response => response.json())
|
|
95
|
+
.then(data => {
|
|
96
|
+
if (data.is_referenced) {
|
|
97
|
+
removeButton.disabled = true;
|
|
98
|
+
removeButton.classList.replace('btn-danger', 'btn-secondary');
|
|
99
|
+
removeButton.setAttribute('title', `Cannot delete column '${columnName}' because it is referenced as a foreign key in another table.`);
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
.catch(error => {
|
|
103
|
+
console.error("Error checking column dependencies:", error);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Adds a new column to the table by fetching the column template HTML
|
|
109
|
+
* Inserts the new column at the beginning of the columns container
|
|
110
|
+
*
|
|
111
|
+
* @param {Event} event - Click event from the add column button
|
|
112
|
+
*/
|
|
113
|
+
add(event) {
|
|
114
|
+
event.preventDefault();
|
|
115
|
+
|
|
116
|
+
// Get current locale from URL or document
|
|
117
|
+
const currentLocale = new URLSearchParams(window.location.search).get('locale') ||
|
|
118
|
+
document.documentElement.lang || 'en';
|
|
119
|
+
|
|
120
|
+
// Add locale to the fetch URL
|
|
121
|
+
fetch(`${this.adminPath}/db_design_dynamic_tables/render_column_fields?locale=${currentLocale}`)
|
|
122
|
+
.then(response => response.text())
|
|
123
|
+
.then(html => {
|
|
124
|
+
// Insert the new column at the beginning of the columns container
|
|
125
|
+
this.columnsTarget.insertAdjacentHTML('afterbegin', html);
|
|
126
|
+
|
|
127
|
+
// Find the newly added column
|
|
128
|
+
const newColumn = this.columnsTarget.firstElementChild;
|
|
129
|
+
|
|
130
|
+
// For the new column, we want to expand it on initial add
|
|
131
|
+
// All existing columns remain collapsed
|
|
132
|
+
if (typeof bootstrap !== 'undefined' && bootstrap.Collapse) {
|
|
133
|
+
// Find the collapse element in the new column
|
|
134
|
+
const collapseElement = newColumn.querySelector('.accordion-collapse');
|
|
135
|
+
if (collapseElement) {
|
|
136
|
+
// Initialize and show the new column's accordion
|
|
137
|
+
const bsCollapse = new bootstrap.Collapse(collapseElement, {
|
|
138
|
+
toggle: false
|
|
139
|
+
});
|
|
140
|
+
bsCollapse.show();
|
|
141
|
+
|
|
142
|
+
// Update the button state
|
|
143
|
+
const accordionButton = newColumn.querySelector('.accordion-button');
|
|
144
|
+
if (accordionButton) {
|
|
145
|
+
accordionButton.classList.remove('collapsed');
|
|
146
|
+
accordionButton.setAttribute('aria-expanded', 'true');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Scroll to the new column
|
|
152
|
+
newColumn.scrollIntoView({ behavior: 'smooth' });
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Updates the accordion title with column metadata when column properties change
|
|
158
|
+
* Displays column name, data type, length, and badges for PK, FK, and NOT NULL constraints
|
|
159
|
+
*
|
|
160
|
+
* @param {Event} event - Change event from column property fields
|
|
161
|
+
*/
|
|
162
|
+
updateAccordionTitle(event) {
|
|
163
|
+
const columnField = event.target.closest('.column-fields');
|
|
164
|
+
const accordionButton = columnField.querySelector('.accordion-button');
|
|
165
|
+
|
|
166
|
+
// Get values for the accordion header
|
|
167
|
+
const nameInput = columnField.querySelector('[name*="[column_name]"]');
|
|
168
|
+
const dataTypeSelect = columnField.querySelector('[name*="[data_type]"]');
|
|
169
|
+
const lengthInput = columnField.querySelector('[name*="[length]"]');
|
|
170
|
+
const isPrimaryKey = columnField.querySelector('[name*="[is_primary_key]"]').checked;
|
|
171
|
+
const isForeignKey = columnField.querySelector('[name*="[is_foreign_key]"]').checked;
|
|
172
|
+
const isNotNull = columnField.querySelector('[name*="[not_null]"]').checked;
|
|
173
|
+
|
|
174
|
+
// Build the header content
|
|
175
|
+
let headerContent = '';
|
|
176
|
+
if (nameInput.value.trim()) {
|
|
177
|
+
headerContent = `<strong>${nameInput.value.trim()}</strong>`;
|
|
178
|
+
|
|
179
|
+
if (dataTypeSelect.value) {
|
|
180
|
+
headerContent += `<span class="ms-2 text-muted">(${dataTypeSelect.value}`;
|
|
181
|
+
if (lengthInput.value && ['varchar', 'char', 'character'].includes(dataTypeSelect.value)) {
|
|
182
|
+
headerContent += `(${lengthInput.value})`;
|
|
183
|
+
}
|
|
184
|
+
headerContent += ')</span>';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Add badges for key constraints
|
|
188
|
+
if (isPrimaryKey) headerContent += '<span class="badge bg-primary ms-2">PK</span>';
|
|
189
|
+
if (isForeignKey) headerContent += '<span class="badge bg-info ms-2">FK</span>';
|
|
190
|
+
if (isNotNull) headerContent += '<span class="badge bg-warning ms-2">NOT NULL</span>';
|
|
191
|
+
} else {
|
|
192
|
+
headerContent = '<em>New Column</em>';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
accordionButton.innerHTML = headerContent;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Handles column removal after checking for dependencies
|
|
200
|
+
* Prevents removal of columns that are referenced by foreign keys in other tables
|
|
201
|
+
*
|
|
202
|
+
* @param {Event} event - Click event from the remove column button
|
|
203
|
+
*/
|
|
204
|
+
remove(event) {
|
|
205
|
+
event.preventDefault();
|
|
206
|
+
const columnField = event.target.closest('.column-fields');
|
|
207
|
+
|
|
208
|
+
// Get table ID from data attribute
|
|
209
|
+
const tableId = columnField.dataset.tableId;
|
|
210
|
+
|
|
211
|
+
// Get column name
|
|
212
|
+
const columnInput = columnField.querySelector('[name*="[column_name]"]');
|
|
213
|
+
const columnName = columnInput ? columnInput.value.trim() : "";
|
|
214
|
+
if (!columnName || !tableId) {
|
|
215
|
+
console.error("Missing column name or table ID");
|
|
216
|
+
this.markForRemoval(columnField, true);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Check if the column can be safely removed
|
|
221
|
+
fetch(`${this.adminPath}/db_design_dynamic_tables/check_column_dependency?column_name=${columnName}&table_id=${tableId}`)
|
|
222
|
+
.then(response => response.json())
|
|
223
|
+
.then(data => {
|
|
224
|
+
if (data.is_referenced) {
|
|
225
|
+
Swal.fire({
|
|
226
|
+
icon: "error",
|
|
227
|
+
title: "Cannot Delete",
|
|
228
|
+
text: `Column '${columnName}' is referenced as a foreign key in another table.`,
|
|
229
|
+
confirmButtonColor: "#364380"
|
|
230
|
+
});
|
|
231
|
+
} else {
|
|
232
|
+
this.markForRemoval(columnField, true);
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
.catch(error => {
|
|
236
|
+
console.error("Error checking column dependencies:", error);
|
|
237
|
+
Swal.fire({
|
|
238
|
+
icon: "warning",
|
|
239
|
+
title: "Dependency Check Failed",
|
|
240
|
+
text: "Error checking column dependencies. Please try again.",
|
|
241
|
+
confirmButtonColor: "#364380"
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Marks a column for removal by setting the _destroy field to 1
|
|
248
|
+
* For existing columns, this flags them for deletion on form submission
|
|
249
|
+
* For new columns, they are immediately removed from the DOM
|
|
250
|
+
*
|
|
251
|
+
* @param {HTMLElement} columnField - The column container element
|
|
252
|
+
* @param {boolean} allowDeletion - Whether deletion is allowed
|
|
253
|
+
*/
|
|
254
|
+
markForRemoval(columnField, allowDeletion) {
|
|
255
|
+
if (!allowDeletion) return;
|
|
256
|
+
|
|
257
|
+
const destroyInput = columnField.querySelector('[data-column-target="destroyField"]');
|
|
258
|
+
if (destroyInput) {
|
|
259
|
+
// For existing columns, set _destroy=1 to mark for deletion and hide the column
|
|
260
|
+
destroyInput.value = "1";
|
|
261
|
+
columnField.style.display = "none";
|
|
262
|
+
} else {
|
|
263
|
+
// For new columns that haven't been saved yet, just remove from DOM
|
|
264
|
+
columnField.remove();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Toggles the visibility of foreign key fields based on checkbox state
|
|
270
|
+
*
|
|
271
|
+
* @param {Event} event - Change event from the foreign key checkbox
|
|
272
|
+
*/
|
|
273
|
+
toggleForeignKeyFields(event) {
|
|
274
|
+
const checkbox = event.target;
|
|
275
|
+
if (checkbox.checked) {
|
|
276
|
+
this.showForeignKeyFields(checkbox);
|
|
277
|
+
} else {
|
|
278
|
+
this.hideForeignKeyFields(checkbox);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Shows the foreign key fields when foreign key checkbox is checked
|
|
284
|
+
*
|
|
285
|
+
* @param {HTMLElement} checkbox - The foreign key checkbox element
|
|
286
|
+
*/
|
|
287
|
+
showForeignKeyFields(checkbox) {
|
|
288
|
+
const columnField = checkbox.closest('.column-fields');
|
|
289
|
+
const foreignKeyFields = columnField.querySelector('.foreign-key-fields');
|
|
290
|
+
if (foreignKeyFields) {
|
|
291
|
+
foreignKeyFields.classList.remove('hidden'); // Remove hidden class
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Hides the foreign key fields when foreign key checkbox is unchecked
|
|
297
|
+
*
|
|
298
|
+
* @param {HTMLElement} checkbox - The foreign key checkbox element
|
|
299
|
+
*/
|
|
300
|
+
hideForeignKeyFields(checkbox) {
|
|
301
|
+
const columnField = checkbox.closest('.column-fields');
|
|
302
|
+
const foreignKeyFields = columnField.querySelector('.foreign-key-fields');
|
|
303
|
+
if (foreignKeyFields) {
|
|
304
|
+
foreignKeyFields.classList.add('hidden'); // Add hidden class
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Loads available columns from a selected referenced table for foreign key selection
|
|
310
|
+
* Populates the column dropdown with columns from the selected table
|
|
311
|
+
*
|
|
312
|
+
* @param {Event} event - Change event from the table dropdown
|
|
313
|
+
* @param {boolean} isInitialLoad - Whether this is the initial load (to select existing values)
|
|
314
|
+
*/
|
|
315
|
+
loadColumns(event, isInitialLoad = false) {
|
|
316
|
+
const tableSelect = event.target;
|
|
317
|
+
const columnField = tableSelect.closest('.column-fields');
|
|
318
|
+
const columnSelect = columnField.querySelector('.foreign-column-dropdown');
|
|
319
|
+
const tableName = tableSelect.value; // Using table name instead of ID
|
|
320
|
+
const existingColumn = tableSelect.dataset.columnExistingForeignColumn;
|
|
321
|
+
|
|
322
|
+
// Get the select-search controller for the column dropdown
|
|
323
|
+
const selectSearchController = window.Stimulus.getControllerForElementAndIdentifier(
|
|
324
|
+
columnSelect.closest('[data-controller="select-search"]'),
|
|
325
|
+
'select-search'
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
if (tableName) {
|
|
329
|
+
// Fetch columns for the selected table
|
|
330
|
+
fetch(`${this.adminPath}/db_design_dynamic_tables/columns?table_name=${encodeURIComponent(tableName)}`)
|
|
331
|
+
.then(response => response.json())
|
|
332
|
+
.then(columns => {
|
|
333
|
+
// Reset and populate the column dropdown
|
|
334
|
+
columnSelect.innerHTML = '<option value="">Select Column</option>';
|
|
335
|
+
columns.forEach(column => {
|
|
336
|
+
const option = document.createElement('option');
|
|
337
|
+
option.value = column.column_name;
|
|
338
|
+
option.textContent = column.column_name;
|
|
339
|
+
// Select the existing column if initializing
|
|
340
|
+
if (isInitialLoad && column.column_name === existingColumn) {
|
|
341
|
+
option.selected = true;
|
|
342
|
+
}
|
|
343
|
+
columnSelect.appendChild(option);
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Refresh the select-search to capture the new options
|
|
347
|
+
if (selectSearchController) {
|
|
348
|
+
selectSearchController.refreshOptions();
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
.catch(error => console.error("Error fetching columns:", error));
|
|
352
|
+
} else {
|
|
353
|
+
// Reset column dropdown if no table selected
|
|
354
|
+
columnSelect.innerHTML = '<option value="">Select Column</option>';
|
|
355
|
+
|
|
356
|
+
// Refresh the select-search
|
|
357
|
+
if (selectSearchController) {
|
|
358
|
+
selectSearchController.refreshOptions();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
connect() {
|
|
5
|
+
// Store all table rows for quick access
|
|
6
|
+
this.tableRows = document.querySelectorAll('table tbody tr');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
filter() {
|
|
10
|
+
const query = this.element.value.toLowerCase().trim();
|
|
11
|
+
|
|
12
|
+
// If search is empty, show all rows
|
|
13
|
+
if (!query) {
|
|
14
|
+
this.tableRows.forEach(row => {
|
|
15
|
+
row.style.display = '';
|
|
16
|
+
});
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Filter rows based on search query
|
|
21
|
+
this.tableRows.forEach(row => {
|
|
22
|
+
const columnName = row.querySelector('td:nth-child(2)').textContent.toLowerCase();
|
|
23
|
+
const physicalName = row.querySelector('td:nth-child(3)').textContent.toLowerCase();
|
|
24
|
+
const dataType = row.querySelector('td:nth-child(4)').textContent.toLowerCase();
|
|
25
|
+
const constraints = row.querySelector('td:nth-child(5)').textContent.toLowerCase();
|
|
26
|
+
const defaultValue = row.querySelector('td:nth-child(6)').textContent.toLowerCase();
|
|
27
|
+
const description = row.querySelector('td:nth-child(7)').textContent.toLowerCase();
|
|
28
|
+
|
|
29
|
+
// Show row if any field contains the query
|
|
30
|
+
if (columnName.includes(query) ||
|
|
31
|
+
physicalName.includes(query) ||
|
|
32
|
+
dataType.includes(query) ||
|
|
33
|
+
constraints.includes(query) ||
|
|
34
|
+
defaultValue.includes(query) ||
|
|
35
|
+
description.includes(query)) {
|
|
36
|
+
row.style.display = '';
|
|
37
|
+
} else {
|
|
38
|
+
row.style.display = 'none';
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// app/javascript/controllers/dbdoc_accordion_controller.js
|
|
2
|
+
import { Controller } from "@hotwired/stimulus"
|
|
3
|
+
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = ["button", "content"]
|
|
6
|
+
|
|
7
|
+
connect() {
|
|
8
|
+
this.ensureClosedState()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
ensureClosedState() {
|
|
12
|
+
// Always start closed
|
|
13
|
+
this.contentTarget.classList.remove("show")
|
|
14
|
+
this.buttonTarget.setAttribute("aria-expanded", "false")
|
|
15
|
+
this.buttonTarget.classList.add("collapsed")
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
toggle(event) {
|
|
19
|
+
event.preventDefault()
|
|
20
|
+
event.stopPropagation()
|
|
21
|
+
|
|
22
|
+
const isCurrentlyOpen = this.contentTarget.classList.contains("show")
|
|
23
|
+
|
|
24
|
+
if (isCurrentlyOpen) {
|
|
25
|
+
this.close()
|
|
26
|
+
} else {
|
|
27
|
+
this.open()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
open() {
|
|
32
|
+
this.contentTarget.classList.add("show")
|
|
33
|
+
this.buttonTarget.setAttribute("aria-expanded", "true")
|
|
34
|
+
this.buttonTarget.classList.remove("collapsed")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
close() {
|
|
38
|
+
this.contentTarget.classList.remove("show")
|
|
39
|
+
this.buttonTarget.setAttribute("aria-expanded", "false")
|
|
40
|
+
this.buttonTarget.classList.add("collapsed")
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ['groupContent', 'viewButton', 'tableGroups'];
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
// Initialize clickable rows when the controller connects
|
|
8
|
+
this.initializeClickableRows();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Toggles the visibility of the group content when the view button is clicked.
|
|
13
|
+
* @param {Event} event - The click event triggered by the user.
|
|
14
|
+
*/
|
|
15
|
+
toggleGroupContent(event) {
|
|
16
|
+
// Get the button that was clicked
|
|
17
|
+
const viewButton = event.currentTarget;
|
|
18
|
+
|
|
19
|
+
// Find the closest group section to the clicked button
|
|
20
|
+
const groupSection = viewButton.closest('.group-section');
|
|
21
|
+
|
|
22
|
+
// Find the associated group content using the Stimulus target
|
|
23
|
+
const groupContent = groupSection.querySelector('[data-ecommerce-target="groupContent"]');
|
|
24
|
+
|
|
25
|
+
// Toggle the 'active' class on the view button
|
|
26
|
+
viewButton.classList.toggle('active');
|
|
27
|
+
|
|
28
|
+
// Toggle the visibility of the group content based on the button state
|
|
29
|
+
if (viewButton.classList.contains('active')) {
|
|
30
|
+
groupContent.classList.add('active');
|
|
31
|
+
} else {
|
|
32
|
+
groupContent.classList.remove('active');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Prevent the event from bubbling up to the header
|
|
36
|
+
event.stopPropagation();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Toggles the group content when the header is clicked
|
|
41
|
+
* @param {Event} event - The click event triggered by the user.
|
|
42
|
+
*/
|
|
43
|
+
toggleGroupContentHeader(event) {
|
|
44
|
+
// Find the closest group section to the clicked header
|
|
45
|
+
const groupSection = event.currentTarget.closest('.group-section');
|
|
46
|
+
|
|
47
|
+
// Find the view button and group content
|
|
48
|
+
const viewButton = groupSection.querySelector('[data-ecommerce-target="viewButton"]');
|
|
49
|
+
const groupContent = groupSection.querySelector('[data-ecommerce-target="groupContent"]');
|
|
50
|
+
|
|
51
|
+
// Toggle the 'active' class on the view button
|
|
52
|
+
viewButton.classList.toggle('active');
|
|
53
|
+
|
|
54
|
+
// Toggle the visibility of the group content based on the button state
|
|
55
|
+
if (viewButton.classList.contains('active')) {
|
|
56
|
+
groupContent.classList.add('active');
|
|
57
|
+
} else {
|
|
58
|
+
groupContent.classList.remove('active');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Initialize clickable table rows
|
|
64
|
+
*/
|
|
65
|
+
initializeClickableRows() {
|
|
66
|
+
document.addEventListener('click', (event) => {
|
|
67
|
+
const row = event.target.closest('.group-table-row');
|
|
68
|
+
if (row && row.dataset.tableLink && !event.target.closest('.table-link')) {
|
|
69
|
+
window.location.href = row.dataset.tableLink;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|