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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f307bf9d9eb772756d40395b38cef8f0f0a9cd905e1e0caff872855d9bedf3a6
4
- data.tar.gz: 0a0a6528451d67eede41bf3a967a87aebf78da47e07adb4ebc8fab6e20b36d12
3
+ metadata.gz: 536ae72fe762f8bfcc75364afa5fa995199f02b6ca43d24f7b97bc118b344731
4
+ data.tar.gz: 87bcc08b867f182fd988bac2fe9c2eb768b46f12869d6e7bfca2a12d4e68b091
5
5
  SHA512:
6
- metadata.gz: ed7ae24cd435790f31d182634a344202d6f5c839cfedae4d41660113d13b394168fb4426fc6916aeb02d2ba81d2e06f9c208f42608cea508d5b650dc39d67ee4
7
- data.tar.gz: 926ec78a8f509413478913ae70fde680992dc353a362250808125c5cac7b8e3c1d69c482dc987fc9f260763a9a67f8427ed1b7645e4a70a99ba34f51d13a2ac5
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 * FROM #{@table_name}" ]
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,7 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ // Connects to data-controller="table"
4
+ export default class extends Controller {
5
+ connect() {
6
+ }
7
+ }
@@ -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.each do |column| %>
24
- <th class="px-6 py-3 text-left text-sm font-medium text-gray-700 whitespace-nowrap">
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.each do |item| %>
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"
@@ -1,3 +1,3 @@
1
1
  module SolidLitequeen
2
- VERSION = "0.8.1"
2
+ VERSION = "0.9.0"
3
3
  end
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.8.1
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/tet_controller.js
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
@@ -1,8 +0,0 @@
1
- import { Controller } from "@hotwired/stimulus";
2
-
3
- // Connects to data-controller="tet"
4
- export default class extends Controller {
5
- connect() {
6
- console.log("hello potato");
7
- }
8
- }