solid_litequeen 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/solid_litequeen/databases_controller.rb +17 -1
- data/app/javascript/controllers/table_controller.js +7 -0
- data/app/javascript/solid_litequeen/controllers/table_controller.js +161 -0
- data/app/views/layouts/solid_litequeen/application.html.erb +2 -0
- data/app/views/solid_litequeen/databases/table_rows.html.erb +14 -7
- data/config/routes.rb +1 -0
- data/lib/solid_litequeen/version.rb +1 -1
- metadata +3 -2
- data/app/javascript/solid_litequeen/controllers/tet_controller.js +0 -8
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 536ae72fe762f8bfcc75364afa5fa995199f02b6ca43d24f7b97bc118b344731
         | 
| 4 | 
            +
              data.tar.gz: 87bcc08b867f182fd988bac2fe9c2eb768b46f12869d6e7bfca2a12d4e68b091
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: b57761f2bce146ca25e0d99812cbc30ac182365141156796edfa2a72efab0f4d4644a0a6f37c328719fa4c1db9ce60c4310f74a6b6a9da8ae23b38d74e9f0e0f
         | 
| 7 | 
            +
              data.tar.gz: 71d073a29576d62ca4dab1ad01bcfdd9b4b8c0eabe0d3b2bf8b1e4efe1d46fe266eb1b2f0095c1fe887f96c0c320b8f68da4584e9df366fc0f4de03aa7f333fa
         | 
| @@ -61,16 +61,20 @@ module SolidLitequeen | |
| 61 61 | 
             
                  # Verify the sort column exists in the table to prevent SQL injection
         | 
| 62 62 | 
             
                  valid_columns = DynamicDatabase.connection.columns(@table_name).map(&:name)
         | 
| 63 63 |  | 
| 64 | 
            +
                  # Use the column order from session if it exists; otherwise, default to all columns
         | 
| 65 | 
            +
                  ordered_columns = session["#{@database_id}_#{@table_name}_column_order"] || valid_columns
         | 
| 66 | 
            +
             | 
| 64 67 | 
             
                  order_clause = if @sort_column.present? && valid_columns.include?(@sort_column)
         | 
| 65 68 | 
             
                    "#{DynamicDatabase.connection.quote_column_name(@sort_column)} #{@sort_direction}"
         | 
| 66 69 | 
             
                  end
         | 
| 67 70 |  | 
| 68 | 
            -
                  sql = [ "SELECT  | 
| 71 | 
            +
                  sql = [ "SELECT #{ordered_columns.join(', ')} FROM #{@table_name}" ]
         | 
| 69 72 | 
             
                  sql << "ORDER BY #{order_clause}" if order_clause
         | 
| 70 73 | 
             
                  sql << "LIMIT 50"
         | 
| 71 74 |  | 
| 72 75 |  | 
| 73 76 | 
             
                  @data = DynamicDatabase.connection.select_all(sql.join(" "))
         | 
| 77 | 
            +
             | 
| 74 78 | 
             
                  @row_count = row_count = DynamicDatabase.connection.select_value("SELECT COUNT(*) FROM #{@table_name}").to_i
         | 
| 75 79 | 
             
                end
         | 
| 76 80 |  | 
| @@ -93,5 +97,17 @@ module SolidLitequeen | |
| 93 97 | 
             
                            type: "application/x-sqlite3",
         | 
| 94 98 | 
             
                            disposition: "attachment"
         | 
| 95 99 | 
             
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def set_column_order
         | 
| 102 | 
            +
                  table_name = params[:table]
         | 
| 103 | 
            +
                  database_id = params[:database_id]
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  column_order = params[:columnOrder]
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  # Store column order in session using database and table as key
         | 
| 108 | 
            +
                  session["#{database_id}_#{table_name}_column_order"] = column_order
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  head :ok
         | 
| 111 | 
            +
                end
         | 
| 96 112 | 
             
              end
         | 
| 97 113 | 
             
            end
         | 
| @@ -0,0 +1,161 @@ | |
| 1 | 
            +
            import { Controller } from "@hotwired/stimulus";
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            // Connects to data-controller="table"
         | 
| 4 | 
            +
            export default class extends Controller {
         | 
| 5 | 
            +
            	static targets = [];
         | 
| 6 | 
            +
            	// We'll store the dragged element here
         | 
| 7 | 
            +
            	draggedTh = null;
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            	connect() {
         | 
| 10 | 
            +
            		this.element.addEventListener(
         | 
| 11 | 
            +
            			"dragstart",
         | 
| 12 | 
            +
            			this.handleThDragstart.bind(this),
         | 
| 13 | 
            +
            		);
         | 
| 14 | 
            +
            		this.element.addEventListener("dragover", this.handleThDragover.bind(this));
         | 
| 15 | 
            +
            		this.element.addEventListener("drop", this.handleThDrop.bind(this));
         | 
| 16 | 
            +
            		this.element.addEventListener("dragend", this.handleThDragend.bind(this));
         | 
| 17 | 
            +
            		this.element.addEventListener(
         | 
| 18 | 
            +
            			"dragenter",
         | 
| 19 | 
            +
            			this.handleThDragenter.bind(this),
         | 
| 20 | 
            +
            		);
         | 
| 21 | 
            +
            		this.element.addEventListener(
         | 
| 22 | 
            +
            			"dragleave",
         | 
| 23 | 
            +
            			this.handleThDragleave.bind(this),
         | 
| 24 | 
            +
            		);
         | 
| 25 | 
            +
            	}
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            	disconnect() {
         | 
| 28 | 
            +
            		this.element.removeEventListener("dragstart", this.handleThDragstart);
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            		this.element.removeEventListener("dragover", this.handleThDragover);
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            		this.element.removeEventListener("drop", this.handleThDrop);
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            		this.element.removeEventListener("dragend", this.handleThDragend);
         | 
| 35 | 
            +
            		this.element.removeEventListener("dragenter", this.handleThDragenter);
         | 
| 36 | 
            +
            		this.element.removeEventListener("dragleave", this.handleThDragleave);
         | 
| 37 | 
            +
            	}
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            	handleThDragstart(e) {
         | 
| 40 | 
            +
            		// Only allow dragging if the target is a table header
         | 
| 41 | 
            +
            		if (!e.target.matches("th")) {
         | 
| 42 | 
            +
            			e.preventDefault();
         | 
| 43 | 
            +
            			return;
         | 
| 44 | 
            +
            		}
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            		// Save the dragged header element
         | 
| 47 | 
            +
            		this.draggedTh = e.target;
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            		this.draggedTh.setAttribute("data-is-dragging", true);
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            		e.dataTransfer.effectAllowed = "move";
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            		// Some browsers require data to be set in order for dragging to work
         | 
| 54 | 
            +
            		e.dataTransfer.setData("text/plain", "");
         | 
| 55 | 
            +
            	}
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            	handleThDragover(e) {
         | 
| 58 | 
            +
            		const targetTh = e.target.closest("th");
         | 
| 59 | 
            +
            		// Only allow dragging if the target is a table header
         | 
| 60 | 
            +
            		if (!targetTh) {
         | 
| 61 | 
            +
            			e.preventDefault();
         | 
| 62 | 
            +
            			return;
         | 
| 63 | 
            +
            		}
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            		e.preventDefault();
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            		// Show a move indicator
         | 
| 68 | 
            +
            		e.dataTransfer.dropEffect = "move";
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            		this.draggedTh.removeAttribute("data-is-dragging");
         | 
| 71 | 
            +
            	}
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            	handleThDrop(e) {
         | 
| 74 | 
            +
            		const targetTh = e.target.closest("th");
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            		// Only handle drop if the target is a table header and we have a dragged header
         | 
| 77 | 
            +
            		if (!targetTh || !this.draggedTh) {
         | 
| 78 | 
            +
            			e.preventDefault();
         | 
| 79 | 
            +
            			return;
         | 
| 80 | 
            +
            		}
         | 
| 81 | 
            +
            		e.preventDefault();
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            		// Swap the positions of the dragged header and the target header
         | 
| 84 | 
            +
            		const parent = targetTh.parentElement;
         | 
| 85 | 
            +
            		// Insert the dragged header before the target header
         | 
| 86 | 
            +
            		parent.insertBefore(this.draggedTh, targetTh);
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            		const headers = Array.from(parent.querySelectorAll("th"));
         | 
| 89 | 
            +
            		const columnOrder = headers.map((th) => {
         | 
| 90 | 
            +
            			// You might want to use data attributes to store column identifiers
         | 
| 91 | 
            +
            			return th.dataset.columnName;
         | 
| 92 | 
            +
            		});
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            		const token = document.querySelector('meta[name="csrf-token"]').content;
         | 
| 95 | 
            +
             | 
| 96 | 
            +
            		fetch(this.element.dataset.setTableOrderPath, {
         | 
| 97 | 
            +
            			method: "POST",
         | 
| 98 | 
            +
            			headers: {
         | 
| 99 | 
            +
            				"Content-Type": "application/json",
         | 
| 100 | 
            +
            				"X-CSRF-Token": token,
         | 
| 101 | 
            +
            			},
         | 
| 102 | 
            +
            			body: JSON.stringify({ columnOrder }),
         | 
| 103 | 
            +
            		}).then((result) => {
         | 
| 104 | 
            +
            			if (result.ok) {
         | 
| 105 | 
            +
            				// Get all rows in the table body
         | 
| 106 | 
            +
            				const tbody = this.element.querySelector("tbody");
         | 
| 107 | 
            +
            				const rows = Array.from(tbody.querySelectorAll("tr"));
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            				// Reorder cells in each row to match new column order
         | 
| 110 | 
            +
            				for (const row of rows) {
         | 
| 111 | 
            +
            					const cells = Array.from(row.querySelectorAll("td"));
         | 
| 112 | 
            +
            					const reorderedCells = columnOrder.map((colName) => {
         | 
| 113 | 
            +
            						// Find cell with matching data-column attribute
         | 
| 114 | 
            +
            						return cells.find((cell) => cell.dataset.column === colName);
         | 
| 115 | 
            +
            					});
         | 
| 116 | 
            +
             | 
| 117 | 
            +
            					// Clear row and append cells in new order
         | 
| 118 | 
            +
            					row.innerHTML = "";
         | 
| 119 | 
            +
            					for (const cell of reorderedCells) {
         | 
| 120 | 
            +
            						row.appendChild(cell);
         | 
| 121 | 
            +
            					}
         | 
| 122 | 
            +
            				}
         | 
| 123 | 
            +
            			}
         | 
| 124 | 
            +
            		});
         | 
| 125 | 
            +
             | 
| 126 | 
            +
            		targetTh.removeAttribute("data-column-order-about-to-be-swapped");
         | 
| 127 | 
            +
            	}
         | 
| 128 | 
            +
             | 
| 129 | 
            +
            	handleThDragend(e) {
         | 
| 130 | 
            +
            		// Only allow dragging if the target is a table header
         | 
| 131 | 
            +
            		if (!e.target.matches("th")) {
         | 
| 132 | 
            +
            			e.preventDefault();
         | 
| 133 | 
            +
            			return;
         | 
| 134 | 
            +
            		}
         | 
| 135 | 
            +
             | 
| 136 | 
            +
            		// Reset the dragged header
         | 
| 137 | 
            +
            		this.draggedTh = null;
         | 
| 138 | 
            +
            	}
         | 
| 139 | 
            +
             | 
| 140 | 
            +
            	handleThDragenter(e) {
         | 
| 141 | 
            +
            		const targetTh = e.target.closest("th");
         | 
| 142 | 
            +
            		// If there's no valid target or the target is the dragged header itself, do nothing
         | 
| 143 | 
            +
            		if (!targetTh || targetTh === this.draggedTh) {
         | 
| 144 | 
            +
            			return;
         | 
| 145 | 
            +
            		}
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            		e.preventDefault();
         | 
| 148 | 
            +
             | 
| 149 | 
            +
            		targetTh.setAttribute("data-column-order-about-to-be-swapped", true);
         | 
| 150 | 
            +
            	}
         | 
| 151 | 
            +
             | 
| 152 | 
            +
            	handleThDragleave(e) {
         | 
| 153 | 
            +
            		const targetTh = e.target.closest("th");
         | 
| 154 | 
            +
            		if (!targetTh) {
         | 
| 155 | 
            +
            			return;
         | 
| 156 | 
            +
            		}
         | 
| 157 | 
            +
            		e.preventDefault();
         | 
| 158 | 
            +
             | 
| 159 | 
            +
            		targetTh.removeAttribute("data-column-order-about-to-be-swapped");
         | 
| 160 | 
            +
            	}
         | 
| 161 | 
            +
            }
         | 
| @@ -5,6 +5,8 @@ | |
| 5 5 | 
             
              <%= csrf_meta_tags %>
         | 
| 6 6 | 
             
              <%= csp_meta_tag %>
         | 
| 7 7 |  | 
| 8 | 
            +
              <meta name="turbo-refresh-method" content="morph">
         | 
| 9 | 
            +
              <meta name="turbo-refresh-scroll" content="preserve">
         | 
| 8 10 |  | 
| 9 11 | 
             
              <%= stylesheet_link_tag "solid_litequeen/application", media: "all", "data-turbo-track": "reload" %>
         | 
| 10 12 | 
             
              <script src="https://unpkg.com/@tailwindcss/browser@4"></script>
         | 
| @@ -15,13 +15,19 @@ | |
| 15 15 | 
             
                    <p class="text-gray-600"><%= pluralize(@row_count, "row") %> found</p>
         | 
| 16 16 | 
             
                </div>
         | 
| 17 17 |  | 
| 18 | 
            +
             | 
| 18 19 | 
             
                <div class="bg-white rounded-lg shadow overflow-x-auto">
         | 
| 19 20 | 
             
                    <div class="min-w-full inline-block align-middle">
         | 
| 20 | 
            -
                        <table class="min-w-full relative">
         | 
| 21 | 
            +
                        <table data-controller="table" data-set-table-order-path="<%= database_set_table_column_order_path(params[:database_id], @table_name) %>" class="min-w-full relative">
         | 
| 21 22 | 
             
                            <thead class="">
         | 
| 22 23 | 
             
                                <tr class="bg-gray-100 border-b border-gray-200">
         | 
| 23 | 
            -
                                    <% @data.columns. | 
| 24 | 
            -
                                        <th  | 
| 24 | 
            +
                                    <% @data.columns.each_with_index do |column, index| %>
         | 
| 25 | 
            +
                                        <th 
         | 
| 26 | 
            +
                                            draggable="true" 
         | 
| 27 | 
            +
                                            data-column-index="<%= index %>"
         | 
| 28 | 
            +
                                            data-column-name="<%= column %>"
         | 
| 29 | 
            +
                                            class="hover:cursor-move px-6 py-3 text-left text-sm font-medium text-gray-700 whitespace-nowrap data-[is-dragging]:bg-orange-300/30 data-[column-order-about-to-be-swapped]:bg-green-300/30"
         | 
| 30 | 
            +
                                        >
         | 
| 25 31 | 
             
                                            <%#= column %>
         | 
| 26 32 |  | 
| 27 33 | 
             
                                              <%= link_to column, 
         | 
| @@ -30,19 +36,20 @@ | |
| 30 36 | 
             
                                                    table: @table_name,
         | 
| 31 37 | 
             
                                                    sort_column: column,
         | 
| 32 38 | 
             
                                                    sort_direction: (@sort_column == column && @sort_direction == 'DESC') ? 'ASC' : 'DESC'
         | 
| 33 | 
            -
                                                ) %>
         | 
| 39 | 
            +
                                                ), class: "" %>
         | 
| 34 40 | 
             
                                            <%= '▼' if @sort_column == column && @sort_direction == 'DESC' %>
         | 
| 35 41 | 
             
                                            <%= '▲' if @sort_column == column && @sort_direction == 'ASC' %>
         | 
| 36 42 | 
             
                                        </th>
         | 
| 37 43 | 
             
                                    <% end %>
         | 
| 38 44 | 
             
                                </tr>
         | 
| 39 45 | 
             
                            </thead>
         | 
| 46 | 
            +
             | 
| 40 47 | 
             
                            <tbody class="divide-y divide-gray-200">
         | 
| 41 48 | 
             
                                <% @data.rows.each do |row| %>
         | 
| 42 | 
            -
                                    <tr class="hover:bg-gray-50">
         | 
| 43 | 
            -
                                        <% row. | 
| 49 | 
            +
                                    <tr class="hover:bg-gray-50" >
         | 
| 50 | 
            +
                                        <% row.each_with_index do |item, index| %>
         | 
| 44 51 | 
             
                                            <% truncated_item =  item&.truncate(80) %>
         | 
| 45 | 
            -
                                            <td class="px-6 py-4 text-sm text-gray-800 whitespace-nowrap" >
         | 
| 52 | 
            +
                                            <td data-column="<%= @data.columns[index] %>" class="px-6 py-4 text-sm text-gray-800 whitespace-nowrap" >
         | 
| 46 53 |  | 
| 47 54 | 
             
                                                <div class="flex justify-between">
         | 
| 48 55 |  | 
    
        data/config/routes.rb
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            SolidLitequeen::Engine.routes.draw do
         | 
| 2 2 | 
             
              resources :databases, only: [ :index, :show ] do
         | 
| 3 3 | 
             
                get "/tables/:table", to: "databases#table_rows", as: :table_rows
         | 
| 4 | 
            +
                post "/tables/:table/set-column-order", to: "databases#set_column_order", as: :set_table_column_order
         | 
| 4 5 | 
             
                get "download", to: "databases#download", as: "download"
         | 
| 5 6 | 
             
              end
         | 
| 6 7 | 
             
              root to: "databases#index"
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: solid_litequeen
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.9.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Vik Borges
         | 
| @@ -157,10 +157,11 @@ files: | |
| 157 157 | 
             
            - app/controllers/solid_litequeen/databases_controller.rb
         | 
| 158 158 | 
             
            - app/helpers/solid_litequeen/application_helper.rb
         | 
| 159 159 | 
             
            - app/helpers/solid_litequeen/databases_helper.rb
         | 
| 160 | 
            +
            - app/javascript/controllers/table_controller.js
         | 
| 160 161 | 
             
            - app/javascript/solid_litequeen/application.js
         | 
| 161 162 | 
             
            - app/javascript/solid_litequeen/controllers/application.js
         | 
| 162 163 | 
             
            - app/javascript/solid_litequeen/controllers/index.js
         | 
| 163 | 
            -
            - app/javascript/solid_litequeen/controllers/ | 
| 164 | 
            +
            - app/javascript/solid_litequeen/controllers/table_controller.js
         | 
| 164 165 | 
             
            - app/jobs/solid_litequeen/application_job.rb
         | 
| 165 166 | 
             
            - app/mailers/solid_litequeen/application_mailer.rb
         | 
| 166 167 | 
             
            - app/models/solid_litequeen/application_record.rb
         |