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,88 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ['groupContent', 'viewButton'];
|
|
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-group-details-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-group-details-target="viewButton"]');
|
|
49
|
+
const groupContent = groupSection.querySelector('[data-group-details-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
|
+
// Find the closest table row to the clicked element
|
|
68
|
+
const row = event.target.closest('.group-table-row');
|
|
69
|
+
|
|
70
|
+
// If a row was found and the click wasn't on an existing link
|
|
71
|
+
if (row && !event.target.closest('.table-link')) {
|
|
72
|
+
// Find the link within the row
|
|
73
|
+
const link = row.querySelector('.table-link');
|
|
74
|
+
if (link) {
|
|
75
|
+
// Navigate to the link's href
|
|
76
|
+
window.location.href = link.getAttribute('href');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Navigates the user to the ecommerce homepage.
|
|
84
|
+
*/
|
|
85
|
+
navigateToEcommerce() {
|
|
86
|
+
window.location.href = '/dbdoc/';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["dropZone", "fileInput", "schemaDropZone", "schemaFileInput"];
|
|
5
|
+
static values = { basePath: String };
|
|
6
|
+
|
|
7
|
+
get adminPath() {
|
|
8
|
+
return `${this.basePathValue.split("?")[0].replace(/\/+$/, "")}/admin`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export(event) {
|
|
12
|
+
event.preventDefault();
|
|
13
|
+
fetch(`${this.adminPath}/export_data.json`)
|
|
14
|
+
.then(r => r.json())
|
|
15
|
+
.then(data => {
|
|
16
|
+
const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
|
|
17
|
+
const url = URL.createObjectURL(blob);
|
|
18
|
+
const a = document.createElement("a");
|
|
19
|
+
a.href = url;
|
|
20
|
+
a.download = "dbdoc_engine_export.json";
|
|
21
|
+
document.body.appendChild(a);
|
|
22
|
+
a.click();
|
|
23
|
+
document.body.removeChild(a);
|
|
24
|
+
})
|
|
25
|
+
.catch(() => this.showError("Export failed!"));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
showImport(event) {
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
if (!this.hasDropZoneTarget) return;
|
|
31
|
+
this.dropZoneTarget.style.display = "block";
|
|
32
|
+
document.addEventListener("click", this.handleDocumentClick);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
handleDocumentClick = (e) => {
|
|
36
|
+
if (this.hasDropZoneTarget && this.dropZoneTarget.contains(e.target)) return;
|
|
37
|
+
if (e.target.closest('[data-action="click->import-export#showImport"]')) return;
|
|
38
|
+
if (this.hasDropZoneTarget) this.dropZoneTarget.style.display = "none";
|
|
39
|
+
document.removeEventListener("click", this.handleDocumentClick);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
handleFileChange(event) {
|
|
43
|
+
if (event.target.files.length) this.handleImportFile(event.target.files[0]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
handleDrop(event) {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
if (this.hasDropZoneTarget) this.dropZoneTarget.style.borderColor = "#ccc";
|
|
49
|
+
if (event.dataTransfer.files.length) this.handleImportFile(event.dataTransfer.files[0]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
handleDragOver(event) {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
const zone = event.currentTarget;
|
|
55
|
+
zone.style.borderColor = "#0d6efd";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
handleDragLeave(event) {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
const zone = event.currentTarget;
|
|
61
|
+
zone.style.borderColor = "#ccc";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
handleImportFile(file) {
|
|
65
|
+
if (!file.name.endsWith(".json")) {
|
|
66
|
+
this.showWarning("Please select a valid JSON file.");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const reader = new FileReader();
|
|
70
|
+
reader.onload = (e) => {
|
|
71
|
+
fetch(`${this.adminPath}/import_data`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: { "Content-Type": "application/json", "X-CSRF-Token": this.csrfToken },
|
|
74
|
+
body: e.target.result
|
|
75
|
+
})
|
|
76
|
+
.then(r => r.json())
|
|
77
|
+
.then(data => {
|
|
78
|
+
if (data.status === "ok") {
|
|
79
|
+
this.showSuccess("Import successful!").then(() => window.location.reload());
|
|
80
|
+
} else {
|
|
81
|
+
this.showError("Import failed: " + (data.error || "Unknown error"));
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
.catch(() => this.showError("Import failed!"));
|
|
85
|
+
};
|
|
86
|
+
reader.readAsText(file);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async importProjectSchema(event) {
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
|
|
92
|
+
const confirmed = await this.showConfirm(
|
|
93
|
+
"Import Project Schema",
|
|
94
|
+
"This will replace all existing documentation with tables from this project's schema.rb. Continue?"
|
|
95
|
+
);
|
|
96
|
+
if (!confirmed) return;
|
|
97
|
+
|
|
98
|
+
fetch(`${this.adminPath}/import_schema?source=project`, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: { "Content-Type": "application/json", "X-CSRF-Token": this.csrfToken }
|
|
101
|
+
})
|
|
102
|
+
.then(r => r.json())
|
|
103
|
+
.then(data => {
|
|
104
|
+
if (data.status === "ok") {
|
|
105
|
+
this.showSuccess(`Schema import successful! ${data.tables_count} tables imported.`)
|
|
106
|
+
.then(() => window.location.reload());
|
|
107
|
+
} else {
|
|
108
|
+
this.showError("Schema import failed: " + (data.error || "Unknown error"));
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
.catch(() => this.showError("Schema import failed!"));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
showSchemaUpload(event) {
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
if (!this.hasSchemaDropZoneTarget) return;
|
|
117
|
+
this.schemaDropZoneTarget.style.display = "block";
|
|
118
|
+
document.addEventListener("click", this.handleSchemaDocumentClick);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
handleSchemaDocumentClick = (e) => {
|
|
122
|
+
if (this.hasSchemaDropZoneTarget && this.schemaDropZoneTarget.contains(e.target)) return;
|
|
123
|
+
if (e.target.closest('[data-action="click->import-export#showSchemaUpload"]')) return;
|
|
124
|
+
if (this.hasSchemaDropZoneTarget) this.schemaDropZoneTarget.style.display = "none";
|
|
125
|
+
document.removeEventListener("click", this.handleSchemaDocumentClick);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
handleSchemaFileChange(event) {
|
|
129
|
+
if (event.target.files.length) this.handleSchemaImportFile(event.target.files[0]);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
handleSchemaDrop(event) {
|
|
133
|
+
event.preventDefault();
|
|
134
|
+
if (this.hasSchemaDropZoneTarget) this.schemaDropZoneTarget.style.borderColor = "#ccc";
|
|
135
|
+
if (event.dataTransfer.files.length) this.handleSchemaImportFile(event.dataTransfer.files[0]);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async handleSchemaImportFile(file) {
|
|
139
|
+
if (!file.name.endsWith(".rb")) {
|
|
140
|
+
this.showWarning("Please select a valid Ruby schema file (.rb).");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const confirmed = await this.showConfirm(
|
|
145
|
+
"Import Schema File",
|
|
146
|
+
"This will replace all existing documentation with tables from the uploaded schema.rb. Continue?"
|
|
147
|
+
);
|
|
148
|
+
if (!confirmed) return;
|
|
149
|
+
|
|
150
|
+
const reader = new FileReader();
|
|
151
|
+
reader.onload = (e) => {
|
|
152
|
+
fetch(`${this.adminPath}/import_schema`, {
|
|
153
|
+
method: "POST",
|
|
154
|
+
headers: { "Content-Type": "text/plain", "X-CSRF-Token": this.csrfToken },
|
|
155
|
+
body: e.target.result
|
|
156
|
+
})
|
|
157
|
+
.then(r => r.json())
|
|
158
|
+
.then(data => {
|
|
159
|
+
if (data.status === "ok") {
|
|
160
|
+
this.showSuccess(`Schema import successful! ${data.tables_count} tables imported.`)
|
|
161
|
+
.then(() => window.location.reload());
|
|
162
|
+
} else {
|
|
163
|
+
this.showError("Schema import failed: " + (data.error || "Unknown error"));
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
.catch(() => this.showError("Schema import failed!"));
|
|
167
|
+
};
|
|
168
|
+
reader.readAsText(file);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get csrfToken() {
|
|
172
|
+
const meta = document.querySelector('meta[name="csrf-token"]');
|
|
173
|
+
return meta ? meta.content : "";
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
showSuccess(message) {
|
|
177
|
+
return Swal.fire({ icon: "success", title: "Success", text: message, confirmButtonColor: "#364380" });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
showError(message) {
|
|
181
|
+
return Swal.fire({ icon: "error", title: "Error", text: message, confirmButtonColor: "#364380" });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
showWarning(message) {
|
|
185
|
+
return Swal.fire({ icon: "warning", title: "Warning", text: message, confirmButtonColor: "#364380" });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
showConfirm(title, text) {
|
|
189
|
+
return Swal.fire({
|
|
190
|
+
icon: "warning",
|
|
191
|
+
title: title,
|
|
192
|
+
text: text,
|
|
193
|
+
showCancelButton: true,
|
|
194
|
+
confirmButtonColor: "#364380",
|
|
195
|
+
cancelButtonColor: "#6c757d",
|
|
196
|
+
confirmButtonText: "Yes, proceed",
|
|
197
|
+
cancelButtonText: "Cancel"
|
|
198
|
+
}).then(result => result.isConfirmed);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { dbdoc_engine } from "controllers/application";
|
|
2
|
+
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading";
|
|
3
|
+
import ChartController from "./chart_controller"
|
|
4
|
+
import DbdocAccordionController from "./dbdoc_accordion_controller"
|
|
5
|
+
|
|
6
|
+
dbdoc_engine.register("chart", ChartController)
|
|
7
|
+
dbdoc_engine.register("dbdoc-accordion", DbdocAccordionController)
|
|
8
|
+
|
|
9
|
+
eagerLoadControllersFrom("controllers", dbdoc_engine);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// app/javascript/controllers/language_controller.js
|
|
2
|
+
import { Controller } from '@hotwired/stimulus';
|
|
3
|
+
|
|
4
|
+
export default class extends Controller {
|
|
5
|
+
static targets = ['button'];
|
|
6
|
+
|
|
7
|
+
connect() {
|
|
8
|
+
this.updateButtonDisplay();
|
|
9
|
+
this.checkActiveButton();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
toggleLanguage(event) {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
|
|
15
|
+
// Get current locale from HTML lang attribute or body data attribute
|
|
16
|
+
const currentLocale = document.documentElement.lang ||
|
|
17
|
+
document.body.dataset.locale ||
|
|
18
|
+
'en';
|
|
19
|
+
|
|
20
|
+
// Toggle between English and Japanese
|
|
21
|
+
const newLocale = currentLocale === 'ja' ? 'en' : 'ja';
|
|
22
|
+
|
|
23
|
+
// Add locale to URL and redirect
|
|
24
|
+
const url = new URL(window.location.href);
|
|
25
|
+
url.searchParams.set('locale', newLocale);
|
|
26
|
+
|
|
27
|
+
// Store in localStorage for immediate visual feedback before page reloads
|
|
28
|
+
localStorage.setItem('userLocale', newLocale);
|
|
29
|
+
|
|
30
|
+
// Navigate to the new URL
|
|
31
|
+
window.location.href = url.toString();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Updates the language button display based on the current locale.
|
|
36
|
+
*/
|
|
37
|
+
updateButtonDisplay() {
|
|
38
|
+
if (!this.hasButtonTarget) return;
|
|
39
|
+
|
|
40
|
+
const currentLocale = document.documentElement.lang ||
|
|
41
|
+
document.body.dataset.locale ||
|
|
42
|
+
localStorage.getItem('userLocale') ||
|
|
43
|
+
'en';
|
|
44
|
+
|
|
45
|
+
// Set the flag image based on the current locale
|
|
46
|
+
if (currentLocale === 'ja') {
|
|
47
|
+
this.buttonTarget.innerHTML = '<img src="/dbdoc_engine_assets/images/japan_circle.png" alt="日本語" class="language-japan-flag">';
|
|
48
|
+
this.buttonTarget.title = '日本語';
|
|
49
|
+
} else {
|
|
50
|
+
this.buttonTarget.innerHTML = '<img src="/dbdoc_engine_assets/images/uk_circle_transparent.png" alt="English" class="language-flag">';
|
|
51
|
+
this.buttonTarget.title = 'English';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
checkActiveButton() {
|
|
56
|
+
const navButtons = document.querySelectorAll('.nav-button');
|
|
57
|
+
|
|
58
|
+
// Remove 'active' class from all navigation buttons
|
|
59
|
+
navButtons.forEach(button => {
|
|
60
|
+
button.classList.remove('active');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Get the current page path (without query parameters)
|
|
64
|
+
const currentPath = window.location.pathname;
|
|
65
|
+
|
|
66
|
+
// Find the navigation buttons
|
|
67
|
+
const wikiButton = document.querySelector('.wiki-button');
|
|
68
|
+
const changelogButton = document.querySelector('.changelog-button');
|
|
69
|
+
const diagramButton = document.querySelector('.diagram-button');
|
|
70
|
+
|
|
71
|
+
// Define routes where each button should be active
|
|
72
|
+
const wikiActiveRoutes = ['/dbdoc/', '/dbdoc/group_details', '/dbdoc/table_details'];
|
|
73
|
+
const changelogRoutes = ['/dbdoc/changelogs'];
|
|
74
|
+
const diagramRoutes = ['/dbdoc/schema_diagram'];
|
|
75
|
+
|
|
76
|
+
// Check if the current path matches diagram routes first (highest priority)
|
|
77
|
+
const isDiagramActivePath = diagramRoutes.some(route =>
|
|
78
|
+
currentPath === route || currentPath.startsWith(route + '/')
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Check if the current path matches changelog routes
|
|
82
|
+
const isChangelogActivePath = changelogRoutes.some(route =>
|
|
83
|
+
currentPath === route || currentPath.startsWith(route + '/')
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Check if the current path matches wiki routes
|
|
87
|
+
const isWikiActivePath = wikiActiveRoutes.some(route =>
|
|
88
|
+
currentPath === route || currentPath.startsWith(route + '/')
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Set active class based on path, with proper prioritization
|
|
92
|
+
if (isDiagramActivePath && diagramButton) {
|
|
93
|
+
diagramButton.classList.add('active');
|
|
94
|
+
} else if (isChangelogActivePath && changelogButton) {
|
|
95
|
+
changelogButton.classList.add('active');
|
|
96
|
+
} else if (isWikiActivePath && wikiButton) {
|
|
97
|
+
wikiButton.classList.add('active');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
connect() {
|
|
5
|
+
this.boundShow = this.show.bind(this);
|
|
6
|
+
this.boundHide = this.hide.bind(this);
|
|
7
|
+
|
|
8
|
+
document.addEventListener("turbo:before-visit", this.boundShow);
|
|
9
|
+
document.addEventListener("turbo:before-fetch-request", this.boundShow);
|
|
10
|
+
document.addEventListener("turbo:load", this.boundHide);
|
|
11
|
+
document.addEventListener("turbo:frame-load", this.boundHide);
|
|
12
|
+
document.addEventListener("turbo:fetch-request-error", this.boundHide);
|
|
13
|
+
|
|
14
|
+
if (document.readyState !== "complete") {
|
|
15
|
+
this.show();
|
|
16
|
+
window.addEventListener("load", this.boundHide, { once: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
disconnect() {
|
|
21
|
+
document.removeEventListener("turbo:before-visit", this.boundShow);
|
|
22
|
+
document.removeEventListener("turbo:before-fetch-request", this.boundShow);
|
|
23
|
+
document.removeEventListener("turbo:load", this.boundHide);
|
|
24
|
+
document.removeEventListener("turbo:frame-load", this.boundHide);
|
|
25
|
+
document.removeEventListener("turbo:fetch-request-error", this.boundHide);
|
|
26
|
+
window.removeEventListener("load", this.boundHide);
|
|
27
|
+
this.clearSafetyTimeout();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
show() {
|
|
31
|
+
this.clearSafetyTimeout();
|
|
32
|
+
this.element.setAttribute("data-loading-spinner-visible", "true");
|
|
33
|
+
|
|
34
|
+
this.safetyTimeout = setTimeout(() => this.hide(), 8000);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
hide() {
|
|
38
|
+
this.clearSafetyTimeout();
|
|
39
|
+
this.element.setAttribute("data-loading-spinner-visible", "false");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
clearSafetyTimeout() {
|
|
43
|
+
if (this.safetyTimeout) {
|
|
44
|
+
clearTimeout(this.safetyTimeout);
|
|
45
|
+
this.safetyTimeout = null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus" // Import the Stimulus Controller base class
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["username", "password"] // Define the form fields that we want to target
|
|
5
|
+
|
|
6
|
+
connect() {
|
|
7
|
+
// Initialize the controller when it connects to the DOM
|
|
8
|
+
this.clearErrors() // Clear any existing error messages when the controller connects
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
validate(event) {
|
|
12
|
+
event.preventDefault() // Stop the form from submitting normally so we can validate first
|
|
13
|
+
|
|
14
|
+
let isValid = true // Assume the form is valid until we find errors
|
|
15
|
+
this.clearErrors() // Remove any previous error messages
|
|
16
|
+
|
|
17
|
+
// Validate username
|
|
18
|
+
const username = this.usernameTarget.value.trim() // Get the trimmed username value
|
|
19
|
+
if (!username) {
|
|
20
|
+
this.showError(this.usernameTarget, "username_required") // Display username error message
|
|
21
|
+
isValid = false // Mark the form as invalid
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Validate password
|
|
25
|
+
const password = this.passwordTarget.value.trim() // Get the trimmed password value
|
|
26
|
+
if (!password) {
|
|
27
|
+
this.showError(this.passwordTarget, "password_required") // Display password error message
|
|
28
|
+
isValid = false // Mark the form as invalid
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isValid) {
|
|
32
|
+
event.target.submit() // Submit the form if all validations pass
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
showError(element, messageKey) {
|
|
37
|
+
if (!element) return // Guard clause if element is undefined
|
|
38
|
+
|
|
39
|
+
// Add 'is-invalid' class to highlight the input field in red
|
|
40
|
+
element.classList.add('is-invalid')
|
|
41
|
+
|
|
42
|
+
// Get translated error message from translations
|
|
43
|
+
const translatedMessage = this.getTranslation(messageKey)
|
|
44
|
+
|
|
45
|
+
// Check if error message element already exists
|
|
46
|
+
let errorElement = element.nextElementSibling
|
|
47
|
+
if (!errorElement || !errorElement.classList.contains('invalid-feedback')) {
|
|
48
|
+
errorElement = document.createElement('div') // Create a new error message container
|
|
49
|
+
errorElement.classList.add('invalid-feedback', 'd-block') // Style it as Bootstrap error message
|
|
50
|
+
element.parentNode.appendChild(errorElement) // Add it after the input field
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Set the error message text
|
|
54
|
+
errorElement.textContent = translatedMessage
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getTranslation(key) {
|
|
58
|
+
// Look for the error message in the global Translations object
|
|
59
|
+
if (window.Translations && window.Translations[key]) {
|
|
60
|
+
return window.Translations[key] // Return the translated message if found
|
|
61
|
+
}
|
|
62
|
+
return key // Fall back to the key itself if translation not found
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
clearErrors() {
|
|
66
|
+
// Find all inputs with error styling
|
|
67
|
+
this.element.querySelectorAll('.is-invalid').forEach(el => {
|
|
68
|
+
el.classList.remove('is-invalid') // Remove the error styling class
|
|
69
|
+
const errorEl = el.nextElementSibling // Get the error message element
|
|
70
|
+
if (errorEl && errorEl.classList.contains('invalid-feedback')) {
|
|
71
|
+
errorEl.remove() // Remove the error message element from the DOM
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus";
|
|
2
|
+
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
static targets = ["sidebar"];
|
|
5
|
+
|
|
6
|
+
openSidebar() {
|
|
7
|
+
this.sidebarTarget.classList.add("open");
|
|
8
|
+
document.body.classList.add("notification-sidebar-open");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
closeSidebar() {
|
|
12
|
+
this.sidebarTarget.classList.remove("open");
|
|
13
|
+
document.body.classList.remove("notification-sidebar-open");
|
|
14
|
+
}
|
|
15
|
+
}
|