dbwatcher 1.0.0 → 1.1.1

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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +81 -210
  3. data/app/assets/config/dbwatcher_manifest.js +15 -0
  4. data/app/assets/javascripts/dbwatcher/alpine_registrations.js +39 -0
  5. data/app/assets/javascripts/dbwatcher/auto_init.js +23 -0
  6. data/app/assets/javascripts/dbwatcher/components/base.js +141 -0
  7. data/app/assets/javascripts/dbwatcher/components/changes_table_hybrid.js +1008 -0
  8. data/app/assets/javascripts/dbwatcher/components/diagrams.js +449 -0
  9. data/app/assets/javascripts/dbwatcher/components/summary.js +234 -0
  10. data/app/assets/javascripts/dbwatcher/core/alpine_store.js +138 -0
  11. data/app/assets/javascripts/dbwatcher/core/api_client.js +162 -0
  12. data/app/assets/javascripts/dbwatcher/core/component_loader.js +70 -0
  13. data/app/assets/javascripts/dbwatcher/core/component_registry.js +94 -0
  14. data/app/assets/javascripts/dbwatcher/dbwatcher.js +120 -0
  15. data/app/assets/javascripts/dbwatcher/services/mermaid.js +315 -0
  16. data/app/assets/javascripts/dbwatcher/services/mermaid_service.js +199 -0
  17. data/app/assets/javascripts/dbwatcher/vendor/date-fns-browser.js +99 -0
  18. data/app/assets/javascripts/dbwatcher/vendor/lodash.min.js +140 -0
  19. data/app/assets/javascripts/dbwatcher/vendor/tabulator.min.js +3 -0
  20. data/app/assets/stylesheets/dbwatcher/application.css +423 -0
  21. data/app/assets/stylesheets/dbwatcher/application.scss +15 -0
  22. data/app/assets/stylesheets/dbwatcher/components/_badges.scss +38 -0
  23. data/app/assets/stylesheets/dbwatcher/components/_compact_table.scss +162 -0
  24. data/app/assets/stylesheets/dbwatcher/components/_diagrams.scss +51 -0
  25. data/app/assets/stylesheets/dbwatcher/components/_forms.scss +27 -0
  26. data/app/assets/stylesheets/dbwatcher/components/_navigation.scss +55 -0
  27. data/app/assets/stylesheets/dbwatcher/core/_base.scss +34 -0
  28. data/app/assets/stylesheets/dbwatcher/core/_variables.scss +47 -0
  29. data/app/assets/stylesheets/dbwatcher/vendor/tabulator.min.css +2 -0
  30. data/app/controllers/dbwatcher/api/v1/sessions_controller.rb +64 -0
  31. data/app/controllers/dbwatcher/base_controller.rb +8 -2
  32. data/app/controllers/dbwatcher/dashboard_controller.rb +8 -0
  33. data/app/controllers/dbwatcher/sessions_controller.rb +25 -10
  34. data/app/helpers/dbwatcher/component_helper.rb +29 -0
  35. data/app/helpers/dbwatcher/diagram_helper.rb +110 -0
  36. data/app/helpers/dbwatcher/session_helper.rb +3 -2
  37. data/app/views/dbwatcher/sessions/_changes_tab.html.erb +265 -0
  38. data/app/views/dbwatcher/sessions/_diagrams_tab.html.erb +166 -0
  39. data/app/views/dbwatcher/sessions/_session_header.html.erb +11 -0
  40. data/app/views/dbwatcher/sessions/_summary_tab.html.erb +88 -0
  41. data/app/views/dbwatcher/sessions/_tab_navigation.html.erb +12 -0
  42. data/app/views/dbwatcher/sessions/changes.html.erb +21 -0
  43. data/app/views/dbwatcher/sessions/components/changes/_filters.html.erb +44 -0
  44. data/app/views/dbwatcher/sessions/components/changes/_table_list.html.erb +96 -0
  45. data/app/views/dbwatcher/sessions/diagrams.html.erb +21 -0
  46. data/app/views/dbwatcher/sessions/index.html.erb +14 -10
  47. data/app/views/dbwatcher/sessions/shared/_layout.html.erb +8 -0
  48. data/app/views/dbwatcher/sessions/shared/_navigation.html.erb +35 -0
  49. data/app/views/dbwatcher/sessions/shared/_session_header.html.erb +25 -0
  50. data/app/views/dbwatcher/sessions/show.html.erb +3 -346
  51. data/app/views/dbwatcher/sessions/summary.html.erb +21 -0
  52. data/app/views/layouts/dbwatcher/application.html.erb +125 -247
  53. data/bin/compile_scss +49 -0
  54. data/config/routes.rb +26 -0
  55. data/lib/dbwatcher/configuration.rb +102 -8
  56. data/lib/dbwatcher/engine.rb +17 -7
  57. data/lib/dbwatcher/services/analyzers/session_data_processor.rb +98 -0
  58. data/lib/dbwatcher/services/analyzers/table_summary_builder.rb +202 -0
  59. data/lib/dbwatcher/services/api/base_api_service.rb +100 -0
  60. data/lib/dbwatcher/services/api/changes_data_service.rb +112 -0
  61. data/lib/dbwatcher/services/api/diagram_data_service.rb +145 -0
  62. data/lib/dbwatcher/services/api/summary_data_service.rb +158 -0
  63. data/lib/dbwatcher/services/base_service.rb +64 -0
  64. data/lib/dbwatcher/services/diagram_analyzers/base_analyzer.rb +162 -0
  65. data/lib/dbwatcher/services/diagram_analyzers/foreign_key_analyzer.rb +354 -0
  66. data/lib/dbwatcher/services/diagram_analyzers/inferred_relationship_analyzer.rb +502 -0
  67. data/lib/dbwatcher/services/diagram_analyzers/model_association_analyzer.rb +603 -0
  68. data/lib/dbwatcher/services/diagram_data/attribute.rb +154 -0
  69. data/lib/dbwatcher/services/diagram_data/dataset.rb +280 -0
  70. data/lib/dbwatcher/services/diagram_data/entity.rb +180 -0
  71. data/lib/dbwatcher/services/diagram_data/relationship.rb +188 -0
  72. data/lib/dbwatcher/services/diagram_data/relationship_params.rb +55 -0
  73. data/lib/dbwatcher/services/diagram_data.rb +65 -0
  74. data/lib/dbwatcher/services/diagram_error_handler.rb +239 -0
  75. data/lib/dbwatcher/services/diagram_generator.rb +154 -0
  76. data/lib/dbwatcher/services/diagram_strategies/base_diagram_strategy.rb +149 -0
  77. data/lib/dbwatcher/services/diagram_strategies/class_diagram_strategy.rb +49 -0
  78. data/lib/dbwatcher/services/diagram_strategies/erd_diagram_strategy.rb +52 -0
  79. data/lib/dbwatcher/services/diagram_strategies/flowchart_diagram_strategy.rb +52 -0
  80. data/lib/dbwatcher/services/diagram_system.rb +69 -0
  81. data/lib/dbwatcher/services/diagram_type_registry.rb +164 -0
  82. data/lib/dbwatcher/services/mermaid_syntax/base_builder.rb +127 -0
  83. data/lib/dbwatcher/services/mermaid_syntax/cardinality_mapper.rb +90 -0
  84. data/lib/dbwatcher/services/mermaid_syntax/class_diagram_builder.rb +140 -0
  85. data/lib/dbwatcher/services/mermaid_syntax/class_diagram_helper.rb +48 -0
  86. data/lib/dbwatcher/services/mermaid_syntax/erd_builder.rb +116 -0
  87. data/lib/dbwatcher/services/mermaid_syntax/flowchart_builder.rb +109 -0
  88. data/lib/dbwatcher/services/mermaid_syntax/sanitizer.rb +118 -0
  89. data/lib/dbwatcher/services/mermaid_syntax_builder.rb +155 -0
  90. data/lib/dbwatcher/storage/api/concerns/table_analyzer.rb +15 -128
  91. data/lib/dbwatcher/storage/api/session_api.rb +47 -0
  92. data/lib/dbwatcher/storage/base_storage.rb +7 -0
  93. data/lib/dbwatcher/version.rb +1 -1
  94. data/lib/dbwatcher.rb +58 -1
  95. metadata +94 -2
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Navigation Styles
3
+ * Sidebar, tabs, and navigation controls
4
+ */
5
+
6
+ /* Sidebar styles */
7
+ .sidebar-item {
8
+ font-size: 13px;
9
+ padding: 6px 12px;
10
+ display: flex;
11
+ align-items: center;
12
+ gap: 8px;
13
+ border-radius: 3px;
14
+ transition: all 0.15s;
15
+ }
16
+
17
+ .sidebar-item:hover {
18
+ background: rgba(108, 173, 223, 0.1);
19
+ color: var(--blue-medium);
20
+ }
21
+
22
+ .sidebar-item.active {
23
+ background: var(--navy-dark);
24
+ color: white;
25
+ }
26
+
27
+ /* Tab styles */
28
+ .tab-bar {
29
+ background: var(--gray-100);
30
+ border-bottom: 1px solid var(--border-medium);
31
+ display: flex;
32
+ align-items: center;
33
+ height: 32px;
34
+ font-size: 12px;
35
+ }
36
+
37
+ .tab-item {
38
+ padding: 0 16px;
39
+ height: 100%;
40
+ display: flex;
41
+ align-items: center;
42
+ border-right: 1px solid var(--border-medium);
43
+ cursor: pointer;
44
+ transition: all 0.15s;
45
+ }
46
+
47
+ .tab-item:hover {
48
+ background: var(--border-medium);
49
+ }
50
+
51
+ .tab-item.active {
52
+ background: white;
53
+ color: var(--navy-dark);
54
+ font-weight: 500;
55
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Base Styles
3
+ * Scrollbars, splitters, and utility styles
4
+ */
5
+
6
+ /* Splitter */
7
+ .splitter {
8
+ width: 4px;
9
+ background: var(--border-medium);
10
+ cursor: col-resize;
11
+ }
12
+
13
+ .splitter:hover {
14
+ background: var(--blue-medium);
15
+ }
16
+
17
+ /* Scrollbar styling */
18
+ ::-webkit-scrollbar {
19
+ width: 8px;
20
+ height: 8px;
21
+ }
22
+
23
+ ::-webkit-scrollbar-track {
24
+ background: var(--gray-100);
25
+ }
26
+
27
+ ::-webkit-scrollbar-thumb {
28
+ background: var(--gray-400);
29
+ border-radius: 4px;
30
+ }
31
+
32
+ ::-webkit-scrollbar-thumb:hover {
33
+ background: var(--blue-medium);
34
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * DBWatcher CSS Variables
3
+ * Core color scheme and layout dimensions
4
+ */
5
+
6
+ :root {
7
+ /* Brand colors */
8
+ --navy-dark: #00285D;
9
+ --blue-light: #96C1E7;
10
+ --blue-medium: #6CADDF;
11
+ --gold-dark: #D4A11E;
12
+ --gold-light: #FFC758;
13
+
14
+ /* Layout dimensions for diagram container */
15
+ --header-height: 64px;
16
+ --tab-bar-height: 32px;
17
+ --toolbar-height: 56px;
18
+ --footer-height: 0px;
19
+
20
+ /* Gray scale */
21
+ --gray-50: #f9fafb;
22
+ --gray-100: #f3f3f3;
23
+ --gray-200: #e8e8e8;
24
+ --gray-300: #d1d5db;
25
+ --gray-400: #c8c8c8;
26
+ --gray-500: #6b7280;
27
+ --gray-600: #4b5563;
28
+ --gray-700: #374151;
29
+ --gray-800: #1f2937;
30
+ --gray-900: #111827;
31
+
32
+ /* Border colors */
33
+ --border-light: #f0f0f0;
34
+ --border-medium: #e8e8e8;
35
+ --border-dark: #d1d5db;
36
+
37
+ /* Status colors */
38
+ --status-insert: #10b981;
39
+ --status-update: #6CADDF;
40
+ --status-delete: #ef4444;
41
+ --status-select: #6b7280;
42
+
43
+ /* Highlight colors */
44
+ --highlight-change: rgba(255, 199, 88, 0.3);
45
+ --highlight-new: rgba(16, 185, 129, 0.2);
46
+ --highlight-deleted: rgba(239, 68, 68, 0.2);
47
+ }
@@ -0,0 +1,2 @@
1
+ .tabulator{background-color:#888;border:1px solid #999;font-size:14px;overflow:hidden;position:relative;text-align:left;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.tabulator[tabulator-layout=fitDataFill] .tabulator-tableholder .tabulator-table{min-width:100%}.tabulator[tabulator-layout=fitDataTable]{display:inline-block}.tabulator.tabulator-block-select,.tabulator.tabulator-ranges .tabulator-cell:not(.tabulator-editing){user-select:none}.tabulator .tabulator-header{background-color:#e6e6e6;border-bottom:1px solid #999;box-sizing:border-box;color:#555;font-weight:700;outline:none;overflow:hidden;position:relative;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;white-space:nowrap;width:100%}.tabulator .tabulator-header.tabulator-header-hidden{display:none}.tabulator .tabulator-header .tabulator-header-contents{overflow:hidden;position:relative}.tabulator .tabulator-header .tabulator-header-contents .tabulator-headers{display:inline-block}.tabulator .tabulator-header .tabulator-col{background:#e6e6e6;border-right:1px solid #aaa;box-sizing:border-box;display:inline-flex;flex-direction:column;justify-content:flex-start;overflow:hidden;position:relative;text-align:left;vertical-align:bottom}.tabulator .tabulator-header .tabulator-col.tabulator-moving{background:#cdcdcd;border:1px solid #999;pointer-events:none;position:absolute}.tabulator .tabulator-header .tabulator-col.tabulator-range-highlight{background-color:#d6d6d6;color:#000}.tabulator .tabulator-header .tabulator-col.tabulator-range-selected{background-color:#3876ca;color:#fff}.tabulator .tabulator-header .tabulator-col .tabulator-col-content{box-sizing:border-box;padding:4px;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button{padding:0 8px}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button:hover{cursor:pointer;opacity:.6}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title-holder{position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title{box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom;white-space:nowrap;width:100%}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title.tabulator-col-title-wrap{text-overflow:clip;white-space:normal}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-title-editor{background:#fff;border:1px solid #999;box-sizing:border-box;padding:1px;width:100%}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-header-popup-button+.tabulator-title-editor{width:calc(100% - 22px)}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{align-items:center;bottom:0;display:flex;position:absolute;right:4px;top:0}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #bbb;border-left:6px solid transparent;border-right:6px solid transparent;height:0;width:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{border-top:1px solid #aaa;display:flex;margin-right:-1px;overflow:hidden;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter{box-sizing:border-box;margin-top:2px;position:relative;text-align:center;width:100%}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter textarea{height:auto!important}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter svg{margin-top:3px}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input::-ms-clear{height:0;width:0}.tabulator .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-right:25px}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable.tabulator-col-sorter-element:hover{background-color:#cdcdcd;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter{color:#bbb}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{border-bottom:6px solid #555;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #bbb;border-top:none}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{border-bottom:6px solid #555;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #666;border-top:none}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{border-top:6px solid #555;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:none;border-top:6px solid #666;color:#666}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical .tabulator-col-content .tabulator-col-title{align-items:center;display:flex;justify-content:center;text-orientation:mixed;writing-mode:vertical-rl}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-col-vertical-flip .tabulator-col-title{transform:rotate(180deg)}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-title{padding-right:0;padding-top:20px}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-vertical-flip .tabulator-col-title{padding-bottom:20px;padding-right:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-sorter{bottom:auto;justify-content:center;left:0;right:0;top:4px}.tabulator .tabulator-header .tabulator-frozen{left:0;position:sticky;z-index:11}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left{border-right:2px solid #aaa}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-right{border-left:2px solid #aaa}.tabulator .tabulator-header .tabulator-calcs-holder{background:#f3f3f3!important;border-bottom:1px solid #aaa;border-top:1px solid #aaa;box-sizing:border-box;display:inline-block}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row{background:#f3f3f3!important}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-header .tabulator-frozen-rows-holder{display:inline-block}.tabulator .tabulator-header .tabulator-frozen-rows-holder:empty{display:none}.tabulator .tabulator-tableholder{-webkit-overflow-scrolling:touch;overflow:auto;position:relative;white-space:nowrap;width:100%}.tabulator .tabulator-tableholder:focus{outline:none}.tabulator .tabulator-tableholder .tabulator-placeholder{align-items:center;box-sizing:border-box;display:flex;justify-content:center;min-width:100%;width:100%}.tabulator .tabulator-tableholder .tabulator-placeholder[tabulator-render-mode=virtual]{min-height:100%}.tabulator .tabulator-tableholder .tabulator-placeholder .tabulator-placeholder-contents{color:#ccc;display:inline-block;font-size:20px;font-weight:700;padding:10px;text-align:center;white-space:normal}.tabulator .tabulator-tableholder .tabulator-table{background-color:#fff;color:#333;display:inline-block;overflow:visible;position:relative;white-space:nowrap}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs{background:#e2e2e2!important;font-weight:700}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-top{border-bottom:2px solid #aaa}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-bottom{border-top:2px solid #aaa}.tabulator .tabulator-tableholder .tabulator-range-overlay{inset:0;pointer-events:none;position:absolute;z-index:10}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range{border:1px solid #2975dd;box-sizing:border-box;position:absolute}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range.tabulator-range-active:after{background-color:#2975dd;border-radius:999px;bottom:-3px;content:"";height:6px;position:absolute;right:-3px;width:6px}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range-cell-active{border:2px solid #2975dd;box-sizing:border-box;position:absolute}.tabulator .tabulator-footer{background-color:#e6e6e6;border-top:1px solid #999;color:#555;font-weight:700;user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;white-space:nowrap}.tabulator .tabulator-footer .tabulator-footer-contents{align-items:center;display:flex;flex-direction:row;justify-content:space-between;padding:5px 10px}.tabulator .tabulator-footer .tabulator-footer-contents:empty{display:none}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs{margin-top:-5px;overflow-x:auto}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab{border:1px solid #999;border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-top:none;display:inline-block;font-size:.9em;padding:5px}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab:hover{cursor:pointer;opacity:.7}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab.tabulator-spreadsheet-tab-active{background:#fff}.tabulator .tabulator-footer .tabulator-calcs-holder{background:#f3f3f3!important;border-bottom:1px solid #aaa;border-top:1px solid #aaa;box-sizing:border-box;overflow:hidden;text-align:left;width:100%}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row{background:#f3f3f3!important;display:inline-block}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-footer .tabulator-calcs-holder:only-child{border-bottom:none;margin-bottom:-5px}.tabulator .tabulator-footer>*+.tabulator-page-counter{margin-left:10px}.tabulator .tabulator-footer .tabulator-page-counter{font-weight:400}.tabulator .tabulator-footer .tabulator-paginator{color:#555;flex:1;font-family:inherit;font-size:inherit;font-weight:inherit;text-align:right}.tabulator .tabulator-footer .tabulator-page-size{border:1px solid #aaa;border-radius:3px;display:inline-block;margin:0 5px;padding:2px 5px}.tabulator .tabulator-footer .tabulator-pages{margin:0 7px}.tabulator .tabulator-footer .tabulator-page{background:hsla(0,0%,100%,.2);border:1px solid #aaa;border-radius:3px;display:inline-block;margin:0 2px;padding:2px 5px}.tabulator .tabulator-footer .tabulator-page.active{color:#d00}.tabulator .tabulator-footer .tabulator-page:disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-footer .tabulator-page:not(disabled):hover{background:rgba(0,0,0,.2);color:#fff;cursor:pointer}}.tabulator .tabulator-col-resize-handle{display:inline-block;margin-left:-3px;margin-right:-3px;position:relative;vertical-align:middle;width:6px;z-index:11}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-col-resize-handle:hover{cursor:ew-resize}}.tabulator .tabulator-col-resize-handle:last-of-type{margin-right:0;width:3px}.tabulator .tabulator-col-resize-guide{background-color:#999;height:100%;margin-left:-.5px;opacity:.5;position:absolute;top:0;width:4px}.tabulator .tabulator-row-resize-guide{background-color:#999;height:4px;left:0;margin-top:-.5px;opacity:.5;position:absolute;width:100%}.tabulator .tabulator-alert{align-items:center;background:rgba(0,0,0,.4);display:flex;height:100%;left:0;position:absolute;text-align:center;top:0;width:100%;z-index:100}.tabulator .tabulator-alert .tabulator-alert-msg{background:#fff;border-radius:10px;display:inline-block;font-size:16px;font-weight:700;margin:0 auto;padding:10px 20px}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-msg{border:4px solid #333;color:#000}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-error{border:4px solid #d00;color:#590000}.tabulator-row{background-color:#fff;box-sizing:border-box;min-height:22px;position:relative}.tabulator-row.tabulator-row-even{background-color:#efefef}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selectable:hover{background-color:#bbb;cursor:pointer}}.tabulator-row.tabulator-selected{background-color:#9abcea}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selected:hover{background-color:#769bcc;cursor:pointer}}.tabulator-row.tabulator-row-moving{background:#fff;border:1px solid #000}.tabulator-row.tabulator-moving{border-bottom:1px solid #aaa;border-top:1px solid #aaa;pointer-events:none;position:absolute;z-index:15}.tabulator-row.tabulator-range-highlight .tabulator-cell.tabulator-range-row-header{background-color:#d6d6d6;color:#000}.tabulator-row.tabulator-range-highlight.tabulator-range-selected .tabulator-cell.tabulator-range-row-header,.tabulator-row.tabulator-range-selected .tabulator-cell.tabulator-range-row-header{background-color:#3876ca;color:#fff}.tabulator-row .tabulator-row-resize-handle{bottom:0;height:5px;left:0;position:absolute;right:0}.tabulator-row .tabulator-row-resize-handle.prev{bottom:auto;top:0}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-row-resize-handle:hover{cursor:ns-resize}}.tabulator-row .tabulator-responsive-collapse{border-bottom:1px solid #aaa;border-top:1px solid #aaa;box-sizing:border-box;padding:5px}.tabulator-row .tabulator-responsive-collapse:empty{display:none}.tabulator-row .tabulator-responsive-collapse table{font-size:14px}.tabulator-row .tabulator-responsive-collapse table tr td{position:relative}.tabulator-row .tabulator-responsive-collapse table tr td:first-of-type{padding-right:10px}.tabulator-row .tabulator-cell{border-right:1px solid #aaa;box-sizing:border-box;display:inline-block;outline:none;overflow:hidden;padding:4px;position:relative;text-overflow:ellipsis;vertical-align:middle;white-space:nowrap}.tabulator-row .tabulator-cell.tabulator-row-header{background:#e6e6e6;border-bottom:1px solid #aaa;border-right:1px solid #999}.tabulator-row .tabulator-cell.tabulator-frozen{background-color:inherit;display:inline-block;left:0;position:sticky;z-index:11}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-right:2px solid #aaa}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-left:2px solid #aaa}.tabulator-row .tabulator-cell.tabulator-editing{border:1px solid #1d68cd;outline:none;padding:0}.tabulator-row .tabulator-cell.tabulator-editing input,.tabulator-row .tabulator-cell.tabulator-editing select{background:transparent;border:1px;outline:none}.tabulator-row .tabulator-cell.tabulator-validation-fail{border:1px solid #d00}.tabulator-row .tabulator-cell.tabulator-validation-fail input,.tabulator-row .tabulator-cell.tabulator-validation-fail select{background:transparent;border:1px;color:#d00}.tabulator-row .tabulator-cell.tabulator-row-handle{align-items:center;display:inline-flex;justify-content:center;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box{width:80%}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box .tabulator-row-handle-bar{background:#666;height:3px;margin-top:2px;width:100%}.tabulator-row .tabulator-cell.tabulator-range-selected:not(.tabulator-range-only-cell-selected):not(.tabulator-range-row-header){background-color:#9abcea}.tabulator-row .tabulator-cell .tabulator-data-tree-branch-empty{display:inline-block;width:7px}.tabulator-row .tabulator-cell .tabulator-data-tree-branch{border-bottom:2px solid #aaa;border-bottom-left-radius:1px;border-left:2px solid #aaa;display:inline-block;height:9px;margin-right:5px;margin-top:-9px;vertical-align:middle;width:7px}.tabulator-row .tabulator-cell .tabulator-data-tree-control{align-items:center;background:rgba(0,0,0,.1);border:1px solid #333;border-radius:2px;display:inline-flex;height:11px;justify-content:center;margin-right:5px;overflow:hidden;vertical-align:middle;width:11px}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-data-tree-control:hover{background:rgba(0,0,0,.2);cursor:pointer}}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:transparent;display:inline-block;height:7px;position:relative;width:1px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{background:#333;content:"";height:1px;left:-3px;position:absolute;top:3px;width:7px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;display:inline-block;height:7px;position:relative;width:1px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{background:#333;content:"";height:1px;left:-3px;position:absolute;top:3px;width:7px}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle{align-items:center;background:#666;border-radius:20px;color:#fff;display:inline-flex;font-size:1.1em;font-weight:700;height:15px;justify-content:center;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;width:15px}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle:hover{cursor:pointer;opacity:.7}}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-close{display:initial}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-open{display:none}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle svg{stroke:#fff}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle .tabulator-responsive-collapse-toggle-close{display:none}.tabulator-row .tabulator-cell .tabulator-traffic-light{border-radius:14px;display:inline-block;height:14px;width:14px}.tabulator-row.tabulator-group{background:#ccc;border-bottom:1px solid #999;border-right:1px solid #aaa;border-top:1px solid #999;box-sizing:border-box;font-weight:700;min-width:100%;padding:5px 5px 5px 10px}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-group:hover{background-color:rgba(0,0,0,.1);cursor:pointer}}.tabulator-row.tabulator-group.tabulator-group-visible .tabulator-arrow{border-bottom:0;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #666;margin-right:10px}.tabulator-row.tabulator-group.tabulator-group-level-1{padding-left:30px}.tabulator-row.tabulator-group.tabulator-group-level-2{padding-left:50px}.tabulator-row.tabulator-group.tabulator-group-level-3{padding-left:70px}.tabulator-row.tabulator-group.tabulator-group-level-4{padding-left:90px}.tabulator-row.tabulator-group.tabulator-group-level-5{padding-left:110px}.tabulator-row.tabulator-group .tabulator-group-toggle{display:inline-block}.tabulator-row.tabulator-group .tabulator-arrow{border-bottom:6px solid transparent;border-left:6px solid #666;border-right:0;border-top:6px solid transparent;display:inline-block;height:0;margin-right:16px;vertical-align:middle;width:0}.tabulator-row.tabulator-group span{color:#d00;margin-left:10px}.tabulator-toggle{background:#dcdcdc;border:1px solid #ccc;box-sizing:border-box;display:flex;flex-direction:row}.tabulator-toggle.tabulator-toggle-on{background:#1c6cc2}.tabulator-toggle .tabulator-toggle-switch{background:#fff;border:1px solid #ccc;box-sizing:border-box}.tabulator-popup-container{-webkit-overflow-scrolling:touch;background:#fff;border:1px solid #aaa;box-shadow:0 0 5px 0 rgba(0,0,0,.2);box-sizing:border-box;display:inline-block;font-size:14px;overflow-y:auto;position:absolute;z-index:10000}.tabulator-popup{border-radius:3px;padding:5px}.tabulator-tooltip{border-radius:2px;box-shadow:none;font-size:12px;max-width:Min(500px,100%);padding:3px 5px;pointer-events:none}.tabulator-menu .tabulator-menu-item{box-sizing:border-box;padding:5px 10px;position:relative;user-select:none}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover{background:#efefef;cursor:pointer}}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu{padding-right:25px}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu:after{border-color:#aaa;border-style:solid;border-width:1px 1px 0 0;content:"";display:inline-block;height:7px;position:absolute;right:10px;top:calc(5px + .4em);transform:rotate(45deg);vertical-align:top;width:7px}.tabulator-menu .tabulator-menu-separator{border-top:1px solid #aaa}.tabulator-edit-list{-webkit-overflow-scrolling:touch;font-size:14px;max-height:200px;overflow-y:auto}.tabulator-edit-list .tabulator-edit-list-item{color:#333;outline:none;padding:4px}.tabulator-edit-list .tabulator-edit-list-item.active{background:#1d68cd;color:#fff}.tabulator-edit-list .tabulator-edit-list-item.active.focused{outline:1px solid hsla(0,0%,100%,.5)}.tabulator-edit-list .tabulator-edit-list-item.focused{outline:1px solid #1d68cd}@media (hover:hover) and (pointer:fine){.tabulator-edit-list .tabulator-edit-list-item:hover{background:#1d68cd;color:#fff;cursor:pointer}}.tabulator-edit-list .tabulator-edit-list-placeholder{color:#333;padding:4px;text-align:center}.tabulator-edit-list .tabulator-edit-list-group{border-bottom:1px solid #aaa;color:#333;font-weight:700;padding:6px 4px 4px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-2,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-2{padding-left:12px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-3,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-3{padding-left:20px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-4,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-4{padding-left:28px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-5,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-5{padding-left:36px}.tabulator.tabulator-ltr{direction:ltr}.tabulator.tabulator-rtl{direction:rtl;text-align:initial}.tabulator.tabulator-rtl .tabulator-header .tabulator-col{border-left:1px solid #aaa;border-right:initial;text-align:initial}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{margin-left:-1px;margin-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-left:25px;padding-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{left:8px;right:auto}.tabulator.tabulator-rtl .tabulator-tableholder .tabulator-range-overlay .tabulator-range.tabulator-range-active:after{background-color:#2975dd;border-radius:999px;bottom:-3px;content:"";height:6px;left:-3px;position:absolute;right:auto;width:6px}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell{border-left:1px solid #aaa;border-right:initial}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-branch{border-bottom-left-radius:0;border-bottom-right-radius:1px;border-left:initial;border-right:2px solid #aaa;margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-control{margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-left:2px solid #aaa}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-right:2px solid #aaa}.tabulator.tabulator-rtl .tabulator-row .tabulator-col-resize-handle:last-of-type{margin-left:0;margin-right:-3px;width:3px}.tabulator.tabulator-rtl .tabulator-footer .tabulator-calcs-holder{text-align:initial}.tabulator-print-fullscreen{bottom:0;left:0;position:absolute;right:0;top:0;z-index:10000}body.tabulator-print-fullscreen-hide>:not(.tabulator-print-fullscreen){display:none!important}.tabulator-print-table{border-collapse:collapse}.tabulator-print-table .tabulator-data-tree-branch{border-bottom:2px solid #aaa;border-bottom-left-radius:1px;border-left:2px solid #aaa;display:inline-block;height:9px;margin-right:5px;margin-top:-9px;vertical-align:middle;width:7px}.tabulator-print-table .tabulator-print-table-group{background:#ccc;border-bottom:1px solid #999;border-right:1px solid #aaa;border-top:1px solid #999;box-sizing:border-box;font-weight:700;min-width:100%;padding:5px 5px 5px 10px}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-print-table-group:hover{background-color:rgba(0,0,0,.1);cursor:pointer}}.tabulator-print-table .tabulator-print-table-group.tabulator-group-visible .tabulator-arrow{border-bottom:0;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #666;margin-right:10px}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-1 td{padding-left:30px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-2 td{padding-left:50px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-3 td{padding-left:70px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-4 td{padding-left:90px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-5 td{padding-left:110px!important}.tabulator-print-table .tabulator-print-table-group .tabulator-group-toggle{display:inline-block}.tabulator-print-table .tabulator-print-table-group .tabulator-arrow{border-bottom:6px solid transparent;border-left:6px solid #666;border-right:0;border-top:6px solid transparent;display:inline-block;height:0;margin-right:16px;vertical-align:middle;width:0}.tabulator-print-table .tabulator-print-table-group span{color:#d00;margin-left:10px}.tabulator-print-table .tabulator-data-tree-control{align-items:center;background:rgba(0,0,0,.1);border:1px solid #333;border-radius:2px;display:inline-flex;height:11px;justify-content:center;margin-right:5px;overflow:hidden;vertical-align:middle;width:11px}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-data-tree-control:hover{background:rgba(0,0,0,.2);cursor:pointer}}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:transparent;display:inline-block;height:7px;position:relative;width:1px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{background:#333;content:"";height:1px;left:-3px;position:absolute;top:3px;width:7px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;display:inline-block;height:7px;position:relative;width:1px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{background:#333;content:"";height:1px;left:-3px;position:absolute;top:3px;width:7px}
2
+ /*# sourceMappingURL=tabulator.min.css.map */
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dbwatcher
4
+ module Api
5
+ module V1
6
+ class SessionsController < BaseController
7
+ before_action :find_session, except: [:diagram_types]
8
+
9
+ def changes_data
10
+ Rails.logger.info "API::V1::SessionsController#changes_data: Getting changes for session #{@session.id}"
11
+
12
+ # Paginated, filtered changes data
13
+ # Convert ActionController::Parameters to a hash before passing to service
14
+ service = Dbwatcher::Services::Api::ChangesDataService.new(@session, filter_params.to_h)
15
+ render json: service.call
16
+ end
17
+
18
+ def summary_data
19
+ Rails.logger.info "API::V1::SessionsController#summary_data: Getting summary for session #{@session.id}"
20
+
21
+ # Aggregated summary statistics
22
+ service = Dbwatcher::Services::Api::SummaryDataService.new(@session)
23
+ render json: service.call
24
+ end
25
+
26
+ def diagram_data
27
+ Rails.logger.info "API::V1::SessionsController#diagram_data: Getting diagram for session #{@session.id}"
28
+
29
+ # Generated diagram content with caching
30
+ # Convert ActionController::Parameters to a hash before passing to service
31
+ diagram_params = params.to_unsafe_h
32
+ service = Dbwatcher::Services::Api::DiagramDataService.new(@session, params[:type], diagram_params)
33
+ result = service.call
34
+
35
+ if result[:error]
36
+ render json: { error: result[:error] }, status: :unprocessable_entity
37
+ else
38
+ render json: result
39
+ end
40
+ end
41
+
42
+ def diagram_types
43
+ Rails.logger.info "API::V1::SessionsController#diagram_types: Getting available diagram types"
44
+
45
+ render json: {
46
+ types: Dbwatcher::Services::Api::DiagramDataService.available_types_with_metadata,
47
+ default_type: "database_tables"
48
+ }
49
+ end
50
+
51
+ private
52
+
53
+ def find_session
54
+ @session = Storage.sessions.find(params[:id])
55
+ render json: { error: "Session not found" }, status: :not_found unless @session
56
+ end
57
+
58
+ def filter_params
59
+ params.permit(:table, :operation, :page, :per_page)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -13,6 +13,11 @@ module Dbwatcher
13
13
  # Common error handling
14
14
  rescue_from StandardError, with: :handle_error
15
15
 
16
+ # Include helpers
17
+ helper Dbwatcher::ApplicationHelper
18
+ helper Dbwatcher::FormattingHelper
19
+ helper Dbwatcher::DiagramHelper if defined?(Dbwatcher::DiagramHelper)
20
+
16
21
  protected
17
22
 
18
23
  # Set current time for consistent timestamp usage across views
@@ -35,7 +40,8 @@ module Dbwatcher
35
40
  respond_to do |format|
36
41
  format.html do
37
42
  flash[:error] = "An error occurred while processing your request."
38
- redirect_to root_path
43
+ # Avoid infinite redirect by using main app root or request referer
44
+ redirect_to(request.referer || main_app.root_path)
39
45
  end
40
46
  format.json do
41
47
  render json: { error: "Internal server error" }, status: :internal_server_error
@@ -54,7 +60,7 @@ module Dbwatcher
54
60
  def format_timestamp(timestamp_str)
55
61
  return "N/A" unless timestamp_str
56
62
 
57
- Time.parse(timestamp_str).strftime("%H:%M:%S")
63
+ Time.parse(timestamp_str).strftime("%Y-%m-%d %H:%M:%S")
58
64
  rescue ArgumentError
59
65
  "N/A"
60
66
  end
@@ -8,5 +8,13 @@ module Dbwatcher
8
8
  @active_tables = dashboard_data[:active_tables]
9
9
  @query_stats = dashboard_data[:query_stats]
10
10
  end
11
+
12
+ def clear_all
13
+ clear_storage_with_message(
14
+ -> { Storage.clear_all },
15
+ "All sessions and SQL logs",
16
+ root_path
17
+ )
18
+ end
11
19
  end
12
20
  end
@@ -2,24 +2,29 @@
2
2
 
3
3
  module Dbwatcher
4
4
  class SessionsController < BaseController
5
+ before_action :find_session, except: [:index]
6
+
5
7
  def index
6
8
  @sessions = Storage.sessions.all
7
9
  end
8
10
 
9
11
  def show
10
- Rails.logger.info "SessionsController#show: Loading session with ID: #{params[:id]}"
11
- @session = Storage.sessions.find(params[:id])
12
- Rails.logger.info "SessionsController#show: Loaded session: #{@session.inspect}"
12
+ redirect_to changes_session_path(@session.id)
13
+ end
13
14
 
14
- return handle_not_found("Session", sessions_path) unless @session
15
+ def changes
16
+ Rails.logger.info "SessionsController#changes: Loading changes for session #{@session.id}"
17
+ # No server-side data processing - API-first architecture
18
+ end
15
19
 
16
- @tables_summary = Storage.sessions.build_tables_summary(@session)
17
- Rails.logger.info "SessionsController#show: Tables summary: #{@tables_summary.inspect}"
20
+ def summary
21
+ Rails.logger.info "SessionsController#summary: Loading summary for session #{@session.id}"
22
+ # No server-side data processing - API-first architecture
23
+ end
18
24
 
19
- respond_to do |format|
20
- format.html
21
- format.json { render json: @session.to_h }
22
- end
25
+ def diagrams
26
+ Rails.logger.info "SessionsController#diagrams: Loading diagrams for session #{@session.id}"
27
+ # No server-side data processing - API-first architecture
23
28
  end
24
29
 
25
30
  def clear
@@ -29,5 +34,15 @@ module Dbwatcher
29
34
  sessions_path
30
35
  )
31
36
  end
37
+
38
+ private
39
+
40
+ def find_session
41
+ @session = Storage.sessions.find(params[:id])
42
+ handle_not_found("Session", sessions_path) unless @session
43
+ end
44
+
45
+ # No longer needed with API-first architecture
46
+ # All data processing happens in API services and is loaded via JavaScript
32
47
  end
33
48
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dbwatcher
4
+ module ComponentHelper
5
+ # Removed as part of API-first migration (Story 6.8)
6
+ # All data is now loaded directly from API endpoints
7
+
8
+ # Removed as part of API-first migration (Story 6.8)
9
+ # All data is now loaded directly from API endpoints
10
+
11
+ # Removed as part of API-first migration (Story 6.8)
12
+ # All data is now loaded directly from API endpoints
13
+
14
+ # Generate data attributes for component binding
15
+ def dbwatcher_component(component_name, config)
16
+ {
17
+ "data-dbwatcher" => component_name,
18
+ "data-config" => config.to_json
19
+ }
20
+ end
21
+
22
+ # Helper to render a DBWatcher component
23
+ def render_dbwatcher_component(component_name, config, html_options = {})
24
+ content_tag :div, dbwatcher_component(component_name, config).merge(html_options) do
25
+ yield if block_given?
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dbwatcher
4
+ module DiagramHelper
5
+ # Generate diagram configuration for Alpine.js
6
+ def diagram_config(session, active_tab)
7
+ {
8
+ auto_generate: active_tab == "diagrams",
9
+ default_type: "database_tables",
10
+ endpoint: diagram_data_api_v1_session_path(session),
11
+ container_id: "diagram-container"
12
+ }.to_json
13
+ end
14
+
15
+ # Generate diagram type options dynamically from registry
16
+ def diagram_type_options
17
+ registry = Dbwatcher::Services::DiagramTypeRegistry.new
18
+ options = registry.available_types_with_metadata.map do |type, metadata|
19
+ [metadata[:display_name], type]
20
+ end
21
+
22
+ options_for_select(options)
23
+ end
24
+
25
+ # Generate CSS variables for diagram container height calculation
26
+ def diagram_container_css_variables
27
+ {
28
+ "--header-height": "64px",
29
+ "--tab-bar-height": "40px",
30
+ "--toolbar-height": "72px",
31
+ "--footer-height": "0px",
32
+ "--diagram-height": "calc(100vh - var(--header-height) - var(--tab-bar-height) - " \
33
+ "var(--toolbar-height) - var(--footer-height) - 2rem)",
34
+ "--diagram-min-height": "500px"
35
+ }.map { |key, value| "#{key}: #{value}" }.join("; ")
36
+ end
37
+
38
+ # Generate button classes for diagram controls
39
+ def diagram_button_classes(type = :default)
40
+ base_classes = "compact-button text-xs rounded"
41
+
42
+ button_styles = {
43
+ primary: "bg-blue-medium text-white px-3 py-1 hover:bg-navy-dark",
44
+ secondary: "bg-navy-dark text-white px-2 py-1 hover:bg-blue-medium",
45
+ toggle: "bg-blue-medium text-white px-2 py-1 hover:bg-navy-dark flex items-center gap-1",
46
+ icon: "bg-white border border-gray-300 hover:bg-gray-50 p-1",
47
+ danger: "bg-red-500 text-white px-2 py-1 hover:bg-red-600",
48
+ success: "bg-green-500 text-white px-2 py-1 hover:bg-green-600"
49
+ }
50
+
51
+ style = button_styles[type] || button_styles[:primary]
52
+ "#{base_classes} #{style}"
53
+ end
54
+
55
+ # Generate a code view with syntax highlighting for Mermaid code
56
+ def diagram_code_view(content)
57
+ content_tag(:div, class: "diagram-code-view") do
58
+ content_tag(:pre,
59
+ class: "text-xs font-mono p-4 bg-gray-50 rounded border border-gray-200 " \
60
+ "overflow-x-auto whitespace-pre-wrap",
61
+ style: "max-height: calc(100vh - 220px); overflow-y: auto;") do
62
+ content_tag(:code, content)
63
+ end
64
+ end
65
+ end
66
+
67
+ # Generate copy to clipboard button
68
+ def copy_to_clipboard_button(target_id)
69
+ button_tag(
70
+ type: "button",
71
+ class: diagram_button_classes(:icon),
72
+ "x-on:click": "copyToClipboard('#{target_id}')",
73
+ title: "Copy to clipboard"
74
+ ) do
75
+ copy_icon_svg
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ # Generate copy icon SVG
82
+ def copy_icon_svg
83
+ content_tag(:svg, class: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24") do
84
+ tag.path(
85
+ stroke_linecap: "round",
86
+ stroke_linejoin: "round",
87
+ stroke_width: "2",
88
+ d: "M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2" \
89
+ "M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3"
90
+ )
91
+ end
92
+ end
93
+
94
+ # Generate toggle view button
95
+ def toggle_view_button
96
+ button_tag(
97
+ type: "button",
98
+ class: diagram_button_classes(:toggle),
99
+ "x-on:click": "toggleViewMode()",
100
+ title: "Toggle between code and preview"
101
+ ) do
102
+ concat(content_tag(:svg, class: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24") do
103
+ tag.path(stroke_linecap: "round", stroke_linejoin: "round", stroke_width: "2",
104
+ d: "M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4")
105
+ end)
106
+ concat(content_tag(:span, "x-text": "viewMode === 'preview' ? 'View Code' : 'View Preview'"))
107
+ end
108
+ end
109
+ end
110
+ end
@@ -17,11 +17,12 @@ module Dbwatcher
17
17
  name.to_s.gsub(/^HTTP \w+ /, "")
18
18
  end
19
19
 
20
- # Generate session ID display (truncated)
20
+ # Generate session ID display (wider format)
21
21
  def display_session_id(id)
22
22
  return "N/A" unless id
23
23
 
24
- "#{id[0..7]}..."
24
+ # Show more characters of the session ID for better readability
25
+ "#{id[0..15]}..."
25
26
  end
26
27
  end
27
28
  end