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.
Files changed (198) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +331 -0
  3. data/Rakefile +8 -0
  4. data/app/assets/builds/dbdoc_engine/application.css +5 -0
  5. data/app/assets/images/dbdoc_engine/arrowdown.svg +3 -0
  6. data/app/assets/images/dbdoc_engine/arrowhorizontal.svg +3 -0
  7. data/app/assets/images/dbdoc_engine/arrowleft.svg +3 -0
  8. data/app/assets/images/dbdoc_engine/changelog.svg +3 -0
  9. data/app/assets/images/dbdoc_engine/column_stats_dbdocs.svg +23 -0
  10. data/app/assets/images/dbdoc_engine/diagram.svg +3 -0
  11. data/app/assets/images/dbdoc_engine/double_arrow.svg +4 -0
  12. data/app/assets/images/dbdoc_engine/group_bu.svg +3 -0
  13. data/app/assets/images/dbdoc_engine/japan_circle.png +0 -0
  14. data/app/assets/images/dbdoc_engine/log_in_image.png +0 -0
  15. data/app/assets/images/dbdoc_engine/logo.svg +12 -0
  16. data/app/assets/images/dbdoc_engine/orange_changelog.svg +3 -0
  17. data/app/assets/images/dbdoc_engine/orange_fields.svg +23 -0
  18. data/app/assets/images/dbdoc_engine/orange_logo.svg +12 -0
  19. data/app/assets/images/dbdoc_engine/orange_table.svg +21 -0
  20. data/app/assets/images/dbdoc_engine/orange_updates.svg +43 -0
  21. data/app/assets/images/dbdoc_engine/orange_wiki.svg +3 -0
  22. data/app/assets/images/dbdoc_engine/search.svg +3 -0
  23. data/app/assets/images/dbdoc_engine/setting.svg +3 -0
  24. data/app/assets/images/dbdoc_engine/table_dbdocs.svg +21 -0
  25. data/app/assets/images/dbdoc_engine/uk_circle_transparent.png +0 -0
  26. data/app/assets/images/dbdoc_engine/update_stats_dbdocs.svg +43 -0
  27. data/app/assets/images/dbdoc_engine/wiki.svg +3 -0
  28. data/app/assets/stylesheets/dbdoc_engine/admin.css +176 -0
  29. data/app/assets/stylesheets/dbdoc_engine/admin_header.css +179 -0
  30. data/app/assets/stylesheets/dbdoc_engine/application.scss +1 -0
  31. data/app/assets/stylesheets/dbdoc_engine/changelog.css +173 -0
  32. data/app/assets/stylesheets/dbdoc_engine/dashboard.css +513 -0
  33. data/app/assets/stylesheets/dbdoc_engine/dbdoc_application.css +117 -0
  34. data/app/assets/stylesheets/dbdoc_engine/ecommerce.css +253 -0
  35. data/app/assets/stylesheets/dbdoc_engine/group_details.css +178 -0
  36. data/app/assets/stylesheets/dbdoc_engine/header.css +212 -0
  37. data/app/assets/stylesheets/dbdoc_engine/loading_spinner.css +127 -0
  38. data/app/assets/stylesheets/dbdoc_engine/login.css +213 -0
  39. data/app/assets/stylesheets/dbdoc_engine/schema_diagram.css +149 -0
  40. data/app/assets/stylesheets/dbdoc_engine/sidebar.css +296 -0
  41. data/app/assets/stylesheets/dbdoc_engine/table_details.css +417 -0
  42. data/app/controllers/dbdoc_engine/admin/base_controller.rb +23 -0
  43. data/app/controllers/dbdoc_engine/admin/dashboard_controller.rb +16 -0
  44. data/app/controllers/dbdoc_engine/admin/data_transfer_controller.rb +63 -0
  45. data/app/controllers/dbdoc_engine/admin/db_design_dynamic_tables_controller.rb +198 -0
  46. data/app/controllers/dbdoc_engine/admin/db_design_table_groups_controller.rb +107 -0
  47. data/app/controllers/dbdoc_engine/application_controller.rb +65 -0
  48. data/app/controllers/dbdoc_engine/concerns/internationalization.rb +57 -0
  49. data/app/controllers/dbdoc_engine/db_doc_sessions_controller.rb +33 -0
  50. data/app/controllers/dbdoc_engine/home_controller.rb +79 -0
  51. data/app/controllers/dbdoc_engine/schema_diagram_controller.rb +293 -0
  52. data/app/helper/dbdoc_engine/application_helper.rb +35 -0
  53. data/app/helpers/dbdoc_engine/application_helper.rb +4 -0
  54. data/app/helpers/dbdoc_engine/changelogs_helper.rb +27 -0
  55. data/app/helpers/dbdoc_engine/column_helper.rb +30 -0
  56. data/app/helpers/dbdoc_engine/db_design_dynamic_tables_helper.rb +15 -0
  57. data/app/helpers/dbdoc_engine/home_helper.rb +75 -0
  58. data/app/javascript/dbdoc_engine/application.js +12 -0
  59. data/app/javascript/dbdoc_engine/controllers/application.js +29 -0
  60. data/app/javascript/dbdoc_engine/controllers/auto_submit_controller.js +17 -0
  61. data/app/javascript/dbdoc_engine/controllers/chart_controller.js +58 -0
  62. data/app/javascript/dbdoc_engine/controllers/column-type_controller.js +149 -0
  63. data/app/javascript/dbdoc_engine/controllers/column_controller.js +362 -0
  64. data/app/javascript/dbdoc_engine/controllers/column_search_controller.js +42 -0
  65. data/app/javascript/dbdoc_engine/controllers/dbdoc_accordion_controller.js +42 -0
  66. data/app/javascript/dbdoc_engine/controllers/ecommerce_controller.js +73 -0
  67. data/app/javascript/dbdoc_engine/controllers/group_details_controller.js +88 -0
  68. data/app/javascript/dbdoc_engine/controllers/import_export_controller.js +200 -0
  69. data/app/javascript/dbdoc_engine/controllers/index.js +9 -0
  70. data/app/javascript/dbdoc_engine/controllers/language_controller.js +100 -0
  71. data/app/javascript/dbdoc_engine/controllers/loading_spinner_controller.js +48 -0
  72. data/app/javascript/dbdoc_engine/controllers/login_controller.js +75 -0
  73. data/app/javascript/dbdoc_engine/controllers/notification_controller.js +15 -0
  74. data/app/javascript/dbdoc_engine/controllers/schema_diagram_controller.js +1129 -0
  75. data/app/javascript/dbdoc_engine/controllers/select2_controller.js +67 -0
  76. data/app/javascript/dbdoc_engine/controllers/sidebar_controller.js +943 -0
  77. data/app/javascript/dbdoc_engine/controllers/table_details_controller.js +245 -0
  78. data/app/javascript/dbdoc_engine/controllers/table_group_validation_controller.js +148 -0
  79. data/app/javascript/dbdoc_engine/controllers/table_validation_controller.js +423 -0
  80. data/app/jobs/dbdoc_engine/application_job.rb +4 -0
  81. data/app/mailers/dbdoc_engine/application_mailer.rb +6 -0
  82. data/app/models/dbdoc_engine/application_record.rb +6 -0
  83. data/app/models/dbdoc_engine/concerns/soft_deletable.rb +30 -0
  84. data/app/models/dbdoc_engine/db_design_changelog.rb +44 -0
  85. data/app/models/dbdoc_engine/db_design_dynamic_column.rb +211 -0
  86. data/app/models/dbdoc_engine/db_design_dynamic_table.rb +124 -0
  87. data/app/models/dbdoc_engine/db_design_table_group.rb +88 -0
  88. data/app/models/dbdoc_engine/user.rb +21 -0
  89. data/app/queries/dbdoc_engine/admin_dashboard_queries.rb +71 -0
  90. data/app/queries/dbdoc_engine/db_design_changelog_queries.rb +68 -0
  91. data/app/queries/dbdoc_engine/db_design_dynamic_column_queries.rb +37 -0
  92. data/app/queries/dbdoc_engine/db_design_dynamic_table_commands.rb +106 -0
  93. data/app/queries/dbdoc_engine/db_design_dynamic_table_queries.rb +194 -0
  94. data/app/queries/dbdoc_engine/db_design_table_group_queries.rb +154 -0
  95. data/app/services/dbdoc_engine/db_design_dynamic_table_export_service.rb +38 -0
  96. data/app/services/dbdoc_engine/db_design_dynamic_table_handler_service.rb +49 -0
  97. data/app/services/dbdoc_engine/db_design_dynamic_tables_service.rb +21 -0
  98. data/app/services/dbdoc_engine/error_handler_service.rb +43 -0
  99. data/app/services/dbdoc_engine/schema_rb_import_service.rb +194 -0
  100. data/app/services/dbdoc_engine/schema_rb_parser_service.rb +339 -0
  101. data/app/services/dbdoc_engine/table_filter_service.rb +35 -0
  102. data/app/services/dbdoc_engine/table_groups_service.rb +199 -0
  103. data/app/services/dbdoc_engine/table_management_service.rb +192 -0
  104. data/app/views/dbdoc_engine/admin/dashboard/_action_badge.html.erb +11 -0
  105. data/app/views/dbdoc_engine/admin/dashboard/_changelog_rows.html.erb +22 -0
  106. data/app/views/dbdoc_engine/admin/dashboard/_changelog_table_headers.html.erb +8 -0
  107. data/app/views/dbdoc_engine/admin/dashboard/_filter_fields.html.erb +43 -0
  108. data/app/views/dbdoc_engine/admin/dashboard/index.html.erb +159 -0
  109. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_column_fields.html.erb +225 -0
  110. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_deleted_table_index.html.erb +110 -0
  111. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_foreign_key_fields.html.erb +51 -0
  112. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_form.html.erb +75 -0
  113. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_recent_activity.html.erb +39 -0
  114. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_columns.html.erb +127 -0
  115. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_index.html.erb +109 -0
  116. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/_table_information.html.erb +99 -0
  117. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/deleted_tables.html.erb +95 -0
  118. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/edit.html.erb +23 -0
  119. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/export_all_to_excel.xlsx.axlsx +240 -0
  120. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/export_to_excel.xlsx.axlsx +135 -0
  121. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/index.html.erb +109 -0
  122. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/new.html.erb +25 -0
  123. data/app/views/dbdoc_engine/admin/db_design_dynamic_tables/show_table_info.html.erb +125 -0
  124. data/app/views/dbdoc_engine/admin/db_design_table_groups/_deleted_table_groups_list.html.erb +75 -0
  125. data/app/views/dbdoc_engine/admin/db_design_table_groups/_form.html.erb +88 -0
  126. data/app/views/dbdoc_engine/admin/db_design_table_groups/_table_groups_list.html.erb +82 -0
  127. data/app/views/dbdoc_engine/admin/db_design_table_groups/deleted_groups.html.erb +60 -0
  128. data/app/views/dbdoc_engine/admin/db_design_table_groups/edit.html.erb +25 -0
  129. data/app/views/dbdoc_engine/admin/db_design_table_groups/index.html.erb +85 -0
  130. data/app/views/dbdoc_engine/admin/db_design_table_groups/new.html.erb +26 -0
  131. data/app/views/dbdoc_engine/db_doc_sessions/new.html.erb +59 -0
  132. data/app/views/dbdoc_engine/home/changelog_details.html.erb +80 -0
  133. data/app/views/dbdoc_engine/home/changelogs.html.erb +20 -0
  134. data/app/views/dbdoc_engine/home/group_details.html.erb +94 -0
  135. data/app/views/dbdoc_engine/home/index.html.erb +11 -0
  136. data/app/views/dbdoc_engine/home/partials/_action_badge.html.erb +11 -0
  137. data/app/views/dbdoc_engine/home/partials/_breadcrumb_navigation.html.erb +30 -0
  138. data/app/views/dbdoc_engine/home/partials/_changelog_rows.html.erb +35 -0
  139. data/app/views/dbdoc_engine/home/partials/_changelog_table_headers.html.erb +16 -0
  140. data/app/views/dbdoc_engine/home/partials/_column_headers.html.erb +23 -0
  141. data/app/views/dbdoc_engine/home/partials/_column_row.html.erb +157 -0
  142. data/app/views/dbdoc_engine/home/partials/_filter_form.html.erb +47 -0
  143. data/app/views/dbdoc_engine/home/partials/_group_section.html.erb +84 -0
  144. data/app/views/dbdoc_engine/home/partials/_pagination.html.erb +5 -0
  145. data/app/views/dbdoc_engine/home/partials/_stats_container.html.erb +46 -0
  146. data/app/views/dbdoc_engine/home/partials/_table_groups.html.erb +7 -0
  147. data/app/views/dbdoc_engine/home/partials/_table_information_section.html.erb +50 -0
  148. data/app/views/dbdoc_engine/home/partials/_table_section.html.erb +48 -0
  149. data/app/views/dbdoc_engine/home/table_details.html.erb +9 -0
  150. data/app/views/dbdoc_engine/schema_diagram/index.html.erb +102 -0
  151. data/app/views/dbdoc_engine/shared/_admin_header.html.erb +78 -0
  152. data/app/views/dbdoc_engine/shared/_header.html.erb +94 -0
  153. data/app/views/dbdoc_engine/shared/_js_translations.html.erb +3 -0
  154. data/app/views/dbdoc_engine/shared/_language_button.html.erb +14 -0
  155. data/app/views/dbdoc_engine/shared/_sidebar.html.erb +128 -0
  156. data/app/views/kaminari/dbdoc_engine/_first_page.html.erb +3 -0
  157. data/app/views/kaminari/dbdoc_engine/_gap.html.erb +3 -0
  158. data/app/views/kaminari/dbdoc_engine/_last_page.html.erb +3 -0
  159. data/app/views/kaminari/dbdoc_engine/_next_page.html.erb +3 -0
  160. data/app/views/kaminari/dbdoc_engine/_page.html.erb +9 -0
  161. data/app/views/kaminari/dbdoc_engine/_paginator.html.erb +17 -0
  162. data/app/views/kaminari/dbdoc_engine/_prev_page.html.erb +3 -0
  163. data/app/views/layouts/dbdoc_engine/application.html.erb +107 -0
  164. data/app/views/layouts/dbdoc_engine/header.html.erb +108 -0
  165. data/config/importmap.rb +11 -0
  166. data/config/locales/en.yml +307 -0
  167. data/config/locales/ja.yml +306 -0
  168. data/config/routes.rb +73 -0
  169. data/db/migrate/rails7/20250227060610_create_db_design_table_groups.rb +15 -0
  170. data/db/migrate/rails7/20250227094626_create_db_design_dynamic_tables.rb +19 -0
  171. data/db/migrate/rails7/20250228022732_create_db_design_dynamic_columns.rb +34 -0
  172. data/db/migrate/rails7/20250401051453_create_db_design_changelogs.rb +26 -0
  173. data/db/migrate/rails7/20250411040822_create_users.rb +14 -0
  174. data/db/migrate/rails7/20250421080851_add_missing_indexes_to_dbdoc_tables.rb +23 -0
  175. data/db/migrate/rails8/20250227060610_create_db_design_table_groups.rb +15 -0
  176. data/db/migrate/rails8/20250227094626_create_db_design_dynamic_tables.rb +19 -0
  177. data/db/migrate/rails8/20250228022732_create_db_design_dynamic_columns.rb +34 -0
  178. data/db/migrate/rails8/20250401051453_create_db_design_changelogs.rb +26 -0
  179. data/db/migrate/rails8/20250411040822_create_users.rb +14 -0
  180. data/db/migrate/rails8/20250421080851_add_missing_indexes_to_dbdoc_tables.rb +23 -0
  181. data/db/seeds.rb +28 -0
  182. data/lib/dbdoc_engine/engine.rb +57 -0
  183. data/lib/dbdoc_engine/version.rb +3 -0
  184. data/lib/dbdoc_engine.rb +9 -0
  185. data/lib/generators/dbdoc_engine/install/install_generator.rb +245 -0
  186. data/lib/generators/dbdoc_engine/uninstall/uninstall_generator.rb +196 -0
  187. data/lib/tasks/dbdoc_engine_tasks.rake +44 -0
  188. data/public/dbdoc_engine_assets/images/camel_chess_head.png +0 -0
  189. data/public/dbdoc_engine_assets/images/dblogo.svg +4 -0
  190. data/public/dbdoc_engine_assets/images/japan_circle.png +0 -0
  191. data/public/dbdoc_engine_assets/images/king_chess_head.png +0 -0
  192. data/public/dbdoc_engine_assets/images/login-bg.svg +44 -0
  193. data/public/dbdoc_engine_assets/images/logo.png +0 -0
  194. data/public/dbdoc_engine_assets/images/logo.svg +12 -0
  195. data/public/dbdoc_engine_assets/images/queen_chess_head.png +0 -0
  196. data/public/dbdoc_engine_assets/images/soldier_chess_headd.png +0 -0
  197. data/public/dbdoc_engine_assets/images/uk_circle_transparent.png +0 -0
  198. metadata +415 -0
@@ -0,0 +1,199 @@
1
+ # Service for table groups controller
2
+ module DbdocEngine
3
+ class TableGroupsService
4
+ def initialize(params)
5
+ @params = params
6
+ @errors = []
7
+ end
8
+
9
+ # Fetch table groups with optional search and soft-delete filters
10
+ def fetch_table_groups
11
+ showing_deleted = @params[:show_deleted] == "true"
12
+ query = DbdocEngine::DbDesignTableGroupQueries.search_all_groups(@params[:search], showing_deleted)
13
+ [ query.page(@params[:page]).per(6), showing_deleted ]
14
+ end
15
+
16
+ # Fetch only soft-deleted table groups
17
+ def fetch_deleted_groups
18
+ query = DbdocEngine::DbDesignTableGroupQueries.search_deleted_groups(@params[:search])
19
+ query.page(@params[:page]).per(5)
20
+ end
21
+
22
+ # Create a new table group
23
+ def create_group
24
+ table_group = DbdocEngine::DbDesignTableGroupQueries.create_table_group(table_group_params)
25
+ if table_group.persisted?
26
+ { success: true, message: I18n.t("dbdoc.group_creation") }
27
+ else
28
+ { success: false, errors: table_group.errors.full_messages, table_group: table_group }
29
+ end
30
+ end
31
+
32
+ # Update an existing table group
33
+ def update_group(table_group)
34
+ if DbdocEngine::DbDesignTableGroupQueries.update_table_group(table_group, table_group_params)
35
+ { success: true, message: I18n.t("dbdoc.group_updation") }
36
+ else
37
+ { success: false, errors: table_group.errors.full_messages, table_group: table_group }
38
+ end
39
+ end
40
+
41
+ # Soft delete a table group
42
+ def delete_group(table_group)
43
+ # Check if the group has any active tables
44
+ if DbdocEngine::DbDesignDynamicTableQueries.group_has_active_tables?(table_group.id, false)
45
+ add_error(I18n.t("dbdoc.group_delete_validation"))
46
+ return failure_result
47
+ end
48
+
49
+ # Store original name before changing it
50
+ original_name = table_group.group_name
51
+
52
+ # Append ID to the group name for soft deletion
53
+ new_name = "#{original_name}_deleted_#{table_group.id}"
54
+
55
+ # Update the group name before soft deletion
56
+ table_group.group_name = new_name
57
+
58
+ if table_group.soft_delete
59
+ # Log the soft deletion to changelog
60
+ DbdocEngine::DbDesignChangelogQueries.log_change(
61
+ DbDesignChangelog::ACTION_DELETE,
62
+ DbDesignChangelog::ENTITY_TABLE_GROUP,
63
+ original_name,
64
+ "Soft deleted table group '#{original_name}'",
65
+ table_group.updated_by,
66
+ {
67
+ group_name: original_name,
68
+ group_color: table_group.group_color,
69
+ deleted: true
70
+ },
71
+ nil
72
+ )
73
+ { success: true, message: I18n.t("dbdoc.group_deletion") }
74
+ else
75
+ add_error("Unable to delete table group.")
76
+ failure_result
77
+ end
78
+ end
79
+
80
+ # Restore a soft-deleted table group
81
+ def restore_group(table_group)
82
+ # Extract the original name from the current name
83
+ original_name = extract_original_name(table_group.group_name)
84
+
85
+ # Check if another group with the same name exists
86
+ if DbdocEngine::DbDesignTableGroupQueries.group_exists?(original_name)
87
+ add_error(I18n.t("dbdoc.group_restore_validation"))
88
+ return failure_result
89
+ end
90
+
91
+ # Restore the original name
92
+ table_group.group_name = original_name
93
+
94
+ # Use query class for restoration
95
+ if DbdocEngine::DbDesignTableGroupQueries.restore(table_group)
96
+ # Use query class for logging
97
+ DbdocEngine::DbDesignChangelogQueries.log_change(
98
+ DbDesignChangelog::ACTION_UPDATE,
99
+ DbDesignChangelog::ENTITY_TABLE_GROUP,
100
+ original_name,
101
+ "Restored deleted table group '#{original_name}'",
102
+ "Super Admin",
103
+ { deleted: true, group_name: table_group.group_name },
104
+ { deleted: false, group_name: original_name }
105
+ )
106
+ message = I18n.t("dbdoc.success.group_restored", original_name: original_name)
107
+ { success: true, message: message }
108
+ else
109
+ add_error("Unable to restore group '#{original_name}'.")
110
+ failure_result
111
+ end
112
+ end
113
+
114
+ # Permanently delete a table group
115
+ def permanent_delete_group(table_group)
116
+ # Check if the group has any associated tables (including soft-deleted ones)
117
+ if DbdocEngine::DbDesignDynamicTableQueries.group_has_active_tables?(table_group.id, true)
118
+ add_error(I18n.t("dbdoc.group_permanent_delete_validation"))
119
+ return failure_result
120
+ end
121
+
122
+ # Extract the original name from the current name
123
+ original_name = extract_original_name(table_group.group_name)
124
+
125
+ # Store table group info before destruction for logging
126
+ group_info = {
127
+ group_name: original_name,
128
+ group_color: table_group.group_color,
129
+ permanently_deleted: true
130
+ }
131
+
132
+ # Use query class for permanent deletion
133
+ if DbdocEngine::DbDesignTableGroupQueries.permanent_delete(table_group)
134
+ # Use query class for logging
135
+ DbdocEngine::DbDesignChangelogQueries.log_change(
136
+ DbDesignChangelog::ACTION_DELETE,
137
+ DbDesignChangelog::ENTITY_TABLE_GROUP,
138
+ original_name,
139
+ "Permanently deleted table group '#{original_name}'",
140
+ "Super Admin",
141
+ group_info,
142
+ nil
143
+ )
144
+ message = I18n.t("dbdoc.success.table_group_permanently_deleted", original_name: original_name)
145
+ { success: true, message: message }
146
+ else
147
+ add_error("Unable to permanently delete table group '#{original_name}'.")
148
+ failure_result
149
+ end
150
+ end
151
+
152
+ # Get existing group names, excluding the current one being edited
153
+ def existing_group_names
154
+ DbdocEngine::DbDesignTableGroupQueries.group_names_except(@params[:id])
155
+ end
156
+
157
+ private
158
+
159
+ # Extract the original group name from a deleted group name
160
+ def extract_original_name(deleted_name)
161
+ # Pattern for names like "original_name_deleted_123"
162
+ if deleted_name =~ /(.+)_deleted_\d+$/
163
+ ::Regexp.last_match(1) # Return the captured group (everything before "_deleted_")
164
+ else
165
+ deleted_name # If pattern doesn't match, return the name as is
166
+ end
167
+ end
168
+
169
+ # Check if there's already a group with the given name
170
+ def name_conflict?(group_name)
171
+ DbdocEngine::DbDesignTableGroupQueries.group_exists?(group_name)
172
+ end
173
+
174
+ # Strong parameters
175
+ def table_group_params
176
+ @params.require(:db_design_table_group).permit(:group_name, :group_color, :created_by, :updated_by)
177
+ end
178
+
179
+ def associated_tables?(table_group, include_deleted = false)
180
+ DbdocEngine::DbDesignDynamicTableQueries.group_has_active_tables?(table_group.id, include_deleted)
181
+ end
182
+
183
+ # Adds an error message to the errors array
184
+ def add_error(message)
185
+ @errors ||= []
186
+ @errors << message
187
+ end
188
+
189
+ # Returns a success result
190
+ def success_result
191
+ { success: true }
192
+ end
193
+
194
+ # Returns a failure result with errors
195
+ def failure_result
196
+ { success: false, errors: @errors }
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,192 @@
1
+ # Service for managing table lifecycle operations
2
+ module DbdocEngine
3
+ class TableManagementService
4
+ attr_reader :table, :errors
5
+
6
+ def initialize(table)
7
+ @table = table
8
+ @errors = []
9
+ end
10
+
11
+ # Soft deletes a table if it's not referenced by other tables
12
+ def soft_delete
13
+ if foreign_key_dependencies?
14
+ add_error(I18n.t("dbdoc.errors.messages.cannot_delete_referenced_table", table_name: table.table_name))
15
+ return failure_result
16
+ end
17
+
18
+ # Store original name before changing it
19
+ original_name = table.table_name
20
+
21
+ # Append ID to the table name for soft deletion
22
+ new_name = "#{original_name}_deleted_#{table.id}"
23
+
24
+ if table.update(table_name: new_name, deleted_at: Time.current)
25
+ # Log the soft deletion to changelog
26
+ DbDesignChangelog.log_change(
27
+ DbDesignChangelog::ACTION_DELETE,
28
+ DbDesignChangelog::ENTITY_TABLE,
29
+ original_name,
30
+ "Soft deleted table '#{original_name}'",
31
+ table.updated_by,
32
+ {
33
+ table_name: original_name,
34
+ group_id: table.db_design_table_group_id,
35
+ description: table.description,
36
+ deleted: true
37
+ },
38
+ nil
39
+ )
40
+ success_result
41
+ else
42
+ add_error("Unable to delete table '#{original_name}'.")
43
+ failure_result
44
+ end
45
+ end
46
+
47
+ # Permanently removes a table from the database
48
+ def permanent_destroy
49
+ # Extract original table name from the current name
50
+ original_name = extract_original_name(table.table_name)
51
+
52
+ table_info = {
53
+ table_name: original_name,
54
+ group_id: table.db_design_table_group_id,
55
+ description: table.description,
56
+ permanently_deleted: true
57
+ }
58
+
59
+ if table.destroy
60
+ # Log the permanent deletion to changelog
61
+ DbDesignChangelog.log_change(
62
+ DbDesignChangelog::ACTION_DELETE,
63
+ DbDesignChangelog::ENTITY_TABLE,
64
+ original_name,
65
+ "Permanently deleted table '#{original_name}'",
66
+ "Super Admin",
67
+ table_info,
68
+ nil
69
+ )
70
+ success_result
71
+ else
72
+ add_error("Unable to permanently delete table '#{original_name}'.")
73
+ failure_result
74
+ end
75
+ end
76
+
77
+ # Restores a soft-deleted table
78
+ def restore
79
+ # Extract the original name from the current name
80
+ original_name = extract_original_name(table.table_name)
81
+
82
+ # Check if another table with the same name exists
83
+ if name_conflict?(original_name)
84
+ add_error(I18n.t("dbdoc.errors.messages.unable_to_restore_table_duplicate", original_name: original_name))
85
+ return failure_result
86
+ end
87
+
88
+ # Check if the table's group is soft-deleted
89
+ if table_group_is_deleted?
90
+ group_name = get_table_group_name
91
+ add_error(I18n.t("dbdoc.errors.messages.cannot_restore_table_deleted_group",
92
+ table_name: original_name,
93
+ group_name: group_name))
94
+ return failure_result
95
+ end
96
+
97
+ if table.update(table_name: original_name, deleted_at: nil)
98
+ # Log the restoration to changelog
99
+ DbDesignChangelog.log_change(
100
+ DbDesignChangelog::ACTION_UPDATE,
101
+ DbDesignChangelog::ENTITY_TABLE,
102
+ original_name,
103
+ "Restored deleted table '#{original_name}'",
104
+ "Super Admin",
105
+ { deleted: true },
106
+ { deleted: false }
107
+ )
108
+ success_result
109
+ else
110
+ add_error("Unable to restore table '#{original_name}'.")
111
+ failure_result
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ # Extract the original table name from a deleted table name
118
+ def extract_original_name(deleted_name)
119
+ # Pattern for names like "original_name_deleted_123"
120
+ if deleted_name =~ /(.+)_deleted_\d+$/
121
+ ::Regexp.last_match(1) # Return the captured group (everything before "_deleted_")
122
+ else
123
+ deleted_name # If pattern doesn't match, return the name as is
124
+ end
125
+ end
126
+
127
+ # Checks if any other table has a foreign key reference to this table
128
+ def foreign_key_dependencies?
129
+ # Check if any active (non-deleted) tables reference this table
130
+ DbDesignDynamicColumn
131
+ .joins(:db_design_dynamic_table)
132
+ .where(foreign_table_name: table.table_name)
133
+ .where(db_design_dynamic_tables: { deleted_at: nil })
134
+ .exists?
135
+ end
136
+
137
+ # Check if there's already a table with the given name
138
+ def name_conflict?(table_name)
139
+ DbDesignDynamicTable
140
+ .without_deleted
141
+ .where(table_name: table_name)
142
+ .where.not(id: table.id)
143
+ .exists?
144
+ end
145
+
146
+ # Check if the table's group is soft-deleted
147
+ def table_group_is_deleted?
148
+ return false if table.db_design_table_group_id.blank?
149
+
150
+ group = DbdocEngine::DbDesignTableGroup.with_deleted.find_by(id: table.db_design_table_group_id)
151
+ group&.deleted_at.present?
152
+ end
153
+
154
+ # Get the table group name (including soft-deleted groups)
155
+ def get_table_group_name
156
+ return "Unknown Group" if table.db_design_table_group_id.blank?
157
+
158
+ group = DbdocEngine::DbDesignTableGroup.with_deleted.find_by(id: table.db_design_table_group_id)
159
+ if group
160
+ # Extract original name if it's a deleted group name
161
+ extract_original_group_name(group.group_name)
162
+ else
163
+ "Unknown Group"
164
+ end
165
+ end
166
+
167
+ # Extract the original group name from a deleted group name
168
+ def extract_original_group_name(deleted_name)
169
+ # Pattern for names like "original_name_deleted_123"
170
+ if deleted_name =~ /(.+)_deleted_\d+$/
171
+ ::Regexp.last_match(1) # Return the captured group (everything before "_deleted_")
172
+ else
173
+ deleted_name # If pattern doesn't match, return the name as is
174
+ end
175
+ end
176
+
177
+ # Adds an error message to the errors array
178
+ def add_error(message)
179
+ @errors << message
180
+ end
181
+
182
+ # Returns a success result
183
+ def success_result
184
+ { success: true }
185
+ end
186
+
187
+ # Returns a failure result with errors
188
+ def failure_result
189
+ { success: false, errors: @errors }
190
+ end
191
+ end
192
+ end
@@ -0,0 +1,11 @@
1
+ <% case action_type %>
2
+ <% when 'create' %>
3
+ <!-- Display a green badge when the action type is 'create' -->
4
+ <span class="badge bg-success"><%= t('dbdoc.create') %></span>
5
+ <% when 'update' %>
6
+ <!-- Display a yellow badge when the action type is 'update' -->
7
+ <span class="badge bg-warning text-dark"><%= t('dbdoc.update') %></span>
8
+ <% when 'delete' %>
9
+ <!-- Display a red badge when the action type is 'delete' -->
10
+ <span class="badge bg-danger"><%= t('dbdoc.delete') %></span>
11
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <% if changelogs.any? %>
2
+ <!-- Check if there are any changelog records -->
3
+ <% changelogs.each do |log| %>
4
+ <!-- Iterate over each changelog entry -->
5
+ <tr>
6
+ <!-- Display the timestamp of the change, formatted as 'YYYY-MM-DD HH:MM:SS' -->
7
+ <td class='contents-changelog'><%= log.change_timestamp.strftime('%Y-%m-%d %H:%M:%S') %></td>
8
+ <td>
9
+ <!-- Render a partial to display an action badge based on the action type -->
10
+ <%= render 'dbdoc_engine/admin/dashboard/action_badge', action_type: log.action_type %>
11
+ </td>
12
+ <!-- Display the change description with a max width of 250px and text truncation -->
13
+ <td class='contents-changelog'><%= log.changed_by %> <%= log.description %></td>
14
+ <!-- Display the name of the user who made the change -->
15
+ </tr>
16
+ <% end %>
17
+ <% else %>
18
+ <!-- If no changelogs exist, display a message -->
19
+ <tr>
20
+ <td colspan='7' class='text-center text-muted'>No changes found matching your criteria.</td>
21
+ </tr>
22
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <tr>
2
+ <!-- Table header for the date and time of the change -->
3
+ <th class='text-custom-secondary changelog-headers-1'><%= t('dbdoc.date_and_time') %></th>
4
+ <!-- Table header for the type of action performed (e.g., create, update, delete) -->
5
+ <th class='text-custom-secondary changelog-headers-2'><%= t('dbdoc.action') %></th>
6
+ <!-- Table header for a brief description of the change made -->
7
+ <th class='text-custom-secondary'><%= t('dbdoc.description') %></th>
8
+ </tr>
@@ -0,0 +1,43 @@
1
+ <!-- Action Type Dropdown Filter -->
2
+ <div class='col-md-3'>
3
+ <div class='input-group'>
4
+ <!-- Icon for action type -->
5
+ <span class='input-group-text'><i class='bi bi-gear'></i></span>
6
+ <!-- Dropdown for selecting action type (create, update, delete, etc.) -->
7
+ <%= f.select :action_type, action_type_options,
8
+ { selected: params[:action_type] },
9
+ class: 'form-select salect' %>
10
+ </div>
11
+ </div>
12
+
13
+ <!-- Date From Filter -->
14
+ <div class='col-md-3'>
15
+ <div class='input-group'>
16
+ <!-- Icon for date from -->
17
+ <span class='input-group-text'><i class='bi bi-calendar'></i></span>
18
+ <!-- Date picker for start date filter -->
19
+ <%= f.date_field :date_from, value: params[:date_from],
20
+ class: 'form-control cantrol', placeholder: 'From Date' %>
21
+ </div>
22
+ </div>
23
+
24
+ <!-- Date To Filter -->
25
+ <div class='col-md-3'>
26
+ <div class='input-group'>
27
+ <!-- Icon for date to -->
28
+ <span class='input-group-text'><i class='bi bi-calendar'></i></span>
29
+ <!-- Date picker for end date filter -->
30
+ <%= f.date_field :date_to, value: params[:date_to],
31
+ class: 'form-control cantrol', placeholder: 'To Date' %>
32
+ </div>
33
+ </div>
34
+
35
+ <!-- Filter Action Buttons -->
36
+ <div class='col-md-12 mt-3'>
37
+ <div class='d-flex gap-2'>
38
+ <!-- Button to apply filters -->
39
+ <%= f.submit 'Apply Filters', class: 'btn applybutton' %>
40
+ <!-- Button to clear filters and reset the form -->
41
+ <%= link_to 'Clear Filters', dbdoc_engine.admin_dashboard_path, class: 'btn clear-button' %>
42
+ </div>
43
+ </div>
@@ -0,0 +1,159 @@
1
+ <%# Dashboard %>
2
+ <div class='dashboard-container'>
3
+ <div class="d-flex align-items-center justify-content-between mb-4">
4
+ <h1 class='fw-bold dashboard-title mb-0'><%= t('dbdoc.dashboard') %></h1>
5
+ <span class="text-muted" style="font-size:13px;"><%= Time.current.strftime("%A, %B %d, %Y") %></span>
6
+ </div>
7
+
8
+ <%# Top Stats Row %>
9
+ <div class='dashboard-stats-row'>
10
+ <div class='stat-card stat-profile'>
11
+ <div class='stat-icon-wrap stat-icon-profile'>
12
+ <% if current_user.role == 'superadmin' %>
13
+ <img src='/dbdoc_engine_assets/images/king_chess_head.png' alt='Superadmin' class='stat-avatar' />
14
+ <% elsif current_user.role == 'admin' %>
15
+ <img src='/dbdoc_engine_assets/images/queen_chess_head.png' alt='Admin' class='stat-avatar' />
16
+ <% else %>
17
+ <img src='/dbdoc_engine_assets/images/camel_chess_head.png' alt='User' class='stat-avatar' />
18
+ <% end %>
19
+ </div>
20
+ <div class='stat-info'>
21
+ <span class='stat-label'><%= t('dbdoc.accountprofile') %></span>
22
+ <span class='stat-value stat-value-sm'>
23
+ <%= case current_user.role
24
+ when 'superadmin' then t('dbdoc.superadmin')
25
+ when 'admin' then t('dbdoc.admin')
26
+ else t('dbdoc.user')
27
+ end %>
28
+ </span>
29
+ </div>
30
+ </div>
31
+
32
+ <div class='stat-card'>
33
+ <div class='stat-icon-wrap stat-icon-groups'><i class="bi bi-collection"></i></div>
34
+ <div class='stat-info'>
35
+ <span class='stat-label'><%= t('dbdoc.groupsdash') %></span>
36
+ <span class='stat-value'><%= @groups_count %></span>
37
+ </div>
38
+ </div>
39
+
40
+ <div class='stat-card'>
41
+ <div class='stat-icon-wrap stat-icon-tables'><i class="bi bi-table"></i></div>
42
+ <div class='stat-info'>
43
+ <span class='stat-label'><%= t('dbdoc.tablesdash') %></span>
44
+ <span class='stat-value'><%= @tables_count %></span>
45
+ </div>
46
+ </div>
47
+
48
+ <div class='stat-card'>
49
+ <div class='stat-icon-wrap stat-icon-columns'><i class="bi bi-layout-three-columns"></i></div>
50
+ <div class='stat-info'>
51
+ <span class='stat-label'><%= t('dbdoc.columnsdash') %></span>
52
+ <span class='stat-value'><%= @columns_count %></span>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ <%# Action Cards Row %>
58
+ <div class='dashboard-action-row'>
59
+ <%= link_to dbdoc_engine.export_all_to_excel_admin_db_design_dynamic_tables_path(format: :xlsx),
60
+ target: '_blank', data: { turbo: false }, class: 'action-card action-card-excel text-decoration-none' do %>
61
+ <div class='action-card-icon'><i class="bi bi-file-earmark-spreadsheet"></i></div>
62
+ <div class='action-card-body'>
63
+ <span class='action-card-title'><%= t('dbdoc.downloadexceldash') %></span>
64
+ <span class='action-card-sub'><%= t('dbdoc.alltables') %></span>
65
+ </div>
66
+ <% end %>
67
+
68
+ <div class="action-card" data-controller="import-export" data-import-export-base-path-value="<%= dbdoc_engine.root_path.chomp('/') %>">
69
+ <div class='action-card-icon action-icon-json'><i class="bi bi-filetype-json"></i></div>
70
+ <div class='action-card-body'>
71
+ <span class='action-card-title'><%= t('dbdoc.export_import') %></span>
72
+ <div class='d-flex gap-2 mt-1'>
73
+ <button data-action="click->import-export#export" class="btn btn-sm action-btn action-btn-export" type="button">
74
+ <i class="bi bi-box-arrow-up me-1"></i><%= t('dbdoc.export') %>
75
+ </button>
76
+ <button data-action="click->import-export#showImport" class="btn btn-sm action-btn action-btn-import" type="button">
77
+ <i class="bi bi-box-arrow-in-down me-1"></i><%= t('dbdoc.import') %>
78
+ </button>
79
+ </div>
80
+ </div>
81
+ <div data-import-export-target="dropZone"
82
+ data-action="dragover->import-export#handleDragOver dragleave->import-export#handleDragLeave drop->import-export#handleDrop"
83
+ class="drop-zone">
84
+ <p class="mb-1"><i class="bi bi-cloud-arrow-up me-1"></i><%= t('dbdoc.drop_file_here') %>
85
+ <label for="import-json-file" class="drop-zone-link"><%= t('dbdoc.click_to_select') %></label>
86
+ </p>
87
+ <input type="file" id="import-json-file" data-import-export-target="fileInput" accept=".json" style="display:none;" data-action="change->import-export#handleFileChange">
88
+ </div>
89
+ </div>
90
+
91
+ <div class="action-card" data-controller="import-export" data-import-export-base-path-value="<%= dbdoc_engine.root_path.chomp('/') %>">
92
+ <div class='action-card-icon action-icon-schema'><i class="bi bi-database-gear"></i></div>
93
+ <div class='action-card-body'>
94
+ <span class='action-card-title'><%= t('dbdoc.import_schema') %></span>
95
+ <div class='d-flex gap-2 mt-1'>
96
+ <button data-action="click->import-export#importProjectSchema" class="btn btn-sm action-btn action-btn-schema" type="button">
97
+ <i class="bi bi-database-down me-1"></i><%= t('dbdoc.import_project_schema') %>
98
+ </button>
99
+ <button data-action="click->import-export#showSchemaUpload" class="btn btn-sm action-btn action-btn-upload" type="button">
100
+ <i class="bi bi-upload me-1"></i><%= t('dbdoc.upload_schema_file') %>
101
+ </button>
102
+ </div>
103
+ </div>
104
+ <div data-import-export-target="schemaDropZone"
105
+ data-action="dragover->import-export#handleDragOver dragleave->import-export#handleDragLeave drop->import-export#handleSchemaDrop"
106
+ class="drop-zone">
107
+ <p class="mb-1"><i class="bi bi-cloud-arrow-up me-1"></i><%= t('dbdoc.drop_schema_here') %>
108
+ <label for="import-schema-file" class="drop-zone-link"><%= t('dbdoc.click_to_select') %></label>
109
+ </p>
110
+ <input type="file" id="import-schema-file" data-import-export-target="schemaFileInput" accept=".rb" style="display:none;" data-action="change->import-export#handleSchemaFileChange">
111
+ </div>
112
+ </div>
113
+ </div>
114
+
115
+ <%# Main Content: Activity + Chart %>
116
+ <div class='dashboard-main-row'>
117
+ <div class='dashboard-table-section'>
118
+ <div class='d-flex align-items-center justify-content-between mb-3'>
119
+ <div class='dashboard-section-title mb-0'><i class="bi bi-clock-history me-2"></i><%= t('dbdoc.recentactivity') %></div>
120
+ <div class='filter-buttons'>
121
+ <%= link_to t('dbdoc.allactions'), dbdoc_engine.admin_dashboard_path,
122
+ class: "filter-button #{'active' unless params[:action_type]}",
123
+ data: { turbo_action: 'replace' } %>
124
+ <%= link_to t('dbdoc.create'), dbdoc_engine.admin_dashboard_path(action_type: 'create'),
125
+ class: "filter-button #{'active' if params[:action_type] == 'create'}",
126
+ data: { turbo_action: 'replace' } %>
127
+ <%= link_to t('dbdoc.update'), dbdoc_engine.admin_dashboard_path(action_type: 'update'),
128
+ class: "filter-button #{'active' if params[:action_type] == 'update'}",
129
+ data: { turbo_action: 'replace' } %>
130
+ <%= link_to t('dbdoc.delete'), dbdoc_engine.admin_dashboard_path(action_type: 'delete'),
131
+ class: "filter-button #{'active' if params[:action_type] == 'delete'}",
132
+ data: { turbo_action: 'replace' } %>
133
+ </div>
134
+ </div>
135
+
136
+ <div class='table-responsive dashboard_table_scroll'>
137
+ <table class='table table-hover mb-0'>
138
+ <thead class='sticky-top dashboard_table_header'>
139
+ <%= render partial: 'dbdoc_engine/admin/dashboard/changelog_table_headers' %>
140
+ </thead>
141
+ <tbody>
142
+ <%= render partial: 'dbdoc_engine/admin/dashboard/changelog_rows', locals: { changelogs: @changelogs } %>
143
+ </tbody>
144
+ </table>
145
+ </div>
146
+ </div>
147
+
148
+ <div class='dashboard-chart-section'>
149
+ <div class='dashboard-section-title mb-3'><i class="bi bi-bar-chart-line me-2"></i><%= t('dbdoc.actionoverview') %></div>
150
+ <div class='dashboard-chart-wrap'>
151
+ <canvas
152
+ id='actionBarChart'
153
+ data-controller='chart'
154
+ data-chart-data-value='<%= @chart_data.to_json %>'>
155
+ </canvas>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>