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,245 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Table Details Controller
|
|
5
|
+
* Handles the interaction for the table details page including field toggling,
|
|
6
|
+
* navigation between tables and groups, and type description tooltips.
|
|
7
|
+
*/
|
|
8
|
+
export default class extends Controller {
|
|
9
|
+
// Define controller targets
|
|
10
|
+
static targets = ['fieldsToggle', 'fieldsContent', 'infoIcon'];
|
|
11
|
+
|
|
12
|
+
connect() {
|
|
13
|
+
// Initialize tooltips if Bootstrap is available
|
|
14
|
+
if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
|
|
15
|
+
this.initTooltips();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Add click event listener to document to close tooltips
|
|
19
|
+
document.addEventListener('click', this.handleDocumentClick.bind(this));
|
|
20
|
+
|
|
21
|
+
this.setConstraintTooltips();
|
|
22
|
+
if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
|
|
23
|
+
this.initTooltips();
|
|
24
|
+
this.initConstraintBadgeTooltips();
|
|
25
|
+
}
|
|
26
|
+
document.addEventListener('click', this.handleDocumentClick.bind(this));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
initConstraintBadgeTooltips() {
|
|
30
|
+
document.querySelectorAll('.constraint-badge').forEach(badge => {
|
|
31
|
+
new bootstrap.Tooltip(badge);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Toggles the visibility of the fields content section
|
|
37
|
+
* @param {Event} event - The click event from the toggle button
|
|
38
|
+
*/
|
|
39
|
+
toggleFields(event) {
|
|
40
|
+
const viewButton = event.currentTarget;
|
|
41
|
+
// Find the closest .group-section (or .main-content-body if that's your outer div)
|
|
42
|
+
const groupSection = viewButton.closest('.main-content-body, .group-section');
|
|
43
|
+
if (!groupSection) return;
|
|
44
|
+
|
|
45
|
+
// Find the .group-content inside this section
|
|
46
|
+
const fieldsContent = groupSection.querySelector('.group-content');
|
|
47
|
+
if (!fieldsContent) return;
|
|
48
|
+
|
|
49
|
+
// Toggle active class on view button
|
|
50
|
+
viewButton.classList.toggle('active');
|
|
51
|
+
|
|
52
|
+
// Toggle visibility of fields content based on button state
|
|
53
|
+
if (viewButton.classList.contains('active')) {
|
|
54
|
+
fieldsContent.classList.add('active');
|
|
55
|
+
} else {
|
|
56
|
+
fieldsContent.classList.remove('active');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
event.stopPropagation();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Toggles the fields content when the header is clicked
|
|
64
|
+
* @param {Event} event - The click event triggered by the user.
|
|
65
|
+
*/
|
|
66
|
+
toggleFieldsHeader(event) {
|
|
67
|
+
// Find the header that was clicked
|
|
68
|
+
const header = event.currentTarget;
|
|
69
|
+
// Find the closest section (div with .main-content-body or .group-section)
|
|
70
|
+
const section = header.closest('.main-content-body, .group-section');
|
|
71
|
+
if (!section) return;
|
|
72
|
+
|
|
73
|
+
// Find the view button and group-content inside this section
|
|
74
|
+
const viewButton = section.querySelector('.view-button');
|
|
75
|
+
const fieldsContent = section.querySelector('.group-content');
|
|
76
|
+
if (!viewButton || !fieldsContent) return;
|
|
77
|
+
|
|
78
|
+
// Toggle active class on view button
|
|
79
|
+
viewButton.classList.toggle('active');
|
|
80
|
+
// Toggle content
|
|
81
|
+
if (viewButton.classList.contains('active')) {
|
|
82
|
+
fieldsContent.classList.add('active');
|
|
83
|
+
} else {
|
|
84
|
+
fieldsContent.classList.remove('active');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Initialize tooltips for info icons
|
|
89
|
+
initTooltips() {
|
|
90
|
+
this.infoIconTargets.forEach(icon => {
|
|
91
|
+
const type = icon.dataset.type;
|
|
92
|
+
const description = this.getTypeDescription(type);
|
|
93
|
+
|
|
94
|
+
const tooltipContent = `
|
|
95
|
+
<div class="type-tooltip-content">
|
|
96
|
+
<div class="type-tooltip-heading">${type}</div>
|
|
97
|
+
<div class="type-tooltip-desc">${description.en}</div>
|
|
98
|
+
<div class="type-tooltip-desc">${description.ja}</div>
|
|
99
|
+
</div>
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
icon.setAttribute('title', tooltipContent);
|
|
103
|
+
|
|
104
|
+
new bootstrap.Tooltip(icon, {
|
|
105
|
+
html: true,
|
|
106
|
+
trigger: 'manual',
|
|
107
|
+
placement: 'right',
|
|
108
|
+
customClass: 'custom-tooltip', // Bootstrap 5.2+ supports this
|
|
109
|
+
template: `
|
|
110
|
+
<div class="tooltip custom-tooltip" role="tooltip">
|
|
111
|
+
<div class="tooltip-arrow"></div>
|
|
112
|
+
<div class="tooltip-inner"></div>
|
|
113
|
+
</div>
|
|
114
|
+
`
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Toggle tooltip on info icon click
|
|
120
|
+
toggleTooltip(event) {
|
|
121
|
+
const icon = event.currentTarget;
|
|
122
|
+
const tooltip = bootstrap.Tooltip.getInstance(icon);
|
|
123
|
+
if (!tooltip) return;
|
|
124
|
+
|
|
125
|
+
// Hide all other tooltips
|
|
126
|
+
this.infoIconTargets.forEach(otherIcon => {
|
|
127
|
+
if (otherIcon !== icon) {
|
|
128
|
+
const otherTooltip = bootstrap.Tooltip.getInstance(otherIcon);
|
|
129
|
+
if (otherTooltip) otherTooltip.hide();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
tooltip.toggle();
|
|
134
|
+
event.stopPropagation();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Handle document clicks to close tooltips
|
|
138
|
+
handleDocumentClick(event) {
|
|
139
|
+
if (!event.target.classList.contains('info-icon')) {
|
|
140
|
+
this.infoIconTargets.forEach(icon => {
|
|
141
|
+
const tooltip = bootstrap.Tooltip.getInstance(icon);
|
|
142
|
+
if (tooltip) tooltip.hide();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Get type description from existing data
|
|
148
|
+
getTypeDescription(type) {
|
|
149
|
+
const typeDescriptions = {
|
|
150
|
+
integer: { en: 'A whole number (e.g., 1, 2, 3).', ja: '整数(例:1、2、3)。' },
|
|
151
|
+
smallint: { en: 'A smaller range of whole numbers.', ja: '小さな範囲の整数。' },
|
|
152
|
+
bigint: { en: 'A larger range of whole numbers.', ja: '大きな範囲の整数。' },
|
|
153
|
+
numeric: { en: 'A number with precision and scale.', ja: '精度とスケールを持つ数値。' },
|
|
154
|
+
real: { en: 'A single-precision floating-point number.', ja: '単精度浮動小数点数。' },
|
|
155
|
+
double_precision: { en: 'A double-precision floating-point number.', ja: '倍精度浮動小数点数。' },
|
|
156
|
+
varchar: { en: 'A variable-length string of characters.', ja: '可変長の文字列。' },
|
|
157
|
+
character: { en: 'A fixed-length string of characters.', ja: '固定長の文字列。' },
|
|
158
|
+
text: { en: 'A long string of characters.', ja: '長い文字列。' },
|
|
159
|
+
char: { en: 'A single character.', ja: '単一の文字。' },
|
|
160
|
+
boolean: { en: 'A true or false value.', ja: '真または偽の値。' },
|
|
161
|
+
date: { en: 'A calendar date (e.g., YYYY-MM-DD).', ja: 'カレンダーの日付(例:YYYY-MM-DD)。' },
|
|
162
|
+
timestamp: { en: 'A precise point in time (e.g., YYYY-MM-DD HH:mm:ss).', ja: '正確な時点(例:YYYY-MM-DD HH:mm:ss)。' },
|
|
163
|
+
timestamptz: { en: 'A timestamp with time zone information.', ja: 'タイムゾーン情報を含むタイムスタンプ。' },
|
|
164
|
+
time: { en: 'A time without date information.', ja: '日付情報を含まない時間。' },
|
|
165
|
+
timetz: { en: 'A time with time zone information.', ja: 'タイムゾーン情報を含む時間。' },
|
|
166
|
+
interval: { en: 'A duration of time.', ja: '時間の期間。' },
|
|
167
|
+
uuid: { en: 'A universally unique identifier.', ja: 'ユニバーサル一意識別子。' }
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
return typeDescriptions[type] || { en: 'Description not available.', ja: '説明がありません。' };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Navigates back to the main ecommerce page
|
|
175
|
+
* Used for breadcrumb navigation
|
|
176
|
+
*/
|
|
177
|
+
navigateToEcommerce() {
|
|
178
|
+
window.location.href = '/dbdoc/';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Navigates to a specific group details page
|
|
183
|
+
* @param {Event} event - The click event containing the group ID
|
|
184
|
+
*/
|
|
185
|
+
navigateToGroup(event) {
|
|
186
|
+
const groupId = event.currentTarget.dataset.groupId;
|
|
187
|
+
if (groupId) {
|
|
188
|
+
window.location.href = '/dbdoc/group_details/' + groupId;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Clean up when controller disconnects
|
|
193
|
+
disconnect() {
|
|
194
|
+
document.removeEventListener('click', this.handleDocumentClick.bind(this));
|
|
195
|
+
|
|
196
|
+
if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
|
|
197
|
+
this.infoIconTargets.forEach(icon => {
|
|
198
|
+
const tooltip = bootstrap.Tooltip.getInstance(icon);
|
|
199
|
+
if (tooltip) tooltip.dispose();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
setConstraintTooltips() {
|
|
204
|
+
// Simple, short explanations (EN/JA)
|
|
205
|
+
const badgeTexts = {
|
|
206
|
+
'NOT_NULL': {
|
|
207
|
+
en: "This column cannot be empty.",
|
|
208
|
+
ja: "このカラムは必ず値が必要です。"
|
|
209
|
+
},
|
|
210
|
+
'FK': {
|
|
211
|
+
en: "Links to another table (foreign key).",
|
|
212
|
+
ja: "他のテーブルへの参照(外部キー)。"
|
|
213
|
+
},
|
|
214
|
+
'PK': {
|
|
215
|
+
en: "Uniquely identifies each row (primary key).",
|
|
216
|
+
ja: "各行を一意に識別する主キー。"
|
|
217
|
+
},
|
|
218
|
+
'CANDIDATE': {
|
|
219
|
+
en: "Can uniquely identify rows (candidate key).",
|
|
220
|
+
ja: "行を一意に識別できる候補キー。"
|
|
221
|
+
},
|
|
222
|
+
'INDEXED': {
|
|
223
|
+
en: "Speeds up searches (indexed column).",
|
|
224
|
+
ja: "検索を高速化するインデックス付きカラム。"
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
// Detect current locale
|
|
228
|
+
let locale = document.documentElement.lang || document.body.dataset.locale || 'en';
|
|
229
|
+
document.querySelectorAll('.constraint-badge').forEach(badge => {
|
|
230
|
+
const key = badge.getAttribute('data-badge');
|
|
231
|
+
if (badgeTexts[key]) {
|
|
232
|
+
badge.setAttribute('data-tooltip',
|
|
233
|
+
badgeTexts[key].en + "\n" + badgeTexts[key].ja
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
// Remove any previous inner arrow
|
|
237
|
+
const prev = badge.querySelector('.tooltip-arrow-inner');
|
|
238
|
+
if (prev) prev.remove();
|
|
239
|
+
// Add inner arrow element
|
|
240
|
+
const arrow = document.createElement('span');
|
|
241
|
+
arrow.className = 'tooltip-arrow-inner';
|
|
242
|
+
badge.appendChild(arrow);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["groupName", "groupColor", "createdBy", "updatedBy"]
|
|
5
|
+
|
|
6
|
+
// Optional: array to store existing group names for uniqueness check
|
|
7
|
+
existingGroupNames = []
|
|
8
|
+
|
|
9
|
+
connect() {
|
|
10
|
+
// Fetch existing group names when the controller connects
|
|
11
|
+
// this.fetchExistingGroupNames()
|
|
12
|
+
|
|
13
|
+
// If creating a new record (both targets exist), set up the event listener to copy values
|
|
14
|
+
if (this.hasCreatedByTarget && this.hasUpdatedByTarget) {
|
|
15
|
+
this.createdByTarget.addEventListener('input', this.copyCreatedByToUpdatedBy.bind(this))
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Copy the created_by value to updated_by field
|
|
20
|
+
copyCreatedByToUpdatedBy() {
|
|
21
|
+
this.updatedByTarget.value = this.createdByTarget.value
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Fetch existing group names from the server
|
|
25
|
+
async fetchExistingGroupNames() {
|
|
26
|
+
try {
|
|
27
|
+
const tableGroupId = this.element.dataset.tableGroupId || ""; // Get the group ID (empty if creating)
|
|
28
|
+
const response = await fetch(`/admin/db_design_table_groups/existing_names?id=${tableGroupId}`);
|
|
29
|
+
|
|
30
|
+
if (response.ok) {
|
|
31
|
+
this.existingGroupNames = await response.json();
|
|
32
|
+
}
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error("Error fetching existing group names:", error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async validate(event) {
|
|
39
|
+
event.preventDefault(); // Prevent submission unless valid
|
|
40
|
+
|
|
41
|
+
let isValid = true;
|
|
42
|
+
this.clearErrors(); // Reset previous errors
|
|
43
|
+
|
|
44
|
+
// Ensure we have the latest existing group names before validation
|
|
45
|
+
await this.fetchExistingGroupNames();
|
|
46
|
+
|
|
47
|
+
// Validate group name
|
|
48
|
+
const groupName = this.groupNameTarget.value.trim();
|
|
49
|
+
if (!groupName) {
|
|
50
|
+
this.showError(this.groupNameTarget, "group_name_required");
|
|
51
|
+
isValid = false;
|
|
52
|
+
} else if (this.isGroupNameTaken(groupName)) {
|
|
53
|
+
this.showError(this.groupNameTarget, "group_name_unique");
|
|
54
|
+
isValid = false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// For new records: validate created_by and automatically copy to updated_by
|
|
58
|
+
if (this.hasCreatedByTarget) {
|
|
59
|
+
const createdBy = this.createdByTarget.value.trim();
|
|
60
|
+
if (!createdBy) {
|
|
61
|
+
this.showError(this.createdByTarget, "created_by_required");
|
|
62
|
+
isValid = false;
|
|
63
|
+
} else if (this.hasUpdatedByTarget) {
|
|
64
|
+
// Ensure updated_by has the same value as created_by before submitting
|
|
65
|
+
this.updatedByTarget.value = createdBy;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// For editing existing records: validate updated_by
|
|
70
|
+
if (this.hasUpdatedByTarget && !this.hasCreatedByTarget) {
|
|
71
|
+
const updatedBy = this.updatedByTarget.value.trim();
|
|
72
|
+
if (!updatedBy) {
|
|
73
|
+
this.showError(this.updatedByTarget, "updated_by_required");
|
|
74
|
+
isValid = false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Validate group color
|
|
79
|
+
const groupColor = this.groupColorTarget.value.trim();
|
|
80
|
+
if (!groupColor) {
|
|
81
|
+
this.showError(this.groupColorTarget, "group_color_required");
|
|
82
|
+
isValid = false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (isValid) {
|
|
86
|
+
event.target.submit(); // Only submit if valid
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check if group name already exists
|
|
91
|
+
isGroupNameTaken(name) {
|
|
92
|
+
// Get the current table group ID (if editing)
|
|
93
|
+
const currentId = this.element.dataset.tableGroupId;
|
|
94
|
+
|
|
95
|
+
// If we're editing an existing record and the name hasn't changed,
|
|
96
|
+
// it's not considered "taken"
|
|
97
|
+
if (currentId) {
|
|
98
|
+
const currentName = this.element.dataset.originalGroupName;
|
|
99
|
+
if (currentName && currentName.toLowerCase() === name.toLowerCase()) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return this.existingGroupNames.some(
|
|
105
|
+
existingName => existingName.toLowerCase() === name.toLowerCase()
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
showError(element, messageKey) {
|
|
110
|
+
if (!element) return;
|
|
111
|
+
|
|
112
|
+
// Add 'is-invalid' class to input field
|
|
113
|
+
element.classList.add('is-invalid');
|
|
114
|
+
|
|
115
|
+
// Get translated message from the hidden translations div
|
|
116
|
+
const translatedMessage = this.getTranslation(messageKey);
|
|
117
|
+
|
|
118
|
+
// Check if error message already exists
|
|
119
|
+
let errorElement = element.nextElementSibling;
|
|
120
|
+
if (!errorElement || !errorElement.classList.contains('invalid-feedback')) {
|
|
121
|
+
errorElement = document.createElement('div');
|
|
122
|
+
errorElement.classList.add('invalid-feedback', 'd-block');
|
|
123
|
+
element.parentNode.appendChild(errorElement);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Set error message
|
|
127
|
+
errorElement.textContent = translatedMessage;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
getTranslation(key) {
|
|
131
|
+
// Use the global Translations object
|
|
132
|
+
if (window.Translations && window.Translations[key]) {
|
|
133
|
+
return window.Translations[key];
|
|
134
|
+
}
|
|
135
|
+
return key;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
clearErrors() {
|
|
139
|
+
// Remove 'is-invalid' class and error messages
|
|
140
|
+
this.element.querySelectorAll('.is-invalid').forEach(el => {
|
|
141
|
+
el.classList.remove('is-invalid');
|
|
142
|
+
const errorEl = el.nextElementSibling;
|
|
143
|
+
if (errorEl && errorEl.classList.contains('invalid-feedback')) {
|
|
144
|
+
errorEl.remove();
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|