dbviewer 0.6.5 → 0.6.6
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 +4 -4
- data/app/helpers/dbviewer/filter_helper.rb +6 -6
- data/app/helpers/dbviewer/table_rendering_helper.rb +3 -3
- data/app/views/dbviewer/tables/show.html.erb +62 -25
- data/app/views/layouts/dbviewer/application.html.erb +143 -16
- data/lib/dbviewer/engine.rb +0 -5
- data/lib/dbviewer/version.rb +1 -1
- data/lib/dbviewer.rb +0 -2
- metadata +2 -4
- data/lib/dbviewer/query/collection.rb +0 -39
- data/lib/dbviewer/validator.rb +0 -9
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8e2d222c36ae2bf4e6d20f4f915d59c3b5acae64eead145e8a0dccd7acb3ddbc
         | 
| 4 | 
            +
              data.tar.gz: 786de8d3e46e54532234790345b52c17e3bdf887882b14534cd85b6b1d2b4fe0
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e461217c6b720cc04ec36e884aa19c1468d034451afdc3d64709056df2004cd9d6830f8f28ae339f9705b62eb7f60cc3d5587378828c9965d2468d7b23659999
         | 
| 7 | 
            +
              data.tar.gz: ffb54b66a8bf3d4d426cdba9dabdf39bf619af8e4b70caeb83a541576ae3ec4bad53fbe1d9f28dea19930e7caf00cb1c71525389e04977a8a4771cfc52a402f1
         | 
| @@ -78,30 +78,30 @@ module Dbviewer | |
| 78 78 | 
             
                    visible_field = form.text_field("column_filters[#{column_name}_display]",
         | 
| 79 79 | 
             
                      disabled: true,
         | 
| 80 80 | 
             
                      value: "",
         | 
| 81 | 
            -
                      class: "form-control form-control-sm column-filter rounded-0 disabled-filter",
         | 
| 81 | 
            +
                      class: "form-control form-control-sm column-filter border-end-1 border-start-0 rounded-0 disabled-filter",
         | 
| 82 82 | 
             
                      data: { column: "#{column_name}_display" })
         | 
| 83 83 |  | 
| 84 84 | 
             
                    hidden_field + visible_field
         | 
| 85 85 | 
             
                  elsif column_type && column_type =~ /datetime/
         | 
| 86 86 | 
             
                    form.datetime_local_field("column_filters[#{column_name}]",
         | 
| 87 87 | 
             
                      value: value,
         | 
| 88 | 
            -
                      class: "form-control form-control-sm column-filter rounded-0",
         | 
| 88 | 
            +
                      class: "form-control form-control-sm column-filter border-end-1 border-start-0 rounded-0",
         | 
| 89 89 | 
             
                      data: { column: column_name })
         | 
| 90 90 | 
             
                  elsif column_type && column_type =~ /^date$/
         | 
| 91 91 | 
             
                    form.date_field("column_filters[#{column_name}]",
         | 
| 92 92 | 
             
                      value: value,
         | 
| 93 | 
            -
                      class: "form-control form-control-sm column-filter rounded-0",
         | 
| 93 | 
            +
                      class: "form-control form-control-sm column-filter border-end-1 border-start-0 rounded-0",
         | 
| 94 94 | 
             
                      data: { column: column_name })
         | 
| 95 95 | 
             
                  elsif column_type && column_type =~ /^time$/
         | 
| 96 96 | 
             
                    form.time_field("column_filters[#{column_name}]",
         | 
| 97 97 | 
             
                      value: value,
         | 
| 98 | 
            -
                      class: "form-control form-control-sm column-filter rounded-0",
         | 
| 98 | 
            +
                      class: "form-control form-control-sm column-filter border-end-1 border-start-0 rounded-0",
         | 
| 99 99 | 
             
                      data: { column: column_name })
         | 
| 100 100 | 
             
                  else
         | 
| 101 101 | 
             
                    form.text_field("column_filters[#{column_name}]",
         | 
| 102 102 | 
             
                      value: value,
         | 
| 103 103 | 
             
                      placeholder: "",
         | 
| 104 | 
            -
                      class: "form-control form-control-sm column-filter rounded-0",
         | 
| 104 | 
            +
                      class: "form-control form-control-sm column-filter border-end-1 border-start-0 rounded-0",
         | 
| 105 105 | 
             
                      data: { column: column_name })
         | 
| 106 106 | 
             
                  end
         | 
| 107 107 | 
             
                end
         | 
| @@ -119,7 +119,7 @@ module Dbviewer | |
| 119 119 | 
             
                  form.select("column_filters[#{column_name}_operator]",
         | 
| 120 120 | 
             
                    options_for_select(operator_options, selected_operator),
         | 
| 121 121 | 
             
                    { include_blank: false },
         | 
| 122 | 
            -
                    { class: "form-select form-select-sm operator-select" })
         | 
| 122 | 
            +
                    { class: "form-select form-select-sm operator-select border-end-1 border-start-0 border-bottom-0" })
         | 
| 123 123 | 
             
                end
         | 
| 124 124 |  | 
| 125 125 | 
             
                # Render complete filter input group for a column
         | 
| @@ -7,7 +7,7 @@ module Dbviewer | |
| 7 7 | 
             
                  content_tag(:tr) do
         | 
| 8 8 | 
             
                    # Start with action column header (sticky first column)
         | 
| 9 9 | 
             
                    headers = [
         | 
| 10 | 
            -
                      content_tag(:th, class: "px-3 py-2 text-center action-column action-column-header", width: "60px", rowspan: 2) do
         | 
| 10 | 
            +
                      content_tag(:th, class: "px-3 py-2 text-center action-column action-column-header border-bottom-0", width: "60px", rowspan: 2) do
         | 
| 11 11 | 
             
                        content_tag(:span, "Actions")
         | 
| 12 12 | 
             
                      end
         | 
| 13 13 | 
             
                    ]
         | 
| @@ -15,7 +15,7 @@ module Dbviewer | |
| 15 15 | 
             
                    # Add all data columns
         | 
| 16 16 | 
             
                    headers += records.columns.map do |column_name|
         | 
| 17 17 | 
             
                      is_sorted = order_by == column_name
         | 
| 18 | 
            -
                      content_tag(:th, class: "px-3 py-2 sortable-column #{is_sorted ? 'sorted' : ''}") do
         | 
| 18 | 
            +
                      content_tag(:th, class: "px-3 py-2 sortable-column border-bottom-0 #{is_sorted ? 'sorted' : ''}") do
         | 
| 19 19 | 
             
                        sortable_column_header(column_name, order_by, order_direction, table_name, current_page, per_page, column_filters)
         | 
| 20 20 | 
             
                      end
         | 
| 21 21 | 
             
                    end
         | 
| @@ -30,7 +30,7 @@ module Dbviewer | |
| 30 30 |  | 
| 31 31 | 
             
                  content_tag(:tr, class: "column-filters") do
         | 
| 32 32 | 
             
                    filters = records.columns.map do |column_name|
         | 
| 33 | 
            -
                      content_tag(:th, class: "p-0") do
         | 
| 33 | 
            +
                      content_tag(:th, class: "p-0 border-bottom-0") do
         | 
| 34 34 | 
             
                        render_column_filter(form, column_name, columns, column_filters)
         | 
| 35 35 | 
             
                      end
         | 
| 36 36 | 
             
                    end
         | 
| @@ -181,7 +181,7 @@ | |
| 181 181 | 
             
                left: 0;
         | 
| 182 182 | 
             
                z-index: 40 !important; /* Even higher z-index to stay on top of everything */
         | 
| 183 183 | 
             
                background-color: var(--bs-tertiary-bg, #f8f9fa) !important;
         | 
| 184 | 
            -
                 | 
| 184 | 
            +
                box-shadow: 2px 0 6px rgba(0, 0, 0, 0.04) !important;
         | 
| 185 185 | 
             
              }
         | 
| 186 186 |  | 
| 187 187 | 
             
              [data-bs-theme="dark"] .action-column-header {
         | 
| @@ -206,7 +206,7 @@ | |
| 206 206 |  | 
| 207 207 | 
             
                /* Fix action column for entire table */
         | 
| 208 208 | 
             
                .action-column {
         | 
| 209 | 
            -
                   | 
| 209 | 
            +
                  box-shadow: 2px 0 6px rgba(0, 0, 0, 0.04);
         | 
| 210 210 | 
             
                }
         | 
| 211 211 |  | 
| 212 212 | 
             
                /* Ensure equal padding for all cells */
         | 
| @@ -332,25 +332,27 @@ | |
| 332 332 |  | 
| 333 333 | 
             
            <% content_for :sidebar_active do %>active<% end %>
         | 
| 334 334 |  | 
| 335 | 
            -
            <div class=" | 
| 336 | 
            -
              <div class="d-flex justify-content-between align-items-center">
         | 
| 337 | 
            -
                < | 
| 338 | 
            -
             | 
| 339 | 
            -
             | 
| 340 | 
            -
                < | 
| 341 | 
            -
                  < | 
| 342 | 
            -
             | 
| 343 | 
            -
             | 
| 344 | 
            -
                  < | 
| 345 | 
            -
             | 
| 346 | 
            -
             | 
| 347 | 
            -
             | 
| 348 | 
            -
                      < | 
| 349 | 
            -
             | 
| 350 | 
            -
             | 
| 351 | 
            -
             | 
| 352 | 
            -
                   | 
| 353 | 
            -
             | 
| 335 | 
            +
            <div class="mb-4">
         | 
| 336 | 
            +
              <div class="d-block d-md-flex justify-content-between align-items-center">
         | 
| 337 | 
            +
                <div class="mb-3 mb-md-0">
         | 
| 338 | 
            +
                  <h1>Table: <%= @table_name %></h1>
         | 
| 339 | 
            +
                </div>
         | 
| 340 | 
            +
                <div class="d-flex flex-wrap gap-2">
         | 
| 341 | 
            +
                  <button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#tableStructureModal">
         | 
| 342 | 
            +
                    <i class="bi bi-table me-1"></i> Table Structure
         | 
| 343 | 
            +
                  </button>
         | 
| 344 | 
            +
                  <button type="button" class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#miniErdModal">
         | 
| 345 | 
            +
                    <i class="bi bi-diagram-3 me-1"></i> View Relationships
         | 
| 346 | 
            +
                  </button>
         | 
| 347 | 
            +
                  <% if Dbviewer.configuration.enable_data_export %>
         | 
| 348 | 
            +
                      <button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#csvExportModal">
         | 
| 349 | 
            +
                        <i class="bi bi-file-earmark-spreadsheet me-1"></i> Export CSV
         | 
| 350 | 
            +
                      </button>
         | 
| 351 | 
            +
                  <% end %>
         | 
| 352 | 
            +
                  <%= link_to query_table_path(@table_name), class: "btn btn-primary" do %>
         | 
| 353 | 
            +
                    <i class="bi bi-code-square me-1"></i> Run SQL Query
         | 
| 354 | 
            +
                  <% end %>
         | 
| 355 | 
            +
                </div>
         | 
| 354 356 | 
             
              </div>
         | 
| 355 357 | 
             
            </div>
         | 
| 356 358 |  | 
| @@ -450,7 +452,7 @@ | |
| 450 452 | 
             
                      <%= form.hidden_field :order_direction, value: @order_direction %>
         | 
| 451 453 | 
             
                      <%= form.hidden_field :page, value: 1 %> <!-- Reset to first page on filter -->
         | 
| 452 454 |  | 
| 453 | 
            -
                      <table class="table table- | 
| 455 | 
            +
                      <table class="table table-striped rounded-none">
         | 
| 454 456 | 
             
                          <thead class="dbviewer-table-header">
         | 
| 455 457 | 
             
                            <%= render_sortable_header_row(@records, @order_by, @order_direction, @table_name, @current_page, @per_page, @column_filters) %>
         | 
| 456 458 | 
             
                            <%= render_column_filters_row(form, @records, @columns, @column_filters) %>
         | 
| @@ -475,7 +477,7 @@ | |
| 475 477 | 
             
                  <div class="modal-body">
         | 
| 476 478 | 
             
                    <!-- Record Data Section -->
         | 
| 477 479 | 
             
                    <div class="table-responsive">
         | 
| 478 | 
            -
                      <table class="table  | 
| 480 | 
            +
                      <table class="table record-detail-table">
         | 
| 479 481 | 
             
                        <thead>
         | 
| 480 482 | 
             
                          <tr>
         | 
| 481 483 | 
             
                            <th width="30%">Column</th>
         | 
| @@ -524,6 +526,42 @@ | |
| 524 526 | 
             
            </div>
         | 
| 525 527 |  | 
| 526 528 | 
             
            <style>
         | 
| 529 | 
            +
              /* Borderless table styling */
         | 
| 530 | 
            +
              .table {
         | 
| 531 | 
            +
                border-collapse: separate;
         | 
| 532 | 
            +
                border-spacing: 0;
         | 
| 533 | 
            +
              }
         | 
| 534 | 
            +
              
         | 
| 535 | 
            +
              .table th,
         | 
| 536 | 
            +
              .table td {
         | 
| 537 | 
            +
                border: none;
         | 
| 538 | 
            +
                border-bottom: 1px solid var(--bs-border-subtle, rgba(0, 0, 0, 0.05));
         | 
| 539 | 
            +
              }
         | 
| 540 | 
            +
              
         | 
| 541 | 
            +
              .table thead th {
         | 
| 542 | 
            +
                border-bottom: 2px solid var(--bs-border-subtle, rgba(0, 0, 0, 0.08));
         | 
| 543 | 
            +
                font-weight: 500;
         | 
| 544 | 
            +
              }
         | 
| 545 | 
            +
              
         | 
| 546 | 
            +
              /* Add a subtle hover effect on table rows */
         | 
| 547 | 
            +
              .table tbody tr:hover {
         | 
| 548 | 
            +
                background-color: var(--bs-tertiary-bg, rgba(0, 0, 0, 0.02));
         | 
| 549 | 
            +
              }
         | 
| 550 | 
            +
              
         | 
| 551 | 
            +
              /* Dark mode compatibility */
         | 
| 552 | 
            +
              [data-bs-theme="dark"] .table th,
         | 
| 553 | 
            +
              [data-bs-theme="dark"] .table td {
         | 
| 554 | 
            +
                border-bottom: 1px solid var(--bs-border-subtle, rgba(255, 255, 255, 0.05));
         | 
| 555 | 
            +
              }
         | 
| 556 | 
            +
              
         | 
| 557 | 
            +
              [data-bs-theme="dark"] .table thead th {
         | 
| 558 | 
            +
                border-bottom: 2px solid var(--bs-border-subtle, rgba(255, 255, 255, 0.08));
         | 
| 559 | 
            +
              }
         | 
| 560 | 
            +
              
         | 
| 561 | 
            +
              [data-bs-theme="dark"] .table tbody tr:hover {
         | 
| 562 | 
            +
                background-color: var(--bs-tertiary-bg, rgba(255, 255, 255, 0.03));
         | 
| 563 | 
            +
              }
         | 
| 564 | 
            +
              
         | 
| 527 565 | 
             
              /* Column filter styling */
         | 
| 528 566 | 
             
              .column-filters td {
         | 
| 529 567 | 
             
                padding: 0.5rem;
         | 
| @@ -539,8 +577,7 @@ | |
| 539 577 | 
             
                left: 0;
         | 
| 540 578 | 
             
                z-index: 30; /* Increased z-index to ensure it stays on top */
         | 
| 541 579 | 
             
                background-color: var(--bs-body-bg, #fff); /* Use body background color */
         | 
| 542 | 
            -
                box-shadow: 2px 0  | 
| 543 | 
            -
                border-right: 1px solid var(--bs-border-color);
         | 
| 580 | 
            +
                box-shadow: 2px 0 6px rgba(0, 0, 0, 0.04);
         | 
| 544 581 | 
             
              }
         | 
| 545 582 |  | 
| 546 583 | 
             
              .copy-factory-btn {
         | 
| @@ -2,6 +2,7 @@ | |
| 2 2 | 
             
            <html data-bs-theme="light">
         | 
| 3 3 | 
             
            <head>
         | 
| 4 4 | 
             
              <title><%= content_for?(:title) ? yield(:title) + " - DB Viewer" : "DB Viewer" %></title>
         | 
| 5 | 
            +
              <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
         | 
| 5 6 | 
             
              <%= csrf_meta_tags %>
         | 
| 6 7 | 
             
              <%= csp_meta_tag %>
         | 
| 7 8 |  | 
| @@ -607,7 +608,7 @@ | |
| 607 608 | 
             
                }
         | 
| 608 609 |  | 
| 609 610 | 
             
                [data-bs-theme="dark"] .navbar.bg-primary {
         | 
| 610 | 
            -
                  background: linear-gradient(to right, # | 
| 611 | 
            +
                  background: linear-gradient(to right, #161819, #1a1d20) !important;
         | 
| 611 612 | 
             
                  border-bottom: 1px solid rgba(73, 80, 87, 0.5);
         | 
| 612 613 | 
             
                }
         | 
| 613 614 |  | 
| @@ -657,12 +658,13 @@ | |
| 657 658 | 
             
                }
         | 
| 658 659 |  | 
| 659 660 | 
             
                [data-bs-theme="dark"] .list-group-item {
         | 
| 660 | 
            -
                  background-color:  | 
| 661 | 
            +
                  background-color: #1a1d20;
         | 
| 661 662 | 
             
                  border-color: rgba(73, 80, 87, 0.3);
         | 
| 662 663 | 
             
                  color: #e9ecef;
         | 
| 663 664 | 
             
                }
         | 
| 664 665 |  | 
| 665 | 
            -
                [data-bs-theme="dark"] .list-group-item.active  | 
| 666 | 
            +
                [data-bs-theme="dark"] .list-group-item.active, 
         | 
| 667 | 
            +
                [data-bs-theme="dark"] .list-group-item.active:hover {
         | 
| 666 668 | 
             
                  background-color: rgba(13, 110, 253, 0.15);
         | 
| 667 669 | 
             
                  border-color: rgba(73, 80, 87, 0.3);
         | 
| 668 670 | 
             
                  color: #6ea8fe;
         | 
| @@ -670,7 +672,7 @@ | |
| 670 672 |  | 
| 671 673 | 
             
                [data-bs-theme="dark"] .list-group-item-action:hover,
         | 
| 672 674 | 
             
                [data-bs-theme="dark"] .list-group-item-action:focus {
         | 
| 673 | 
            -
                  background-color: rgba(255, 255, 255, 0. | 
| 675 | 
            +
                  background-color: rgba(255, 255, 255, 0.05);
         | 
| 674 676 | 
             
                  color: #f8f9fa;
         | 
| 675 677 | 
             
                }
         | 
| 676 678 |  | 
| @@ -881,6 +883,14 @@ | |
| 881 883 | 
             
                  background: linear-gradient(135deg, #0d6efd, #0a58ca);
         | 
| 882 884 | 
             
                  color: #ffffff;
         | 
| 883 885 | 
             
                }
         | 
| 886 | 
            +
             | 
| 887 | 
            +
                [data-bs-theme="light"] .offcanvas .nav-link {
         | 
| 888 | 
            +
                  color:rgb(78, 86, 95);
         | 
| 889 | 
            +
                }
         | 
| 890 | 
            +
             | 
| 891 | 
            +
                [data-bs-theme="dark"] .offcanvas .nav-link {
         | 
| 892 | 
            +
                  color:rgb(191, 191, 191);
         | 
| 893 | 
            +
                }
         | 
| 884 894 |  | 
| 885 895 | 
             
                /* Enhanced table styling - Grafana-like compact design */
         | 
| 886 896 | 
             
                .table {
         | 
| @@ -1057,12 +1067,6 @@ | |
| 1057 1067 | 
             
                  color: #ff6b6b !important;
         | 
| 1058 1068 | 
             
                }
         | 
| 1059 1069 |  | 
| 1060 | 
            -
                /* Styling for form controls in SQL logs page */
         | 
| 1061 | 
            -
                [data-bs-theme="dark"] .list-group-item {
         | 
| 1062 | 
            -
                  background-color: #212529;
         | 
| 1063 | 
            -
                  border-color: #495057;
         | 
| 1064 | 
            -
                }
         | 
| 1065 | 
            -
                
         | 
| 1066 1070 | 
             
                /* Code block styling for SQL logs */
         | 
| 1067 1071 | 
             
                [data-bs-theme="light"] .code-block {
         | 
| 1068 1072 | 
             
                  background-color: #f8f9fa;
         | 
| @@ -1393,11 +1397,15 @@ | |
| 1393 1397 | 
             
                <nav class="navbar navbar-expand-lg navbar-dark bg-primary dbviewer-navbar fixed-top">
         | 
| 1394 1398 | 
             
                  <div class="container-fluid">
         | 
| 1395 1399 | 
             
                    <a class="navbar-brand" href="<%= dbviewer.root_path %>"><i class="bi bi-database-fill me-1"></i>DB Viewer</a>
         | 
| 1396 | 
            -
                    < | 
| 1397 | 
            -
             | 
| 1398 | 
            -
             | 
| 1399 | 
            -
             | 
| 1400 | 
            -
             | 
| 1400 | 
            +
                    <div class="d-flex align-items-center">
         | 
| 1401 | 
            +
                      <button class="navbar-toggler border-0 px-2 d-lg-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#navbarOffcanvas"
         | 
| 1402 | 
            +
                              aria-controls="navbarOffcanvas" aria-expanded="false" aria-label="Toggle navigation">
         | 
| 1403 | 
            +
                        <span class="navbar-toggler-icon"></span>
         | 
| 1404 | 
            +
                      </button>
         | 
| 1405 | 
            +
                    </div>
         | 
| 1406 | 
            +
                    
         | 
| 1407 | 
            +
                    <!-- Visible navigation items on larger screens -->
         | 
| 1408 | 
            +
                    <div class="collapse navbar-collapse d-none d-lg-flex" id="navbarNav">
         | 
| 1401 1409 | 
             
                      <ul class="navbar-nav">
         | 
| 1402 1410 | 
             
                        <li class="nav-item">
         | 
| 1403 1411 | 
             
                          <%= link_to raw('<i class="bi bi-table"></i> Tables'), dbviewer.tables_path, class: "nav-link #{tables_nav_class}" %>
         | 
| @@ -1446,6 +1454,56 @@ | |
| 1446 1454 | 
             
                      </ul>
         | 
| 1447 1455 | 
             
                    </div>
         | 
| 1448 1456 | 
             
                  </div>
         | 
| 1457 | 
            +
                  
         | 
| 1458 | 
            +
                  <!-- Offcanvas sidebar for mobile/tablet view that slides from right -->
         | 
| 1459 | 
            +
                  <div class="offcanvas offcanvas-end d-lg-none" tabindex="-1" id="navbarOffcanvas" aria-labelledby="offcanvasNavbarLabel">
         | 
| 1460 | 
            +
                    <div class="offcanvas-header bg-light-subtle">
         | 
| 1461 | 
            +
                      <h5 class="offcanvas-title" id="offcanvasNavbarLabel"><i class="bi bi-database-fill me-2 text-primary"></i>DB Viewer</h5>
         | 
| 1462 | 
            +
                      <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
         | 
| 1463 | 
            +
                    </div>
         | 
| 1464 | 
            +
                    <div class="offcanvas-body bg-body-tertiary">
         | 
| 1465 | 
            +
                      <ul class="navbar-nav mb-2 mb-lg-0 fw-medium">
         | 
| 1466 | 
            +
                    <li class="nav-item py-1">
         | 
| 1467 | 
            +
                      <%= link_to raw('<i class="bi bi-table me-2 text-primary"></i> Tables'), dbviewer.tables_path, class: "nav-link rounded #{tables_nav_class}" %>
         | 
| 1468 | 
            +
                    </li>
         | 
| 1469 | 
            +
                    <li class="nav-item py-1">
         | 
| 1470 | 
            +
                      <%= link_to raw('<i class="bi bi-diagram-3 me-2 text-primary"></i> ERD'), dbviewer.entity_relationship_diagrams_path, class: "nav-link rounded #{erd_nav_class}" %>
         | 
| 1471 | 
            +
                    </li>
         | 
| 1472 | 
            +
                    <% if Dbviewer.configuration.enable_query_logging %>
         | 
| 1473 | 
            +
                      <li class="nav-item py-1">
         | 
| 1474 | 
            +
                        <%= link_to raw('<i class="bi bi-journal-code me-2 text-primary"></i> SQL Logs'), dbviewer.logs_path, class: "nav-link rounded #{logs_nav_class}" %>
         | 
| 1475 | 
            +
                      </li>
         | 
| 1476 | 
            +
                    <% end %>
         | 
| 1477 | 
            +
                    <li class="nav-item dropdown py-1">
         | 
| 1478 | 
            +
                      <a class="nav-link dropdown-toggle d-flex align-items-center rounded" href="#" id="offcanvasDbDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
         | 
| 1479 | 
            +
                        <i class="bi bi-database me-2 text-primary"></i> <%= (current_conn = available_connections.find { |c| c[:current] }) ? current_conn[:name] : "Database" %>
         | 
| 1480 | 
            +
                      </a>
         | 
| 1481 | 
            +
                      <ul class="dropdown-menu shadow-sm mt-2" aria-labelledby="offcanvasDbDropdown">
         | 
| 1482 | 
            +
                        <% available_connections.each do |connection| %>
         | 
| 1483 | 
            +
                      <li>
         | 
| 1484 | 
            +
                        <%= button_to connection_path(connection[:key]), method: :post, class: "dropdown-item border-0 w-100 text-start #{'active' if connection[:current]}" do %>
         | 
| 1485 | 
            +
                          <% if connection[:current] %>
         | 
| 1486 | 
            +
                        <i class="bi bi-check2-circle me-2 text-primary"></i>
         | 
| 1487 | 
            +
                          <% else %>
         | 
| 1488 | 
            +
                        <i class="bi bi-circle me-2"></i>
         | 
| 1489 | 
            +
                          <% end %>
         | 
| 1490 | 
            +
                          <%= connection[:name] %>
         | 
| 1491 | 
            +
                        <% end %>
         | 
| 1492 | 
            +
                      </li>
         | 
| 1493 | 
            +
                        <% end %>
         | 
| 1494 | 
            +
                        <li><hr class="dropdown-divider"></li>
         | 
| 1495 | 
            +
                        <li><%= link_to "<i class='bi bi-gear me-2'></i> Manage Connections".html_safe, connections_path, class: "dropdown-item" %></li>
         | 
| 1496 | 
            +
                      </ul>
         | 
| 1497 | 
            +
                    </li>
         | 
| 1498 | 
            +
                    <li class="mt-4 pt-2 border-top">
         | 
| 1499 | 
            +
                      <div class="d-flex align-items-center py-2">
         | 
| 1500 | 
            +
                        <i class="bi bi-tools me-2 text-secondary"></i> 
         | 
| 1501 | 
            +
                        <span class="text-secondary"><%= Rails.env %> environment</span>
         | 
| 1502 | 
            +
                      </div>
         | 
| 1503 | 
            +
                    </li>
         | 
| 1504 | 
            +
                      </ul>
         | 
| 1505 | 
            +
                    </div>
         | 
| 1506 | 
            +
                  </div>
         | 
| 1449 1507 | 
             
                </nav>
         | 
| 1450 1508 | 
             
                <!-- Spacer to prevent content from hiding under fixed navbar -->
         | 
| 1451 1509 | 
             
                <div class="dbviewer-navbar-spacer"></div>
         | 
| @@ -1472,7 +1530,7 @@ | |
| 1472 1530 | 
             
                      <% end %>
         | 
| 1473 1531 |  | 
| 1474 1532 | 
             
                      <div class="d-flex d-lg-none align-items-center mb-3">
         | 
| 1475 | 
            -
                        <button class="btn btn-sm  | 
| 1533 | 
            +
                        <button class="btn btn-sm dbviewer-sidebar-toggle" type="button">
         | 
| 1476 1534 | 
             
                          <i class="bi bi-list me-1"></i> Tables
         | 
| 1477 1535 | 
             
                        </button>
         | 
| 1478 1536 | 
             
                      </div>
         | 
| @@ -1593,6 +1651,75 @@ | |
| 1593 1651 | 
             
                      }
         | 
| 1594 1652 | 
             
                    }, 250);
         | 
| 1595 1653 | 
             
                  });
         | 
| 1654 | 
            +
                  
         | 
| 1655 | 
            +
                  // Offcanvas enhancement for theme synchronization
         | 
| 1656 | 
            +
                  const offcanvasElement = document.getElementById('navbarOffcanvas');
         | 
| 1657 | 
            +
                  if (offcanvasElement) {
         | 
| 1658 | 
            +
                    // Get all theme toggles
         | 
| 1659 | 
            +
                    const allThemeToggles = document.querySelectorAll('.theme-toggle');
         | 
| 1660 | 
            +
                    
         | 
| 1661 | 
            +
                    // Handle theme change from any toggle button
         | 
| 1662 | 
            +
                    allThemeToggles.forEach(toggleBtn => {
         | 
| 1663 | 
            +
                      toggleBtn.addEventListener('click', function() {
         | 
| 1664 | 
            +
                        const currentTheme = document.documentElement.getAttribute('data-bs-theme') || 'light';
         | 
| 1665 | 
            +
                        
         | 
| 1666 | 
            +
                        // Update all theme toggle buttons to maintain consistency
         | 
| 1667 | 
            +
                        allThemeToggles.forEach(btn => {
         | 
| 1668 | 
            +
                          // Update icon in all theme toggle buttons
         | 
| 1669 | 
            +
                          if (currentTheme === 'dark') {
         | 
| 1670 | 
            +
                            btn.querySelector('span').innerHTML = '<i class="bi bi-sun-fill"></i>';
         | 
| 1671 | 
            +
                            btn.setAttribute('aria-label', 'Switch to light mode');
         | 
| 1672 | 
            +
                          } else {
         | 
| 1673 | 
            +
                            btn.querySelector('span').innerHTML = '<i class="bi bi-moon-fill"></i>';
         | 
| 1674 | 
            +
                            btn.setAttribute('aria-label', 'Switch to dark mode');
         | 
| 1675 | 
            +
                          }
         | 
| 1676 | 
            +
                        });
         | 
| 1677 | 
            +
                      });
         | 
| 1678 | 
            +
                    });
         | 
| 1679 | 
            +
                    
         | 
| 1680 | 
            +
                    // Function to sync offcanvas colors with current theme
         | 
| 1681 | 
            +
                    function syncOffcanvasWithTheme() {
         | 
| 1682 | 
            +
                      const currentTheme = document.documentElement.getAttribute('data-bs-theme') || 'light';
         | 
| 1683 | 
            +
                      if (currentTheme === 'dark') {
         | 
| 1684 | 
            +
                        offcanvasElement.querySelector('.offcanvas-header').classList.remove('bg-light-subtle');
         | 
| 1685 | 
            +
                        offcanvasElement.querySelector('.offcanvas-header').classList.add('bg-dark-subtle');
         | 
| 1686 | 
            +
                      } else {
         | 
| 1687 | 
            +
                        offcanvasElement.querySelector('.offcanvas-header').classList.remove('bg-dark-subtle');
         | 
| 1688 | 
            +
                        offcanvasElement.querySelector('.offcanvas-header').classList.add('bg-light-subtle');
         | 
| 1689 | 
            +
                      }
         | 
| 1690 | 
            +
                    }
         | 
| 1691 | 
            +
                    
         | 
| 1692 | 
            +
                    // Sync on page load
         | 
| 1693 | 
            +
                    document.addEventListener('DOMContentLoaded', syncOffcanvasWithTheme);
         | 
| 1694 | 
            +
                    
         | 
| 1695 | 
            +
                    // Listen for theme changes
         | 
| 1696 | 
            +
                    document.addEventListener('dbviewerThemeChanged', syncOffcanvasWithTheme);
         | 
| 1697 | 
            +
                    
         | 
| 1698 | 
            +
                    // Handle link click in offcanvas (auto-close on mobile)
         | 
| 1699 | 
            +
                    const offcanvasLinks = offcanvasElement.querySelectorAll('.nav-link:not(.dropdown-toggle)');
         | 
| 1700 | 
            +
                    offcanvasLinks.forEach(link => {
         | 
| 1701 | 
            +
                      link.addEventListener('click', function() {
         | 
| 1702 | 
            +
                        if (window.innerWidth < 992) {
         | 
| 1703 | 
            +
                          bootstrap.Offcanvas.getInstance(offcanvasElement).hide();
         | 
| 1704 | 
            +
                        }
         | 
| 1705 | 
            +
                      });
         | 
| 1706 | 
            +
                    });
         | 
| 1707 | 
            +
                    
         | 
| 1708 | 
            +
                    // Fix offcanvas backdrop on desktop
         | 
| 1709 | 
            +
                    window.addEventListener('resize', function() {
         | 
| 1710 | 
            +
                      if (window.innerWidth >= 992) {
         | 
| 1711 | 
            +
                        const offcanvasInstance = bootstrap.Offcanvas.getInstance(offcanvasElement);
         | 
| 1712 | 
            +
                        if (offcanvasInstance) {
         | 
| 1713 | 
            +
                          offcanvasInstance.hide();
         | 
| 1714 | 
            +
                        }
         | 
| 1715 | 
            +
                        // Also remove any backdrop
         | 
| 1716 | 
            +
                        const backdrop = document.querySelector('.offcanvas-backdrop');
         | 
| 1717 | 
            +
                        if (backdrop) {
         | 
| 1718 | 
            +
                          backdrop.remove();
         | 
| 1719 | 
            +
                        }
         | 
| 1720 | 
            +
                      }
         | 
| 1721 | 
            +
                    });
         | 
| 1722 | 
            +
                  }
         | 
| 1596 1723 | 
             
                });
         | 
| 1597 1724 | 
             
              </script>
         | 
| 1598 1725 | 
             
            </body>
         | 
    
        data/lib/dbviewer/engine.rb
    CHANGED
    
    | @@ -2,11 +2,6 @@ module Dbviewer | |
| 2 2 | 
             
              class Engine < ::Rails::Engine
         | 
| 3 3 | 
             
                isolate_namespace Dbviewer
         | 
| 4 4 |  | 
| 5 | 
            -
                # Autoload lib directory
         | 
| 6 | 
            -
                lib_path = File.expand_path("..", __dir__)
         | 
| 7 | 
            -
                config.autoload_paths << lib_path
         | 
| 8 | 
            -
                config.eager_load_paths << lib_path
         | 
| 9 | 
            -
             | 
| 10 5 | 
             
                # Register generators
         | 
| 11 6 | 
             
                config.app_generators do |g|
         | 
| 12 7 | 
             
                  g.templates.unshift File.expand_path("../../generators/dbviewer/templates", __dir__)
         | 
    
        data/lib/dbviewer/version.rb
    CHANGED
    
    
    
        data/lib/dbviewer.rb
    CHANGED
    
    | @@ -1,7 +1,6 @@ | |
| 1 1 | 
             
            require "dbviewer/version"
         | 
| 2 2 | 
             
            require "dbviewer/configuration"
         | 
| 3 3 | 
             
            require "dbviewer/engine"
         | 
| 4 | 
            -
            require "dbviewer/validator"
         | 
| 5 4 | 
             
            require "dbviewer/validator/sql"
         | 
| 6 5 |  | 
| 7 6 | 
             
            require "dbviewer/storage/base"
         | 
| @@ -10,7 +9,6 @@ require "dbviewer/storage/file_storage" | |
| 10 9 |  | 
| 11 10 | 
             
            require "dbviewer/query/executor"
         | 
| 12 11 | 
             
            require "dbviewer/query/analyzer"
         | 
| 13 | 
            -
            require "dbviewer/query/collection"
         | 
| 14 12 | 
             
            require "dbviewer/query/logger"
         | 
| 15 13 | 
             
            require "dbviewer/query/notification_subscriber"
         | 
| 16 14 | 
             
            require "dbviewer/query/parser"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: dbviewer
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.6. | 
| 4 | 
            +
              version: 0.6.6
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Wailan Tirajoh
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2025-06- | 
| 11 | 
            +
            date: 2025-06-07 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rails
         | 
| @@ -109,7 +109,6 @@ files: | |
| 109 109 | 
             
            - lib/dbviewer/datatable/query_params.rb
         | 
| 110 110 | 
             
            - lib/dbviewer/engine.rb
         | 
| 111 111 | 
             
            - lib/dbviewer/query/analyzer.rb
         | 
| 112 | 
            -
            - lib/dbviewer/query/collection.rb
         | 
| 113 112 | 
             
            - lib/dbviewer/query/executor.rb
         | 
| 114 113 | 
             
            - lib/dbviewer/query/logger.rb
         | 
| 115 114 | 
             
            - lib/dbviewer/query/notification_subscriber.rb
         | 
| @@ -117,7 +116,6 @@ files: | |
| 117 116 | 
             
            - lib/dbviewer/storage/base.rb
         | 
| 118 117 | 
             
            - lib/dbviewer/storage/file_storage.rb
         | 
| 119 118 | 
             
            - lib/dbviewer/storage/in_memory_storage.rb
         | 
| 120 | 
            -
            - lib/dbviewer/validator.rb
         | 
| 121 119 | 
             
            - lib/dbviewer/validator/sql.rb
         | 
| 122 120 | 
             
            - lib/dbviewer/version.rb
         | 
| 123 121 | 
             
            - lib/generators/dbviewer/install_generator.rb
         | 
| @@ -1,39 +0,0 @@ | |
| 1 | 
            -
            module Dbviewer
         | 
| 2 | 
            -
              module Query
         | 
| 3 | 
            -
                # Collection handles the storage and retrieval of SQL queries
         | 
| 4 | 
            -
                # This class is maintained for backward compatibility
         | 
| 5 | 
            -
                # New code should use InMemoryStorage or FileStorage directly
         | 
| 6 | 
            -
                class Collection < ::Dbviewer::Storage::InMemoryStorage
         | 
| 7 | 
            -
                  # Maximum number of queries to keep in memory
         | 
| 8 | 
            -
                  MAX_QUERIES = 1000
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                  # Get recent queries, optionally filtered
         | 
| 11 | 
            -
                  def filter(limit: 100, table_filter: nil, request_id: nil, min_duration: nil)
         | 
| 12 | 
            -
                    result = all
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                    # Apply filters if provided
         | 
| 15 | 
            -
                    result = filter_by_table(result, table_filter) if table_filter.present?
         | 
| 16 | 
            -
                    result = filter_by_request_id(result, request_id) if request_id.present?
         | 
| 17 | 
            -
                    result = filter_by_duration(result, min_duration) if min_duration.present?
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                    # Return most recent queries first, limited to requested amount
         | 
| 20 | 
            -
                    result.reverse.first(limit)
         | 
| 21 | 
            -
                  end
         | 
| 22 | 
            -
             | 
| 23 | 
            -
                  private
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                  def filter_by_table(queries, table_filter)
         | 
| 26 | 
            -
                    queries.select { |q| q[:sql].downcase.include?(table_filter.downcase) }
         | 
| 27 | 
            -
                  end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                  def filter_by_request_id(queries, request_id)
         | 
| 30 | 
            -
                    queries.select { |q| q[:request_id].to_s.include?(request_id) }
         | 
| 31 | 
            -
                  end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                  def filter_by_duration(queries, min_duration)
         | 
| 34 | 
            -
                    min_ms = min_duration.to_f
         | 
| 35 | 
            -
                    queries.select { |q| q[:duration_ms] >= min_ms }
         | 
| 36 | 
            -
                  end
         | 
| 37 | 
            -
                end
         | 
| 38 | 
            -
              end
         | 
| 39 | 
            -
            end
         | 
    
        data/lib/dbviewer/validator.rb
    DELETED
    
    | @@ -1,9 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Dbviewer
         | 
| 4 | 
            -
              # Validator namespace contains validation classes for different types of content
         | 
| 5 | 
            -
              # such as SQL queries, configuration parameters, etc.
         | 
| 6 | 
            -
              module Validator
         | 
| 7 | 
            -
                # This module serves as a namespace for various validator classes
         | 
| 8 | 
            -
              end
         | 
| 9 | 
            -
            end
         |